根据官方文档,“数据属性覆盖具有相同名称的方法属性”。但是,我发现这是不正确的。
class C:
x = 111
def x(self):
print('I am x')
c = C()
print(c.x)
Run Code Online (Sandbox Code Playgroud)
上面代码中的print语句显示cx是一种方法,而不是分配给111的数据属性。因此,此代码表明数据属性不一定覆盖具有相同名称的方法属性,并且文档错误。谁能确认我的发现?
PS我在Python 3.5和Python 2.7中都尝试了该代码,并获得了相同的结果。
我猜这个教程很不幸(因为模棱两可),措辞和
[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
Run Code Online (Sandbox Code Playgroud)
将其与重新定义又称为重新分配(或本教程中称为“覆盖”)的对比:
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'
Run Code Online (Sandbox Code Playgroud)
方法C.x()没有暂时隐藏数据属性C.x。它取代了它,所以当我们删除该方法时,x完全丢失了C,而不是data属性以该名称重新出现。
实例化增加了另一个名称空间,因此也增加了阴影的机会:
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
Run Code Online (Sandbox Code Playgroud)
实际上,Python中所有(嵌套的)名称空间都是这样工作的:程序包,模块,类,函数和方法,实例对象,内部类,嵌套函数...
读取变量时,名称空间层次结构将自下而上,直到找到名称为止。(在这里读意味着找到变量名称绑定到的值(对象,函数/方法或内置值)。如果该值是可变的,则也可以用来更改该值。)
另一方面,在设置(定义或重新定义)变量时,将使用当前名称空间的名称:如果该名称空间中已经存在该名称(而不是仅从另一个名称空间包含该名称),则绑定到新值;或者一个新创建的名称(如果以前不存在)。