简体   繁体   English

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

[英]Why is this code not throwing a 'not defined' error?

I created some test code, but I can't really understand why it works. 我创建了一些测试代码,但我无法真正理解它为什么起作用。

Shouldn't moo be defined before we can use it? 在使用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()

Output: 输出:

$ python test.py
Y u printing?

I know that the rule is define earlier , not higher , but in this case it's neither of those. 我知道规则是更早定义的 ,不是更高 定义的 ,但是在这种情况下,它们都不是。

There's really nothing to be confused about here. 这里真的没有什么可混淆的。

We have a function that says "when you call foo with a self parameter, look up moo in self 's namespace, assign that value to printer in self 's namespace, look up printer in self 's namespace, and call that value". 我们,说:“当你调用一个函数fooself参数,查找mooself的命名空间,该值分配给printerself的命名空间,查找printerself的命名空间,称之为价值” 。 1 1个

Unless/until you call that function, it doesn't matter whether or not anyone anywhere has an attribute named moo . 除非/直到您调用该函数,否则任何地方的任何人是否都具有名为moo的属性都没有关系。

When you do call that method, whatever you pass as the self had better have a moo attribute or you're going to get an AttributeError . 当您确实调用该方法时,无论通过什么作为self ,最好具有moo属性,否则将获得AttributeError But this is no different from looking up an attribute on any object. 但这与在任何对象上查找属性没有什么不同。 If you write def spam(n): return n.bit_length() as a global function, when you call that function, whatever you pass as the n had better have a bit_length attribute or you're going to get an AttributeError . 如果编写def spam(n): return n.bit_length()作为全局函数,则在调用该函数时,无论传递的是什么,因为n最好具有bit_length属性,否则将获得AttributeError

So, we're calling it as test.foo() , so we're passing test as self . 因此,我们将其称为test.foo() ,因此将test作为self If you know how attribute lookup works (and there are already plenty of questions and answers on SO about that), you can trace this through. 如果您知道属性查找的工作方式(关于SO的问题和答案已经很多),则可以进行追溯。 Slightly oversimplified: 略为简化:

  • Does test.__dict__ have a 'moo' ? test.__dict__是否有'moo' No. 没有。
  • Does type(test).__dict__ have a 'moo' ? type(test).__dict__是否有'moo' Yes. 是。 So we're done. 至此就完成了。

Again, this is the same way we check if 3 has a bit_length() method; 同样,这与我们检查3是否具有bit_length()方法的方式相同; there's no extra magic here. 这里没有多余的魔法。

That's really all there is to it. 这就是全部。


In particular, notice that test.__dict__ does not have a 'moo' . 特别要注意, test.__dict__ 没有'moo' Methods don't get created at construction time ( __new__ ) any more than they get created at initialization time ( __init__ ). 方法在构造时( __new__ )的创建要比在初始化时( __init__ )的创建要多。 The instance doesn't have any methods in it, because it doesn't have to; 该实例中没有任何方法,因为它没有必须; they can be looked up on the type. 他们可以在类型上查找。 2 2

Sure, we could get into descriptors, and method resolution order, and object.__getattribute__ , and how class and def statements are compiled and executed, and special method lookup to see if there's a custom __getattribute__ , and so on, but you don't need any of that to understand this question. 当然,我们可以了解描述符,方法解析顺序和object.__getattribute__ ,以及如何编译和执行classdef语句,并可以进行特殊的方法查找以查看是否有自定义的__getattribute__ ,依此类推,但是您不需要需要任何一个来理解这个问题。


1. If you're confused by this, it's probably because you're thinking in terms of semi-OO languages like C++ and its descendants, where a class has to specify all of its instances' attributes and methods, so the compiler can look at this->moo() , work out that this has a static type of Foo , work out that moo is the third method defined on Foo , and compile it into something like this->vptr2`. 1.如果对此感到困惑,那可能是因为您在考虑使用半面向对象语言(例如C ++及其后代),在该类中,类必须指定其所有实例的属性和方法,以便编译器可以查找在this->moo() ,计算出它this has a static type of Foo , work out that moo is the third method defined on Foo上is the third method defined on , and compile it into something like this-> vptr2`的形式。 If that's what you're expecting, forget all of it. 如果那是您所期望的,那么请忘记所有。 In Python, methods are just attributes, and attributes are just looked up, by name, on demand. 在Python中,方法只是属性,而属性只是按需查找名称。

2. If you're going to ask "then why is a bound method not the same thing as a function?", the answer is descriptors. 2.如果您要问“那为什么绑定方法和函数为什么不一样?”,答案就是描述符。 Briefly: when an attribute is found on the type, Python calls the value's __get__ method, passing it the instance, and function objects' __get__ methods return method objects. 简要地说:当在类型上找到属性时,Python调用值的__get__方法,将其传递给实例,而函数对象的__get__方法返回方法对象。 So, if you want to refer specifically to bound method objects, then they get created every time a method is looked up . 因此,如果要专门引用绑定的方法对象,则每次查找方法时都会创建它们。 In particular, the bound method object does not exist yet when we call foo ; 特别是,当我们调用foo时,绑定方法对象尚不存在。 it gets created by looking up self.moo inside foo . 它是通过在foo查找self.moo创建的。

While all that @scharette says is likely true (I don't know enough of Python internals to agree with confidence :) ), I'd like to propose an alternative explanation as to why one can instantiate Test and call foo() : 虽然@scharette所说的一切可能都是正确的(我对Python的内部知识了解不足,不足以表示信心:)),但我想提出一个替代的解释,说明为什么可以实例化Test并调用foo()

The method's body is not executed until you actually call it. 在实际调用该方法之前,不会执行该方法的主体。 It does not matter if foo() contains references to undefined attributes, it will be parsed fine. foo()包含对未定义属性的引用都没有关系,它将被很好地解析。 As long as you create moo before you call foo , you're ok. 只要在调用foo之前创建moo ,就可以。

Try entering a truncated Test class in your interpreter: 尝试在解释器中输入截断的Test类:

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

No moo , so we get this: 没有moo ,所以我们得到这个:

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

Let's add moo to the class now: 让我们现在将moo添加到类中

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

Alternatively, you can add moo directly to the instance : 另外,您可以将moo直接添加到实例

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

The only difference is that the instance's moo does not take a self (see here for explanation). 唯一的区别是实例的moo不具有self (请参阅此处以获取解释)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM