class 属性和具有默认值的实例变量之间的区别
Difference between class attribute and instance variable with default value
- class 变量和具有默认值的实例变量之间有什么区别吗?
(特别是在 "normal use" 下的行为方面,在内部我认为它们很可能以不同的方式实现)
- 在什么情况下应该使用哪个版本?
以这两个class为例:
class A:
d = 4
class A:
def __init__(self, d=4):
self.d = d
无论你选择什么版本,当你运行下面的代码时,你会得到相同的结果:
a2 = A()
a = A()
print(a.d) # 4
a.d = 2
print(a.d) # 2
print(a2.d) # 4
看完之后想到了这个问题:
- class attribute behavior
Are there any differences between a class variable and an instance variable with a default value?
嗯,显然是的:一个 class 属性(不是 "variable")属于 class,一个实例属性属于实例。
In what context should I use which version?
当您希望属性由 class 的所有实例共享时使用 class 属性,当您希望属性特定于该实例时使用实例属性。实际上,您很少需要 class 属性。
请注意,如果您为 class 和实例定义相同的属性,则实例上的属性将隐藏 class 的属性。
nb: 以上是非常非常粗略的简化,否则我需要解释整个 Python 对象模型,这值得写一本书
Take these two classes as an example (...) no matter what version you choose, when you run the code below, you'll get the same result
是的,这是此代码段的预期结果。
对于a
:
在第一种情况下,当您第一次打印 a.d
时,a
没有实例属性 d
所以您得到的是 class 属性值.然后通过分配给它来创建实例属性 a.d
,然后它会隐藏 class 属性。
在第二种情况下,a.d
最初有它的默认值,然后你将它重新绑定到另一个值...很普通的东西。
对于a2
:
在第一种情况下,a2.a
将始终是 4
,因为您没有使用实例属性对其进行隐藏,因此它从 class 获取值。
在第二种情况下,它将始终是 4
,因为您没有重新绑定实例属性,所以它仍然是默认值。
现在用列表作为属性尝试同样的事情,并附加到列表而不是重新绑定它:
class A:
d = []
class B:
def __init__(self):
self.d = []
def test(cls):
print("test {}".format(cls.__name__))
a = cls()
print(a.d)
a.d.append(2)
print(a.d)
a2 = cls()
print(a2.d)
if __name__ == "__main__":
test(A)
test(B)
最后一点:您可能已经看到(或者有一天您可能会看到)代码使用 class 属性作为实例的默认值 - 或者您可能想自己这样做(因此提到'default' 实例属性的值)-,就像您的第一个示例一样。 这是不好的做法。它充其量是令人困惑的,如果属性是可变类型,则可能导致错误行为。
TLDR:差异对可见性和特殊 class 属性(例如描述符)很重要。它还会影响 class 签名。
相似之处
定义 class 属性时,它存储在 class 上。同样,当您为方法定义默认值时,it is stored on the method 并且该方法又存储在class 上。最后,class 属性和方法默认值都存储在 class 上——后者只是增加了一个间接级别。
class A:
# store d on class
d = 4
class B:
# store init on class
def __init__(self, d=4): # store d on method
self.d = d
这两个值都是可访问和可写的。它们在可变性方面具有相同的属性,例如如果值为 list
.
>>> A.d
4
>>> B.__init__.__defaults__[0]
4
>>> A.d = 3
>>> B.__init__.__defaults__ = (3,)
>>> A.d
3
>>> B.__init__.__defaults__[0]
3
差异
与 class 或实例属性(即函数等描述符)表现不同的值存在差异。
class AD:
d = lambda x='Nothing': x
class BD:
def __init__(self, d=lambda x='Nothing': x):
self.d = d
查找将调用或跳过描述符协议,导致不同的行为:
>>> AD().d() # class attribute
<__main__.AD at 0x10d4c7c10>
>>> BD().d() # instance attribute
'Nothing'
在 class 上存储默认值本质上与该属性的描述符不兼容。例如,__slots__
和 property
需要 __init__
上的默认值。
class BS:
__slots__ = 'd',
def __init__(self, d=4):
self.d = 4
申请
最重要的区别是 __init__
默认值意味着一个参数。存储在 class 上的默认值不是 class 签名的一部分。
>>> B(d=42)
<__main__.B at 0x10d4dac90>
>>> A(d=42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: A() takes no arguments
因此,当属性应该是可定制的时,总是使用 __init__
默认值。相反,当属性始终以相同常量开头时,请考虑 class 默认值。
请注意,如果一个属性始终以相同的值开头但并非 不可变,请在__init__
中对其进行初始化。如果您需要可变属性的默认值,请使用占位符并在方法中创建默认值。
class C:
def __init__(self, e=None):
self.d = [1, 2, 3, 4]
self.e = e if e is not None else []
- class 变量和具有默认值的实例变量之间有什么区别吗?
(特别是在 "normal use" 下的行为方面,在内部我认为它们很可能以不同的方式实现)
- 在什么情况下应该使用哪个版本?
以这两个class为例:
class A:
d = 4
class A:
def __init__(self, d=4):
self.d = d
无论你选择什么版本,当你运行下面的代码时,你会得到相同的结果:
a2 = A()
a = A()
print(a.d) # 4
a.d = 2
print(a.d) # 2
print(a2.d) # 4
看完之后想到了这个问题:
- class attribute behavior
Are there any differences between a class variable and an instance variable with a default value?
嗯,显然是的:一个 class 属性(不是 "variable")属于 class,一个实例属性属于实例。
In what context should I use which version?
当您希望属性由 class 的所有实例共享时使用 class 属性,当您希望属性特定于该实例时使用实例属性。实际上,您很少需要 class 属性。
请注意,如果您为 class 和实例定义相同的属性,则实例上的属性将隐藏 class 的属性。
nb: 以上是非常非常粗略的简化,否则我需要解释整个 Python 对象模型,这值得写一本书
Take these two classes as an example (...) no matter what version you choose, when you run the code below, you'll get the same result
是的,这是此代码段的预期结果。
对于a
:
在第一种情况下,当您第一次打印 a.d
时,a
没有实例属性 d
所以您得到的是 class 属性值.然后通过分配给它来创建实例属性 a.d
,然后它会隐藏 class 属性。
在第二种情况下,a.d
最初有它的默认值,然后你将它重新绑定到另一个值...很普通的东西。
对于a2
:
在第一种情况下,a2.a
将始终是 4
,因为您没有使用实例属性对其进行隐藏,因此它从 class 获取值。
在第二种情况下,它将始终是 4
,因为您没有重新绑定实例属性,所以它仍然是默认值。
现在用列表作为属性尝试同样的事情,并附加到列表而不是重新绑定它:
class A:
d = []
class B:
def __init__(self):
self.d = []
def test(cls):
print("test {}".format(cls.__name__))
a = cls()
print(a.d)
a.d.append(2)
print(a.d)
a2 = cls()
print(a2.d)
if __name__ == "__main__":
test(A)
test(B)
最后一点:您可能已经看到(或者有一天您可能会看到)代码使用 class 属性作为实例的默认值 - 或者您可能想自己这样做(因此提到'default' 实例属性的值)-,就像您的第一个示例一样。 这是不好的做法。它充其量是令人困惑的,如果属性是可变类型,则可能导致错误行为。
TLDR:差异对可见性和特殊 class 属性(例如描述符)很重要。它还会影响 class 签名。
相似之处
定义 class 属性时,它存储在 class 上。同样,当您为方法定义默认值时,it is stored on the method 并且该方法又存储在class 上。最后,class 属性和方法默认值都存储在 class 上——后者只是增加了一个间接级别。
class A:
# store d on class
d = 4
class B:
# store init on class
def __init__(self, d=4): # store d on method
self.d = d
这两个值都是可访问和可写的。它们在可变性方面具有相同的属性,例如如果值为 list
.
>>> A.d
4
>>> B.__init__.__defaults__[0]
4
>>> A.d = 3
>>> B.__init__.__defaults__ = (3,)
>>> A.d
3
>>> B.__init__.__defaults__[0]
3
差异
与 class 或实例属性(即函数等描述符)表现不同的值存在差异。
class AD:
d = lambda x='Nothing': x
class BD:
def __init__(self, d=lambda x='Nothing': x):
self.d = d
查找将调用或跳过描述符协议,导致不同的行为:
>>> AD().d() # class attribute
<__main__.AD at 0x10d4c7c10>
>>> BD().d() # instance attribute
'Nothing'
在 class 上存储默认值本质上与该属性的描述符不兼容。例如,__slots__
和 property
需要 __init__
上的默认值。
class BS:
__slots__ = 'd',
def __init__(self, d=4):
self.d = 4
申请
最重要的区别是 __init__
默认值意味着一个参数。存储在 class 上的默认值不是 class 签名的一部分。
>>> B(d=42)
<__main__.B at 0x10d4dac90>
>>> A(d=42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: A() takes no arguments
因此,当属性应该是可定制的时,总是使用 __init__
默认值。相反,当属性始终以相同常量开头时,请考虑 class 默认值。
请注意,如果一个属性始终以相同的值开头但并非 不可变,请在__init__
中对其进行初始化。如果您需要可变属性的默认值,请使用占位符并在方法中创建默认值。
class C:
def __init__(self, e=None):
self.d = [1, 2, 3, 4]
self.e = e if e is not None else []