2

考虑以下代码:

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 会do在 A 类中找到该函数。但是为什么它能够访问在子类中定义的属性呢?

这个功能有用例吗?我想举个例子。

4

4 回答 4

3

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

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

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

于 2012-12-29T00:59:31.477 回答
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 对象只是字典。:)

于 2012-12-29T00:49:22.620 回答
0

从对象中检索属性时(打印self.attrname)Python 遵循以下步骤:

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

  2. 检查attrnameobjectname.__class__.__dict___ 如果存在并且是数据描述符,则返回描述符结果。搜索同一案例的所有基地。objectname.__class__

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

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

  5. 引发属性错误

    来源

    了解getset以及 Python 描述符

于 2016-05-01T23:52:36.247 回答
-1

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

也没有声明A对象没有这样的对象(try o = A(); a.z = whatever),也不需要是1self的实例。事实上,根本没有任何声明。一切都是“继续尝试”;这是一种动态语言的定义(不仅仅是动态类型)。doA

该对象的z属性始终“无处不在” 2,无论从哪个“上下文”访问它。解决过程或其他几种行为定义代码的位置无关紧要3。出于同样的原因,尽管没有用 C 语言编写代码,您仍可以访问列表的方法listobject.c;-) 不,方法并不特殊。它们也只是对象(实际上是 type 的实例function)并且涉及完全相同的查找序列。

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

2直到它被del或它的功能等价物(delattr和朋友)之一删除。

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

于 2012-12-29T00:53:46.573 回答