请考虑以下代码:
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
函数。但为什么它能够访问子类中定义的属性?
此功能是否有用例?我想要一个例子。
答案 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遵循以下步骤:
如果 attrname 是objectname的特殊(即Python提供的)属性,请将其返回。
检查objectname.__class__.__dict__
attrname 。如果它存在且是数据描述符,则返回描述符结果。搜索objectname.__class__
的所有基础以查找相同的案例。
检查objectname.__dict__
attrname ,如果找到则返回。如果objectname是一个类,也搜索它的基础。如果它是一个类,并且它或其基础中存在描述符,则返回描述符结果。
检查objectname.__class__.__dict__
attrname 。如果它存在且是非数据描述符,则返回描述符结果。如果它存在,并且不是描述符,则返回它。如果它存在并且是一个数据描述符,我们不应该在这里,因为我们将在第2点返回。搜索objectname.__class__
的所有基础相同的情况。
提升属性错误