[英]Functions, Callable Objects, and how both are created in Python
我想知道函數和可調用對象之間更復雜的差異。 例如,如果你這樣做:
def foo1():
return 2 + 4
class foo2:
def __call__():
return 2 + 4
import sys
sys.getsizeof(foo1) # returns 136
sys.getsizeof(foo2) # returns 1016
功能和可調用對象之間顯然存在很大差異。 但是,我找不到很多關於幕后發生的事情的文檔。 我知道函數是一流的對象,但我也知道類比常規函數要多得多。 class foo2是使用元類type()
。
那么我的問題是:
def foo1():
,這與使用元類定義類的過程有何不同? 是否有type()
的版本,但對於函數,一個元函數? 函數也是可調用對象:
>>> foo1.__call__
<method-wrapper '__call__' of function object at 0x105bafd90>
>>> callable(foo1)
True
但是課程需要跟蹤更多信息 ; 你在這里給它一個__call__
方法並不重要。 任何類都比函數大:
>>> import sys
>>> def foo1():
... return 2 + 4
...
>>> class foo3:
... pass
...
>>> sys.getsizeof(foo1)
136
>>> sys.getsizeof(foo3)
1056
函數對象是一種不同的對象類型:
>>> type(foo1)
<class 'function'>
並且相當緊湊,因為肉實際上不在函數對象中,而是在函數對象引用的其他對象中:
>>> sys.getsizeof(foo1.__code__)
144
>>> sys.getsizeof(foo1.__dict__)
240
這就是真的; 不同類型的對象具有不同的大小,因為它們跟蹤不同的事物或使用組合來將東西存儲在其他對象中。
如果您types.FunctionType
,可以使用type(foo1)
返回值(或types.FunctionType
,它是同一個對象)來生成新的函數對象:
>>> import types
>>> types.FunctionType(foo1.__code__, globals(), 'somename')
<function foo1 at 0x105fbc510>
這基本上是解釋器在執行def function(..): ...
語句時所執行的操作。
當對API有意義時,使用__call__
使自定義類可調用。 例如, enum.Enum()
類是可調用的 ,特別是因為使用調用語法為您提供了與訂閱不同的語法,該語法用於其他目的。 並且xmlrpc.client.ServerProxy()
對象生成作為_Method
實例的方法對象,因為它們代理遠程調用,而不是本地函數。
是否有類型()的版本,但對於函數,一個元函數?
有點。 函數有一個類型,該類型至少可以用來構造代碼對象和全局字典的新函數。 (代碼對象可以使用compile()
構建或從現有函數中獲取,或者,如果您是受虐狂,則使用代碼類型的構造函數從字節碼字符串和其他信息構建。)函數類型不是元代碼因為函數是實例而不是類。 您可以使用type(lambda:0)
(將任何函數放在括號中)獲取此類型,並執行help(type(lambda:0))
以查看其參數。 功能類型是的一個實例type
。
說有人想寫自己的元功能
抱歉,你不能將函數類型子類化。
class FunkyFunc(type(lambda: 0)): pass
TypeError: Error when calling the metaclass bases
type 'function' is not an acceptable base type
具有可調用對象的唯一目的是具有可以在其中存儲信息的功能嗎?
它有很多用途。 你的例子是一個(盡管函數實例可以有屬性,類可以提供更好的屬性文檔); 反面,使一個普通的舊數據對象可以調用,是另一個(我在這個答案中給出了一種時髦的例子)。 如果實例可調用,則可以使用類來編寫裝飾器。 您可以在元類上使用__call__
來自定義實例構造。 並且您可以使用它來編寫具有不同行為的函數,就像您實際上可以將函數類型子類化一樣。 (如果在函數中需要C風格的“靜態”變量,可以將其寫為帶有__call__
方法的類,並使用屬性來存儲靜態數據。)
關於函數對象的一個有趣的事情是,因為它們是可調用的,所以它們本身具有__call__
方法。 但是一個方法是可調用的,所以__call__
方法也有一個__call__
方法,而__call__
方法(可調用)也有一個__call__
方法,依此類推, 無窮無盡。 :-)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.