簡體   English   中英

函數,可調用對象以及如何在Python中創建它們

[英]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()

那么我的問題是:

  1. 當您創建函數def foo1(): ,這與使用元類定義類的過程有何不同? 是否有type()的版本,但對於函數,一個元函數?
  2. 假設有人想編寫他們自己的元函數(這背后的真正原因),是否更好地使用裝飾器,或者可能是使得可調用類的元類? 它們會提供什么樣的優勢(使得可調用類的元類看起來很笨重)?
  3. 具有可調用對象的唯一目的是具有可以在其中存儲信息的功能嗎?

函數也是可調用對象:

>>> 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM