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

时间:2022-09-20 10:39:37

Should I give my class members default values like this:

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

class Foo:
    num = 1

or like this?

或者像这样?

class Foo:
    def __init__(self):
        self.num = 1

In this question I discovered that in both cases,

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

bar = Foo()
bar.num += 1

is a well-defined operation.

是一个定义明确的操作。

I understand that the first method will give me a class variable while the second one will not. However, 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?

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

One thing I've noticed is that in the Django tutorial, they use the second method to declare Models. Personally I think the second method is more elegant, but I'd like to know what the 'standard' way is.

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

5 个解决方案

#1


93  

Extending bp's answer, I wanted to show you what he meant by immutable types.

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

First, this is okay:

首先,这没关系:

>>> class TestB():
...     def __init__(self, attr=1):
...         self.attr = attr
...     
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1

However, this only works for immutable (unchangable) types. If the default value was mutable (meaning it can be replaced), this would happen instead:

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

>>> class Test():
...     def __init__(self, attr=[]):
...         self.attr = attr
...     
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>> 

Note both a and b have a shared attribute. This is often unwanted.

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

This is the Pythonic way of defining default values for instance variables, when the type is mutable:

当类型是可变的时,这是为实例变量定义默认值的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
[]

The reason my first snippet of code works is because, with immutable types, Python creates a new instance of it whenever you want one. If you needed to add 1 to 1, Python makes a new 2 for you, because the old 1 cannot be changed. The reason is mostly for hashing, I believe.

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

#2


40  

The two snippets do different things, so it's not a matter of taste but a matter of what's the right behaviour in your context. Python documentation explains the difference, but here are some examples:

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

Exhibit A

class Foo:
  def __init__(self):
    self.num = 1

This binds num to the Foo instances. Change to this field is not propagated to other instances.

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

Thus:

从而:

>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1

Exhibit B

class Bar:
  num = 1

This binds num to the Bar class. Changes are propagated!

这会将num绑定到Bar类。变化传播!

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

Actual answer

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?

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

The code in exhibit B is plain wrong for this: why would you want to bind a class attribute (default value on instance creation) to the single instance?

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

The code in exhibit A is okay.

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

If you want to give defaults for instance variables in your constructor I would however do this:

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

class Foo:
  def __init__(num = None):
    self.num = num if num is not None else 1

...or even:

...甚至:

class Foo:
  DEFAULT_NUM = 1
  def __init__(num = None):
    self.num = num if num is not None else DEFAULT_NUM

...or even: (preferrable, but if and only if you are dealing with immutable types!)

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

class Foo:
  def __init__(num = 1):
    self.num = num

This way you can do:

这样你可以做到:

foo1 = Foo(4)
foo2 = Foo() #use default

#3


3  

Using class members for default values of instance variables is not a good idea, and it's the first time I've seen this idea mentioned at all. It works in your example, but it may fail in a lot of cases. E.g., if the value is mutable, mutating it on an unmodified instance will alter the default:

使用类成员来获取实例变量的默认值并不是一个好主意,而且这是我第一次看到这个想法。它适用于您的示例,但在很多情况下可能会失败。例如,如果值是可变的,则在未修改的实例上进行变更将改变默认值:

>>> class c:
...     l = []
... 
>>> x = c()
>>> y = c()
>>> x.l
[]
>>> y.l
[]
>>> x.l.append(10)
>>> y.l
[10]
>>> c.l
[10]

#4


3  

Using class members to give default values works very well just so long as you are careful only to do it with immutable values. If you try to do it with a list or a dict that would be pretty deadly. It also works where the instance attribute is a reference to a class just so long as the default value is None.

只要您只注意使用不可变值,使用类成员给出默认值就可以很好地工作。如果你试图用一个非常致命的列表或词典来做这件事。它也适用于instance属性是对类的引用,只要默认值为None即可。

I've seen this technique used very successfully in repoze which is a framework that runs on top of Zope. The advantage here is not just that when your class is persisted to the database only the non-default attributes need to be saved, but also when you need to add a new field into the schema all the existing objects see the new field with its default value without any need to actually change the stored data.

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

I find it also works well in more general coding, but it's a style thing. Use whatever you are happiest with.

我发现它在更通用的编码中也很有效,但它是一种风格的东西。使用你最开心的任何东西。

#5


0  

You can also declare class variables as None which will prevent propagation. This is useful when you need a well defined class and want to prevent AttributeErrors. For example:

您还可以将类变量声明为None,这将阻止传播。当您需要一个定义良好的类并希望防止AttributeErrors时,这非常有用。例如:

>>> class TestClass(object):
...     t = None
... 
>>> test = TestClass()
>>> test.t
>>> test2 = TestClass()
>>> test.t = 'test'
>>> test.t
'test'
>>> test2.t
>>>

Also if you need defaults:

如果您需要默认值:

>>> class TestClassDefaults(object):
...    t = None
...    def __init__(self, t=None):
...       self.t = t
... 
>>> test = TestClassDefaults()
>>> test.t
>>> test2 = TestClassDefaults([])
>>> test2.t
[]
>>> test.t
>>>

