我应该如何在Python中声明实例变量的默认值?

int*_*nt3 80 python oop class

我应该给我的班级成员默认值如下:

class Foo:
    num = 1
Run Code Online (Sandbox Code Playgroud)

或者像这样?

class Foo:
    def __init__(self):
        self.num = 1
Run Code Online (Sandbox Code Playgroud)

这个问题中,我发现在这两种情况下,

bar = Foo()
bar.num += 1
Run Code Online (Sandbox Code Playgroud)

是一个定义明确的操作.

我知道第一种方法会给我一个类变量,而第二种方法则不会.但是,如果我不需要类变量,但只需要为我的实例变量设置默认值,这两种方法同样好吗?或者其中一个比另一个更'pythonic'?

我注意到的一件事是,在Django教程中,他们使用第二种方法来声明模型.我个人认为第二种方法更优雅,但我想知道"标准"方式是什么.

Xav*_* Ho 115

扩展bp的答案,我想向你展示他对不可变类型的意义.

首先,这没关系:

>>> class TestB():
...     def __init__(self, attr=1):
...         self.attr = attr
...     
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1
Run Code Online (Sandbox Code Playgroud)

但是,这仅适用于不可变(不可变)的类型.如果默认值是可变的(意味着它可以被替换),则会发生以下情况:

>>> class Test():
...     def __init__(self, attr=[]):
...         self.attr = attr
...     
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>> 
Run Code Online (Sandbox Code Playgroud)

请注意a和b都有共享属性.这通常是不需要的.

当类型是可变的时,这是为实例变量定义默认值的Pythonic方法:

>>> 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
[]
Run Code Online (Sandbox Code Playgroud)

我的第一段代码工作原因是因为,对于不可变类型,Python会在您需要时创建它的新实例.如果您需要添加1比1,Python会为您创建一个新的2,因为旧的1无法更改.我相信原因主要是哈希.

  • 抱歉 我在上面的评论中对该建议进行了更多研究,但显然并不完全相同,因此[不推荐](https://www.safaribooksonline.com/library/view/learning-python/1565924649/ch04s06.html) 。 (3认同)
  • 我最近遇到了这种为可变属性实现默认值的更简单的方法。只需在 `__init__` 方法中写入 `self.attr = attr 或 []`。它实现了相同的结果(我认为)并且仍然清晰易读。 (2认同)

bad*_*adp 51

这两个片段做了不同的事情,所以这不是一个品味问题,而是一个在你的背景下正确行为的问题.Python文档解释了差异,但这里有一些例子:

图表A.

class Foo:
  def __init__(self):
    self.num = 1
Run Code Online (Sandbox Code Playgroud)

这绑定num到Foo 实例.对此字段的更改不会传播到其他实例.

从而:

>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1
Run Code Online (Sandbox Code Playgroud)

图表B

class Bar:
  num = 1
Run Code Online (Sandbox Code Playgroud)

这与numBar 绑定.变化传播!

>>> 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
Run Code Online (Sandbox Code Playgroud)

实际答案

如果我不需要类变量,但只需要为我的实例变量设置默认值,这两种方法同样好吗?或者其中一个比另一个更'pythonic'?

展览B中的代码对此是完全错误的:为什么要将类属性(实例创建时的默认值)绑定到单个实例?

展览A中的代码是可以的.

如果你想在构造函数中给出实例变量的默认值,我会这样做:

class Foo:
  def __init__(num = None):
    self.num = num if num is not None else 1
Run Code Online (Sandbox Code Playgroud)

...甚至:

class Foo:
  DEFAULT_NUM = 1
  def __init__(num = None):
    self.num = num if num is not None else DEFAULT_NUM
Run Code Online (Sandbox Code Playgroud)

......甚至:(首选,但当且仅当您处理不可变类型时!)

class Foo:
  def __init__(num = 1):
    self.num = num
Run Code Online (Sandbox Code Playgroud)

这样你可以做到:

foo1 = Foo(4)
foo2 = Foo() #use default
Run Code Online (Sandbox Code Playgroud)


Dun*_*can 6

使用类成员来提供默认值非常有效,只要您小心地只使用不可变值。如果您尝试使用列表或字典来执行此操作,那将是非常致命的。只要默认值为 None ,它也适用于实例属性是对类的引用。

我已经看到这种技术在 repoze 中使用得非常成功,这是一个运行在 Zope 之上的框架。这里的优点不仅是当你的类被持久化到数据库时,只需要保存非默认属性,而且当你需要在模式中添加一个新字段时,所有现有对象都会看到新字段的默认值值而无需实际更改存储的数据。

我发现它在更一般的编码中也能很好地工作,但它是一种风格。使用您最满意的任何东西。