How should I declare default values for instance variables in Python?
我应该给我的类成员这样的默认值吗:
1 2 | class Foo: num = 1 |
或者像这样?
1 2 3 | class Foo: def __init__(self): self.num = 1 |
在这个问题上,我发现在这两种情况下,
1 2 | bar = Foo() bar.num += 1 |
是一个定义明确的操作。
我知道第一个方法会给我一个类变量,而第二个方法不会。但是,如果我不需要类变量,但只需要为实例变量设置一个默认值,那么这两种方法是否都一样好?或者其中一个比另一个更"Python"?
我注意到的一件事是,在Django教程中,他们使用第二种方法来声明模型。我个人认为第二种方法更优雅,但我想知道"标准"方法是什么。
扩展bp的答案,我想向您展示他所说的不变类型的含义。
首先,这没关系:
1 2 3 4 5 6 7 8 9 10 11 | >>> class TestB(): ... def __init__(self, attr=1): ... self.attr = attr ... >>> a = TestB() >>> b = TestB() >>> a.attr = 2 >>> a.attr 2 >>> b.attr 1 |
但是,这只适用于不可变(不可变)类型。如果默认值是可变的(意味着它可以被替换),则会发生以下情况:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> class Test(): ... def __init__(self, attr=[]): ... self.attr = attr ... >>> a = Test() >>> b = Test() >>> a.attr.append(1) >>> a.attr [1] >>> b.attr [1] >>> |
注意,
当类型可变时,这是定义实例变量默认值的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>> class TestC(): ... def __init__(self, attr=None): ... if attr is None: ... attr = [] ... self.attr = attr ... >>> a = TestC() >>> b = TestC() >>> a.attr.append(1) >>> a.attr [1] >>> b.attr [] |
我的第一个代码片段工作的原因是,对于不可变的类型,只要您需要,Python就会创建一个新的实例。如果您需要添加1到1,那么Python会为您生成一个新的2,因为旧的1不能更改。我相信原因主要是散列。
这两个片段做了不同的事情,所以这不是一个品味的问题,而是一个在你的上下文中什么是正确的行为的问题。python文档解释了不同之处,但以下是一些示例:
展示A1 2 3 | class Foo: def __init__(self): self.num = 1 |
这会将
因此:
1 2 3 4 5 | >>> foo1 = Foo() >>> foo2 = Foo() >>> foo1.num = 2 >>> foo2.num 1 |
展品B
1 2 | class Bar: num = 1 |
这会将
1 2 3 4 5 6 7 8 9 10 11 12 | >>> bar1 = Bar() >>> bar2 = Bar() >>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation >>> bar2.num 1 >>> Bar.num = 3 >>> bar2.num 3 >>> bar1.num 2 >>> bar1.__class__.num 3 |
实际答案
If I do not require a class variable, but only need to set a default value for my instance variables, are both methods equally good? Or one of them more 'pythonic' than the other?
图表B中的代码显然是错误的:为什么要将类属性(实例创建时的默认值)绑定到单个实例?
附件A中的代码是可以的。
但是,如果要为构造函数中的实例变量提供默认值,我会这样做:
1 2 3 | class Foo: def __init__(num = None): self.num = num if num is not None else 1 |
……甚至:
1 2 3 4 | class Foo: DEFAULT_NUM = 1 def __init__(num = None): self.num = num if num is not None else DEFAULT_NUM |
…甚至:(首选,但如果且仅当您处理不可变类型时!)
1 2 3 | class Foo: def __init__(num = 1): self.num = num |
您可以这样做:
1 2 | foo1 = Foo(4) foo2 = Foo() #use default |
使用类成员提供默认值非常有效,只要您只小心使用不可变的值。如果你试着用一张单子或口述来做,那将是非常致命的。它也适用于实例属性是对类的引用的情况,只要默认值为"无"。
我已经看到这个技术在repoze中非常成功地使用,repoze是一个运行在zope之上的框架。这里的优点不仅是当类被持久化到数据库时,只需要保存非默认属性,而且当您需要向架构中添加一个新字段时,所有现有对象都会看到新字段及其默认值,而不需要实际更改存储的数据。
我发现它在更一般的编码中也能很好地工作,但它是一种风格。用你最喜欢的东西。
使用类成员作为实例变量的默认值不是一个好主意,而且这是我第一次看到提到这个主意。它在您的示例中有效,但在许多情况下可能会失败。例如,如果值是可变的,在未修改的实例上对其进行修改将更改默认值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> class c: ... l = [] ... >>> x = c() >>> y = c() >>> x.l [] >>> y.l [] >>> x.l.append(10) >>> y.l [10] >>> c.l [10] |
还可以将类变量声明为"无",这将阻止传播。当您需要一个定义良好的类并希望防止attributeErrors时,这非常有用。例如:
1 2 3 4 5 6 7 8 9 10 11 | >>> class TestClass(object): ... t = None ... >>> test = TestClass() >>> test.t >>> test2 = TestClass() >>> test.t = 'test' >>> test.t 'test' >>> test2.t >>> |
如果需要默认值:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> class TestClassDefaults(object): ... t = None ... def __init__(self, t=None): ... self.t = t ... >>> test = TestClassDefaults() >>> test.t >>> test2 = TestClassDefaults([]) >>> test2.t [] >>> test.t >>> |
当然,仍然遵循其他答案中关于使用可变类型和不可变类型作为