[英]Why is this code not throwing a 'not defined' error?
我創建了一些測試代碼,但我無法真正理解它為什么起作用。
在使用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?
我知道規則是更早定義的 ,不是更高 定義的 ,但是在這種情況下,它們都不是。
這里真的沒有什么可混淆的。
我們,說:“當你調用一個函數foo
有self
參數,查找moo
在self
的命名空間,該值分配給printer
在self
的命名空間,查找printer
在self
的命名空間,稱之為價值” 。 1個
除非/直到您調用該函數,否則任何地方的任何人是否都具有名為moo
的屬性都沒有關系。
當您確實調用該方法時,無論通過什么作為self
,最好具有moo
屬性,否則將獲得AttributeError
。 但這與在任何對象上查找屬性沒有什么不同。 如果編寫def spam(n): return n.bit_length()
作為全局函數,則在調用該函數時,無論傳遞的是什么,因為n
最好具有bit_length
屬性,否則將獲得AttributeError
。
因此,我們將其稱為test.foo()
,因此將test
作為self
。 如果您知道屬性查找的工作方式(關於SO的問題和答案已經很多),則可以進行追溯。 略為簡化:
test.__dict__
是否有'moo'
? 沒有。 type(test).__dict__
是否有'moo'
? 是。 至此就完成了。 同樣,這與我們檢查3
是否具有bit_length()
方法的方式相同; 這里沒有多余的魔法。
這就是全部。
特別要注意, test.__dict__
沒有'moo'
。 方法在構造時( __new__
)的創建要比在初始化時( __init__
)的創建要多。 該實例中沒有任何方法,因為它沒有必須; 他們可以在類型上查找。 2
當然,我們可以了解描述符,方法解析順序和object.__getattribute__
,以及如何編譯和執行class
和def
語句,並可以進行特殊的方法查找以查看是否有自定義的__getattribute__
,依此類推,但是您不需要需要任何一個來理解這個問題。
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`的形式。 如果那是您所期望的,那么請忘記所有。 在Python中,方法只是屬性,而屬性只是按需查找名稱。
2.如果您要問“那為什么綁定方法和函數為什么不一樣?”,答案就是描述符。 簡要地說:當在類型上找到屬性時,Python調用值的__get__
方法,將其傳遞給實例,而函數對象的__get__
方法返回方法對象。 因此,如果要專門引用綁定的方法對象,則每次查找方法時都會創建它們。 特別是,當我們調用foo
時,綁定方法對象尚不存在。 它是通過在foo
查找self.moo
創建的。
雖然@scharette所說的一切可能都是正確的(我對Python的內部知識了解不足,不足以表示信心:)),但我想提出一個替代的解釋,說明為什么可以實例化Test
並調用foo()
:
在實際調用該方法之前,不會執行該方法的主體。 foo()
包含對未定義屬性的引用都沒有關系,它將被很好地解析。 只要在調用foo
之前創建moo
,就可以。
嘗試在解釋器中輸入截斷的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
(請參閱此處以獲取解釋)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.