为什么此代码未引发“未定义”错误?

时间:2018-07-19 13:08:26

标签: python python-3.x scope

我创建了一些测试代码,但我真的不明白它为什么起作用。

在使用moo之前不应该对其进行定义吗?

#!/usr/bin/python3

class Test():
    def __init__(self):
        self.printer = None

    def foo(self):
        self.printer = self.moo
        self.printer()

    def moo(self):
        print("Y u printing?")

test = Test()

test.foo()

输出:

$ python test.py
Y u printing?

我知道规则是更早定义,而不是更高,但是在这种情况下,它们都不是。

2 个答案:

答案 0 :(得分:3)

这里真的没有什么可混淆的。

我们有一个函数说:“当您使用foo参数调用self时,在moo的命名空间中查找self,并将该值分配给{{ 1}}在printer的命名空间中,在self的命名空间中查找printer,并调用该值。” 1

除非/直到您调用该函数,否则任何地方的任何人是否都具有名为self的属性都没关系。

当您执行调用该方法时,无论您作为moo传递的内容最好具有self属性,否则您将获得moo 。但这与在 any 对象上查找属性没有什么不同。如果您将AttributeError编写为全局函数,则在调用该函数时,以def spam(n): return n.bit_length()传递的内容最好具有n属性,否则您将获得{{1 }}。

因此,我们将其称为bit_length,因此将AttributeError传递为test.foo()。如果您知道属性查找的工作方式(关于SO的问题和答案已经很多),则可以进行追溯。略为简化:

  • test是否有self?不。
  • test.__dict__是否有'moo'?是。我们完成了。

再次,这与我们检查type(test).__dict__是否具有'moo'方法的方法相同;这里没有多余的魔法。

这就是全部。


尤其要注意,3 没有bit_length() 。方法在构造时(test.__dict__)的创建比在初始化时('moo')的创建要多。该实例中没有任何方法,因为它没有必须;可以在类型上查找它们。 2

当然,我们可以了解描述符,方法解析顺序,__new____init__object.__getattribute__语句的编译和执行方式,以及特殊的方法查找以了解是否存在自定义class,依此类推,但您无需使用任何一个即可理解此问题。


1。如果对此感到困惑,那可能是因为您正在考虑使用半面向对象语言(例如C ++及其后代),在该类中,类必须指定其所有实例的属性和方法,因此编译器可以查看{ {1}},计算出def Foo __getattribute__ moo this->moo() Foo this has a static type of this-> vptr2`。如果那是您所期望的,那么请忘记所有。在Python中,方法只是属性,而属性只是按需查找名称。

2。如果您要问“那为什么绑定方法和函数为什么不一样?”,答案就是描述符。简要地说:当在类型上找到属性时,Python调用值的, work out that方法,并将其传递给实例,而函数对象的is the third method defined on方法则返回方法对象。因此,如果要专门引用绑定的方法对象,则每次查找方法时都会创建它们。特别是,当我们调用, and compile it into something like时,绑定方法对象尚不存在。它是通过在__get__内查找__get__来创建的。

答案 1 :(得分:1)

虽然@scharette所说的所有内容都是正确的(我对Python的内部知识了解不足,不足以表示信心:)),但我想提出一个替代性的解释,说明为什么可以实例化Test并致电foo()

该方法的主体直到您实际调用它之后才执行。 foo()是否包含对未定义属性的引用并不重要,它将被很好地解析。只要在致电moo之前创建foo,就可以了。

尝试在解释器中输入截断的Test类:

   class Test():
        def __init__(self):
            self.printer = None
        def foo(self):
            self.printer = self.moo
            self.printer()

moo,所以我们得到了:

>>> test = Test()
>>> test.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in foo

现在将moo添加到课程

>>> def moo(self):
...     print("Y u printing?")
...
>>> Test.moo = moo
>>> test1 = Test()
>>> test1.foo()
Y u printing?
>>>

或者,您可以将moo直接添加到实例

>>> def moo():
...     print("Y u printing?")
...
>>> test.moo = moo
>>> test.foo()
Y u printing?

唯一的区别是实例的moo不使用self(有关解释,请参见here)。