属性解析如何在Python中工作?

时间:2012-12-29 00:41:25

标签: python oop

请考虑以下代码:

class A(object):
    def do(self):
        print self.z

class B(A):
    def __init__(self, y):
        self.z = y

b = B(3)
b.do()

为什么这样做?执行b = B(3)时,设置属性z。调用b.do()时,Python的MRO在类A中找到do函数。但为什么它能够访问子类中定义的属性?

此功能是否有用例?我想要一个例子。

4 个答案:

答案 0 :(得分:3)

它以一种非常简单的方式工作:当执行设置属性的语句时,它被设置。当执行读取属性的语句时,将读取该语句。当您编写读取属性的代码时,Python不会尝试在执行该代码时猜测属性是否存在;它只是等待代码实际 执行,如果那时属性不存在,那么你将得到一个例外。

默认情况下,您始终可以在用户定义的类的实例上设置任何属性;类通常不会定义可以设置的“允许”属性列表(尽管你也可以这样做),它们实际上只是设置了属性。当然,您只能读取存在的属性,但重要的是,当您实际尝试阅读它们时,它们是否存在。因此,当定义试图读取它的函数时,是否存在属性并不重要;只有在你真正调用这个功能的时候才会这么做。

在您的示例中,有两个类并不重要,因为只有一个实例。由于您只在一个实例上创建一个实例并调用方法,因此两个方法中的self是同一个对象。首先运行__init__,然后在self上设置属性。然后运行do,它从同一self读取属性。这里的所有都是它的。设置属性的位置无关紧要;一旦在实例上设置它,就可以从任何地方访问它:超类,子类,其他类中的代码,或者不在任何类中。

答案 1 :(得分:1)

由于可以随时将新属性添加到任何对象,因此属性解析在执行时发生,而不是在编译时发生。考虑这个可能更有启发性的例子,来自你的:

class A(object):
  def do(self):
    print(self.z) # references an attribute which we have't "declared" in an __init__()

#make a new A
aa = A()

# this next line will error, as you would expect, because aa doesn't have a self.z
aa.do()

# but we can make it work now by simply doing
aa.z = -42
aa.do()

第一个会瞄准你,但第二个会按预期打印-42。

Python对象只是字典。 :)

答案 2 :(得分:0)

由于您实例化了一个B对象,因此调用了B.__init__并添加了一个属性z。此属性现在存在于对象中。它不是一些奇怪的重载的B方法的神奇共享局部变量,它在某种程度上变得无法访问其他地方编写的代码。没有这样的事情。当self传递给超类'方法时,它不会成为另一个对象(如果发生这种情况,多态性应该如何工作?)。

也没有声明A个对象没有这样的对象(尝试o = A(); a.z = whatever),self中的do都不是实例A 1 。事实上,根本没有声明。这都是“继续尝试”;这就是动态语言的定义(不仅仅是动态类型)。

该对象的z属性“无处不在”,始终 2 ,无论访问它的“上下文”如何。 永远不会为解决过程定义代码或 3 的其他几个行为。出于同样的原因,你可以访问列表的方法,尽管没有在listobject.c编写C代码;-)而且,方法并不特殊。它们也只是对象(function类型的实例,因为它发生)并且涉及完全相同的查找序列。

1 这是一个轻微的谎言;在Python 2中,A.do将是“绑定方法”对象,如果第一个参数不满足isinstance(A, <first arg>),它实际上会抛出错误。

2 直到用del或其中一个功能等同物(delattr和朋友)删除它。

3 好吧,有名称错误,理论上,代码可以检查堆栈,从而检查调用者代码对象,从而检查其源代码的位置。

答案 3 :(得分:0)

从对象检索属性时(print self。 attrname ),Python遵循以下步骤:

  1. 如果 attrname 是objectname的特殊(即Python提供的)属性,请将其返回。

  2. 检查objectname.__class__.__dict__ attrname 。如果它存在且是数据描述符,则返回描述符结果。搜索objectname.__class__的所有基础以查找相同的案例。

  3. 检查objectname.__dict__ attrname ,如果找到则返回。如果objectname是一个类,也搜索它的基础。如果它是一个类,并且它或其基础中存在描述符,则返回描述符结果。

  4. 检查objectname.__class__.__dict__ attrname 。如果它存在且是非数据描述符,则返回描述符结果。如果它存在,并且不是描述符,则返回它。如果它存在并且是一个数据描述符,我们不应该在这里,因为我们将在第2点返回。搜索objectname.__class__的所有基础相同的情况。

  5. 提升属性错误

    Source

    Understanding get and set and Python descriptors