Python initialize multiple variables to the same initial value
Asked Answered
C

8

53

I have gone through these questions,

  1. Python assigning multiple variables to same value? list behavior
    concerned with tuples, I want just variables may be a string, integer or dictionary
  2. More elegant way of declaring multiple variables at the same time
    The question has something I want to ask, but the accepted answer is much complex

so what I'm trying to achieve,

I declare variables as follows, and I want to reduce these declarations to as less line of code as possible.

details = None
product_base = None
product_identity = None
category_string = None
store_id = None
image_hash = None
image_link_mask = None
results = None
abort = False
data = {}

What is the simplest, easy to maintain ?

Capitoline answered 25/10, 2015 at 15:58 Comment(5)
You'd use a dictionary.Nellnella
thats complex, I'd have to call dicitonary['details'] and KeyErrors suck., plus ides won't highlight invalid keys but variables. If I have to use details = dicitonary['details'], I'd better use details = None than this round dict creations, lookups and KeyErrors.Capitoline
How do you define complex?Calico
@CrakC see comment above.Capitoline
a,b=(True,)*2 Or a=b=TruePenninite
M
81

I agree with the other answers but would like to explain the important point here.

None object is singleton object. How many times you assign None object to a variable, same object is used. So

x = None
y = None

is equal to

x = y = None

but you should not do the same thing with any other object in python. For example,

x = {}  # each time a dict object is created
y = {}

is not equal to

x = y = {}  # same dict object assigned to x ,y. We should not do this.
Majorette answered 25/10, 2015 at 16:15 Comment(2)
Does not answer the questionUnbound
Doing assignment chaining should be fine for other non-mutable objects too, such as strings.Gel
B
37

First of all I would advice you not to do this. It's unreadable and un-Pythonic. However you can reduce the number of lines with something like:

details, product_base, product_identity, category_string, store_id, image_hash, image_link_mask, results = [None] * 8
abort = False
data = {}
Bangalore answered 25/10, 2015 at 16:5 Comment(1)
wow I don't understand this [None]*8 , but it's so easy to readDyeing
T
18
(
    details,
    producy_base,
    product_identity,
    category_string,
    store_id,
    image_hash,
    image_link_mask,
    results,
) = (None, None, None, None, None, None, None, None)

abort = False
data = {}

That's how I do.

Tesstessa answered 25/10, 2015 at 16:2 Comment(0)
C
7

I have a one-line lambda function I use that helps me out with this.

nones = lambda n: [None for _ in range(n)]
v, w, x, y, z = nones(5)

The lambda is the same thing as this.

def nones(n):
    return [None for _ in range(n)]
Champlin answered 29/6, 2018 at 19:38 Comment(2)
Sorry I'm a noob, is there some benefit to using lambda instead of doing it directly? a, b, c = (None for _ in range(3))Bickart
This can also be written like this: v, w, x, y, z = [None]*5Baras
G
2

A mix of previous answers :

from copy import deepcopy

def default(number, value = None):
    if type(value) is dict or object:
        return [deepcopy(value) for _ in range(number)]
    else:
        return [value] * number
    
o, p, q, r, s = default(5)
t, u = default(2, false)
v, w, x = default(3, {})

class GPU:
  def __init__(self, m, p):
    self.model = m
    self.price = p
 
rtx_3080 = GPU("RTX 3080", 99999)

y, z = default(2, rtx_3080)

Edit:

Tried to optimize deepcopy call by better handling mutable variables with pandas/numpy types as well. Might have missed some other ones. If someone find a better way to check for mutability, feel free to share. Althought, this maybe over-engineering whatever your use case is...

import builtins
from copy import deepcopy
from numbers import Number
from pandas import api, DataFrame

mutables = (dict, list, set, DataFrame)
immutables = (str, tuple, frozenset, Number)

def is_built_in_type(var):
    return type(var).__name__ in dir(builtins)

def is_mutable(var):
    return var is not None and (api.types.is_list_like(var) or isinstance(var, mutables) or not is_built_in_type(var))

def is_immutable(var):
    return var is None or isinstance(var, immutables)

def default(number, value=None):
    if is_mutable(value):
        return [deepcopy(value) for _ in range(number)]
    elif is_immutable(value):
        return [value] * number
    else:
        raise ValueError("Unexpected value type")

