[英]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". 我们,说:“当你调用一个函数
foo
有self
参数,查找moo
在self
的命名空间,该值分配给printer
在self
的命名空间,查找printer
在self
的命名空间,称之为价值” 。 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:
略为简化:
test.__dict__
have a 'moo'
? test.__dict__
是否有'moo'
? No. type(test).__dict__
have a 'moo'
? type(test).__dict__
是否有'moo'
? Yes. 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__
,以及如何编译和执行class
和def
语句,并可以进行特殊的方法查找以查看是否有自定义的__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.