数据属性是否覆盖Python中类的方法属性?

时间:2016-08-18 19:00:45

标签: python

根据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中的代码并获得了相同的结果。

4 个答案:

答案 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的第二个定义,这就是官方文档看起来错误的原因。