Python如何将多个变量初始化为相同的初始值?

48

我已经阅读了以下问题:

  1. Python如何将多个变量赋给同一个值?列表行为
    关于元组的问题,我想让变量可以是字符串、整数或字典
  2. 声明多个变量的更优雅方式
    这个问题中有我想问的内容,但被接受的回答太复杂了

所以我想要实现的目标是:

我按照以下方式声明变量,我希望能够尽可能地减少这些声明所需的代码行数。

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 = {}

最简单、易于维护的是什么?


1
你可以使用字典。 - vaultah
这很复杂,我必须调用 dicitonary['details'],而且 KeyErrors 很糟糕。此外,IDE 不会突出显示无效的键,只会突出显示变量。如果我必须使用 details = dicitonary['details'],那么我最好使用 details = None 而不是这种循环字典创建、查找和 KeyErrors。 - user5170375
你如何定义复杂? - Swastik Padhi
1
@CrakC 请查看上面的评论。 - user5170375
2
a,b=(True,)*2 Or a=b=True - hassanzadeh.sd
7个回答

76

我同意其他答案,但我想在这里解释重要的一点。

None对象是单例对象。无论你将None对象分配给变量多少次,都会使用相同的对象。

x = None
y = None

等于

x = y = None

但是您不应该在Python中对任何其他对象执行相同的操作。例如,

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

不等于

x = y = {}  # same dict object assigned to x ,y. We should not do this.

2
不回答问题 - Efren

35

首先我建议你不要这样做。这种写法难以阅读且不符合Python语言的惯例。但是,你可以使用以下类似的代码来减少行数:

details, product_base, product_identity, category_string, store_id, image_hash, image_link_mask, results = [None] * 8
abort = False
data = {}

哇,我不懂这个[None]*8,但它很容易阅读。 - greendino

15
(
    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 = {}

这就是我的做法。


7

我有一个一行的lambda函数,可以帮助我处理这个问题。

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

Lambda表达式与这个是同一件事情。
def nones(n):
    return [None for _ in range(n)]

2
抱歉,我是个新手,使用lambda有什么好处,而不是直接操作?a、b、c = (None for _ in range(3))。 - Armored Wolf
3
这也可以写成这样:v,w,x,y,z = [None]*5 - Yaxhpal

2

以下是之前回答的综合:

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)

编辑:

尝试通过更好地处理带有 pandas/numpy 类型的可变变量来优化 deepcopy 调用。可能会错过其他一些情况。如果有人找到了更好的检查可变性的方法,请随时分享。虽然,这可能是过度工程化,无论您的用例是什么...

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)

1
v, w, x = default(3, {}) 不会按预期工作:因为在调用 default() 时你实例化了 {},所以你实际上将同一个字典分配给每个变量:运行 v["a"] = "b" 然后打印 w,你会发现 w=={'a': 'b'} - joanis
1
如果你真的想保留这种方法,你可以通过返回一个值的深拷贝数组来修补这段代码。这样做的话,代码如下:return [copy.deepcopy(value) for _ in range(number)] - joanis
@joanis 谢谢您的注意。我已经编辑了我的答案。 - belgacea
1
更好了,而且它通常可以工作,尽管我怀疑深拷贝行被系统地调用,因为 type(value) is dict or object(type(value) is dict) or object 是相同的,而在布尔上下文中评估 object 总是为真。虽然我认为优化无关紧要,但你真正想测试的是 value 是否可变。在Python中,一切都是对象,所以像 if isinstance(value, dict) or isinstance(value, object): 这样的东西,我认为更接近你想做的事情,也将被系统地证明是真的。 - joanis
@joanis deepcopy确实一直被调用。if isinstance(value, (dict, object))给出了相同的行为。由于我不知道Python中任何关于可变性的类型/类描述符,因此我添加了一些类型检查以满足最常见的用例,但这开始感觉有点过多,超出了我最初的目标。 - belgacea
1
它使代码变得更加复杂。实际上,我会坚持使用一个单一的无条件深拷贝行,基于NoneFalseTrue()``的深拷贝都非常便宜,所以不值得尝试优化该调用。此外,我刚刚测试了一下,字符串或仅包含不可变类型元素的元组的深拷贝实际上已经被copy.deepcopy自身优化为引用拷贝,因此看起来我们正在尝试添加的所有魔法已经存在了。因此,为了简化代码,并且没有性能损失,我将default()`变成一个单行函数。 - joanis

1

在参与了@belgacea回答下的讨论后,我想发布我认为最好的答案变体:

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)

在所有情况下,我们通过deepcopy()得到保证,可变对象不会在初始化的不同变量之间共享,这是需要避免的重要陷阱。
但我已将代码简化为其最简表达式,利用了deepcopy已经被优化为在其输入是不可变时执行浅拷贝的事实。我已经测试过字符串和元组,如果x是递归不可变的,例如x = ((1,2),"foo"),那么x is copy.deepcopy(x)返回True,因此没有必要尝试避免对不需要深度复制的值调用deepcopy
我还更改了函数的签名和名称,以便在使用它的地方可以更好地自我记录代码。

-2

这并不是直接回答问题,但它是相关的——我使用一个空类的实例来分组相似的属性,这样我就不必在我的init方法中列出它们,以免弄乱。

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)

SimpleNamespace和空类定义之间有什么区别?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接