Of course still follow the info in the other answers about using mutable vs immutable types as the default in __init__.

当然,仍然遵循其他答案中关于使用可变与不可变类型作为__init__中的默认值的信息。

#1


93  

Extending bp's answer, I wanted to show you what he meant by immutable types.

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

First, this is okay:

首先,这没关系:

>>> class TestB():
...     def __init__(self, attr=1):
...         self.attr = attr
...     
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1

However, this only works for immutable (unchangable) types. If the default value was mutable (meaning it can be replaced), this would happen instead:

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

>>> class Test():
...     def __init__(self, attr=[]):
...         self.attr = attr
...     
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>> 

Note both a and b have a shared attribute. This is often unwanted.

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

This is the Pythonic way of defining default values for instance variables, when the type is mutable:

当类型是可变的时,这是为实例变量定义默认值的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
[]

The reason my first snippet of code works is because, with immutable types, Python creates a new instance of it whenever you want one. If you needed to add 1 to 1, Python makes a new 2 for you, because the old 1 cannot be changed. The reason is mostly for hashing, I believe.

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

#2


40  

The two snippets do different things, so it's not a matter of taste but a matter of what's the right behaviour in your context. Python documentation explains the difference, but here are some examples:

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

Exhibit A

class Foo:
  def __init__(self):
    self.num = 1

This binds num to the Foo instances. Change to this field is not propagated to other instances.

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

Thus:

从而:

>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1

Exhibit B

class Bar:
  num = 1

This binds num to the Bar class. Changes are propagated!

这会将num绑定到Bar类。变化传播!

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

Actual answer

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?

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

The code in exhibit B is plain wrong for this: why would you want to bind a class attribute (default value on instance creation) to the single instance?

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

The code in exhibit A is okay.

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

If you want to give defaults for instance variables in your constructor I would however do this:

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

class Foo:
  def __init__(num = None):
    self.num = num if num is not None else 1

...or even:

...甚至:

class Foo:
  DEFAULT_NUM = 1
  def __init__(num = None):
    self.num = num if num is not None else DEFAULT_NUM

...or even: (preferrable, but if and only if you are dealing with immutable types!)

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

class Foo:
  def __init__(num = 1):
    self.num = num

This way you can do:

这样你可以做到:

foo1 = Foo(4)
foo2 = Foo() #use default

#3


3  

Using class members for default values of instance variables is not a good idea, and it's the first time I've seen this idea mentioned at all. It works in your example, but it may fail in a lot of cases. E.g., if the value is mutable, mutating it on an unmodified instance will alter the default:

使用类成员来获取实例变量的默认值并不是一个好主意,而且这是我第一次看到这个想法。它适用于您的示例,但在很多情况下可能会失败。例如,如果值是可变的,则在未修改的实例上进行变更将改变默认值:

>>> class c:
...     l = []
... 
>>> x = c()
>>> y = c()
>>> x.l
[]
>>> y.l
[]
>>> x.l.append(10)
>>> y.l
[10]
>>> c.l
[10]

#4


3  

Using class members to give default values works very well just so long as you are careful only to do it with immutable values. If you try to do it with a list or a dict that would be pretty deadly. It also works where the instance attribute is a reference to a class just so long as the default value is None.

只要您只注意使用不可变值,使用类成员给出默认值就可以很好地工作。如果你试图用一个非常致命的列表或词典来做这件事。它也适用于instance属性是对类的引用,只要默认值为None即可。

I've seen this technique used very successfully in repoze which is a framework that runs on top of Zope. The advantage here is not just that when your class is persisted to the database only the non-default attributes need to be saved, but also when you need to add a new field into the schema all the existing objects see the new field with its default value without any need to actually change the stored data.

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

I find it also works well in more general coding, but it's a style thing. Use whatever you are happiest with.

我发现它在更通用的编码中也很有效,但它是一种风格的东西。使用你最开心的任何东西。

#5


0  

You can also declare class variables as None which will prevent propagation. This is useful when you need a well defined class and want to prevent AttributeErrors. For example:

您还可以将类变量声明为None,这将阻止传播。当您需要一个定义良好的类并希望防止AttributeErrors时,这非常有用。例如:

>>> class TestClass(object):
...     t = None
... 
>>> test = TestClass()
>>> test.t
>>> test2 = TestClass()
>>> test.t = 'test'
>>> test.t
'test'
>>> test2.t
>>>

Also if you need defaults:

如果您需要默认值:

>>> class TestClassDefaults(object):
...    t = None
...    def __init__(self, t=None):
...       self.t = t
... 
>>> test = TestClassDefaults()
>>> test.t
>>> test2 = TestClassDefaults([])
>>> test2.t
[]
>>> test.t
>>>

Of course still follow the info in the other answers about using mutable vs immutable types as the default in __init__.

当然,仍然遵循其他答案中关于使用可变与不可变类型作为__init__中的默认值的信息。