Python initialize multiple variables to the same initial value

Question:

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 ?

Asked By: user5170375

||

Answers:

(
    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.

Answered By: DasFranck

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 = {}
Answered By: Aske Doerge

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.
Answered By: Shan

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)]
Answered By: jerblack

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?

Answered By: Stan

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)
Answered By: belgacea

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.

Answered By: joanis