根据official documentation,“数据属性覆盖具有相同名称的方法属性”。但是,我发现这是不正确的。
class C:
x = 111
def x(self):
print('I am x')
c = C()
print(c.x)
上面代码中的print语句显示c.x是一个方法,而不是分配给111的数据属性。因此,此代码表明数据属性不一定覆盖具有相同名称的方法属性,并且文档错误。谁能证实我的发现?
P.S。我尝试了Python 3.5和Python 2.7中的代码并获得了相同的结果。
答案 0 :(得分:4)
我觉得教程不幸(因为含糊不清)措辞和
[d] ata属性覆盖具有相同名称的方法属性
它实际上意味着“数据属性覆盖以前分配/定义的同名方法属性,反之亦然:方法属性覆盖以前分配/定义的同名数据属性。”
“Duh”,您可能认为“数据属性也会覆盖以前分配的同名数据属性,那么最重要的是什么?为什么会提到这个?”在变量(无论是否称为“属性”)中分配和重新分配(在引用的教程中称为“重写”)是命令式编程语言的所有原型特征之一。
好吧,我来介绍一下
Python类是名称空间。因此,本教程可能会在这里告诉我们的是,类中的数据属性和方法属性共享命名空间。
但是,不同类的属性不是这种情况。如果一个类继承自另一个类,则它可以访问其父级的名称。如果在继承类中重用了方法定义或数据赋值的名称,则父类将保留原始值。在儿童班中,他们只是暂时被遮蔽。如果从子类中删除该名称,它也将再次提供对同名父项属性的访问:
class A:
x = 111
class B1(A):
x = 123 # Shadows A.x
assert B1.x == 123
del B1.x # But after removing B1's own x attribute ...
assert B1.x == 111 # ... B1.x is just an alias to A.x !
# Shadowing can happen at any time:
class B2(A):
pass
assert B2.x == A.x == 111
B2.x = 5 # shadowing attributes can also be added after the class definition
assert B2.x == 5
assert A.x == 111
del B2.x
assert B2.x == A.x == 111
将此与重新定义a.k.a.重新分配(或教程称之为“覆盖”)进行对比:
class C:
x = 555
def x(self):
print('I am x')
C().x() # outputs "I am x"
del C.x
print(C.x) # AttributeError: 'C' object has no attribute 'x'
方法C.x()
未暂时隐藏数据属性C.x
。它取代了它,因此当我们删除该方法时,x
内的C
完全丢失,而不是在该名称下重新显示的数据属性。
实例化添加了另一个命名空间,从而增加了另一个阴影的机会:
a = A()
assert a.x == 111 # instance namespace includes class namespace
a.x = 1000
assert a.x == 1000
assert A.x == 111 # class attribute unchanged
del a.x
assert a.x == 111 # sees A.x again
事实上,Python中的所有(嵌套)命名空间都是这样工作的:包,模块,类,函数和方法,实例对象,内部类,嵌套函数......
读取变量时,命名空间层次结构自下而上走,直到找到名称。 (读取这里意味着找到变量名称绑定的值(对象,函数/方法或内置)。如果值是可变的,这也可以用来更改值。)
另一方面,在设置(定义或重新定义)变量时,使用当前命名空间的名称:如果名称已存在于该命名空间中,则重新绑定到新值(而不是仅包含在另一个命名空间中)命名空间)或新创建的名称(如果以前不存在)。
答案 1 :(得分:3)
属性覆盖方法,反之亦然。根据经验,后者优先于前者。所以,如果你这样做
class C:
x = 111
def x(self):
print('I am x')
x = 112
c = C()
print(c.x)
你会得到
112
答案 2 :(得分:0)
您的示例正在尝试将共享类数据属性C.x与实例方法c.x()比较。由于c是实例,因此获取后者是有意义的。下面,我使用c.__dict__
自省工具检查实例名称空间中是否存在具有键/值对的数据属性True或False:
inDict = (lambda a, b: a in b.__dict__.keys())
class C:
x = 111
def x(self):
print('I am x')
c = C()
print("C.x() hides shared class attribute x = 111")
print(c.x, '\n', c.__class__, inDict('x', c))
c.x()
C.x
c.x = 222
print("Now, c.x is an instance attribute.")
print(c.x, '\n', c.__class__, inDict('x', c))
import sys
try:
c.x()
except:
print("c.x hides c.x() => except error: {0}".format(sys.exc_info()[1:2]))
print("Instead, call C.x() in class namespace by passing instance c as self")
C.x(c)
class B:
def __init__(self):
self.x = 111
def x(self):
print('I am x')
b = B()
print(b.x, '\n', b.__class__, inDict('x', b))
运行结果:
C.x() hides shared class attribute x = 111
<bound method C.x of <__main__.C object at 0x00000212DF8086A0>>
<class '__main__.C'> False
I am x
Now, c.x is an instance attribute.
222
<class '__main__.C'> True
c.x hides c.x() => except error: (TypeError("'int' object is not callable"),)
Instead, call C.x() in class namespace by passing instance c as self
I am x
111
<class '__main__.B'> True
Process finished with exit code 0
如您所见,c.x开头是您的代码所示的方法。 但是,一旦我们为c.x分配了一个值222,它就会成为数据属性,因为 规则“数据属性覆盖具有相同名称的方法属性” 现在都适用,因为它们都在实例名称空间c中。 最后,在新的类B中,我们同时具有数据和方法属性 b实例名称空间,它再次显示规则成立。
答案 3 :(得分:-1)
你错过了x的第二个定义,这就是官方文档看起来错误的原因。