a, b, c, d, e = default(5)
f, g, h = default(3, False)
i, j = default(2, "False")
k, l, m, n = default(4, (3, 2, 1))
o, p = default(2, 3.14159265358979)
q, r, s = default(3, [1, 2, 3])
t, u = default(2, {})
v, w, x = default(3, DataFrame({'col1': [7, 13, 42, 73, 666], 'col2': [1, 0.6, 2, 1.4, 0.3]}))

class GPU:
    def __init__(self, m, p):
        self.model = m
        self.price = p

rtx_3080 = GPU("RTX 3080", 99999)

y, z = default(2, rtx_3080)
Glassful answered 11/10, 2019 at 15:55 Comment(6)
v, w, x = default(3, {}) will not work as expected: because you instantiated {} in the call to default(), you actually assign the same dictionary to each variable: run v["a"] = "b" and then print w, and you'll see that w=={'a': 'b'}.Graybeard
You could patch this code by returning an array of deepcopies of value if you really wanted to keep this approach. This would work return [copy.deepcopy(value) for _ in range(number)].Graybeard
@Graybeard Thanks for noticing. I edited my answer.Glassful
Much better, and it will work in general, although I suspect the deepcopy line is called systematically, because type(value) is dict or object is the same as (type(value) is dict) or object , and object evaluated in a Boolean context is always true. While I think that optimization is superfluous anyway, what you're really trying to test is whether value is mutable or not. In Python, everything is an object, so something like if isinstance(value, dict) or isinstance(value, object):, which I think is closer to what you were trying to do, would also be systematically true.Graybeard
@Graybeard deepcopy was indeed called all the time. if isinstance(value, (dict, object)) gives the same behavior. As I am not aware of any type/class descriptor for mutability in Python, I added some type checking for the most common use cases, but it starts to feel a bit too much for what I was aiming to achieve in a first place.Glassful
It makes the code a lot more complicated. I would actually stick with a single unconditional deepcopy line, on the premise that the deepcopy of None, False, True, (), are all super cheap, so it's not worth trying to optimize that call out. Furthermore, I just tested, and the deepcopy of a string or a tuple containing only immutables turns out to be a optimized back to a ref copy by copy.deepcopy itself, so it looks like all the magic we're trying to add already exists. So, for code simplicity, and at no performance cost, I would make default() a one-line function.Graybeard
G
1

After having participated in the discussion under @belgacea's answer, I'd like to post the variant of that answer that I think is the best:

import copy

def duplicate(value, n):
    return [copy.deepcopy(value) for _ in range(n)]

a, b, c = duplicate([0], 3)
o, p, q, r, s = duplicate(None, 5)
t, u = duplicate(False, 2)
v, w, x = duplicate({}, 3)
y, z = duplicate(MyClass(), 2)

In all cases, we're guaranteed by deepcopy() that mutable objects are not shared among the different variables initialized together, which is the important gotcha to avoid.

But I've simplified the code to its simplest expression, taking advantage of the fact that deepcopy is already well optimized to do a shallow copy when its input is immutable. I've tested this with strings and tuples, and if x is recursively immutable, e.g., x = ((1,2),"foo"), then x is copy.deepcopy(x) returns True, so there's no benefit in trying to avoid calling deepcopy on values that don't need a deep copy.

I've also changed the signature and name of the function to something that I think will make better self-documenting code where it is used.

Graybeard answered 20/10, 2021 at 12:48 Comment(0)
M
0

This way:

( var1, var2, var3, var3, var4,
var5, var6 ) = ( [None] * 6 )

The parenthesis are important if you want it to be multiline due to several variables and/or large names. Also do not forget to put the "None" between square brackets.

Modestia answered 8/5, 2024 at 19:8 Comment(0)
B
-2

This does not directly answer the question, but it is related -- I use an instance of an empty class to group similar attributes, so I do not have to clutter up my init method by listing them all.

class Empty:
    pass

class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.w = Empty()          # widgets
        self.master = master
        self.pack()
        self.create_widgets()

    def create_widgets(self):
        self.w.entry = tk.Entry(self, bg="orange", fg="black", font=FONT)

What is the difference between SimpleNamespace and empty class definition?

Belonging answered 25/7, 2019 at 13:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.