Python騷操作:動態定義函式

dicksonjyl560101發表於2019-04-15


Python騷操作:動態定義函式

標題:Python Tips: Dynamic function definition

作者:Philip Trauner

譯者:豌豆花下貓

連結:https://philip-trauner.me/blog/post/python-tips-dynamic-function-definition

基於 MIT 許可協議

在 Python 中,沒有可以在執行時簡化函式定義的語法糖。然而,這並不意味著它就不可能,或者是難以實現。


 from types import FunctionType


foo_code = compile('def foo(): return "bar"', "<string>", "exec")
foo_func = FunctionType(foo_code.co_consts[0], globals(), "foo")

print(foo_func())

輸出:

 

bar

剖析

逐行檢視程式碼,你會發現語言/直譯器的屏障是多麼脆弱。

 

>>> from types import FunctionType

Python 文件通常不會列出那些非用於手動建立的類的特徵(這是完全合理的)。有三種方法可以解決這個問題:help()、inspect(無法檢視內建方法)、以及最後的解決方案,即檢視 CPython 原始碼。

在本例中,help() 與 inspect 都可以完成工作,但是檢視實際的原始碼,則會揭示出關於資料型別的更多細節。

 

>>> from inspect import signature
>>> signature(FunctionType)
<Signature (code, globals, name=None, argdefs=None, closure=None)>

1. code

內部是一個PyCodeobject ,作為types.CodeType 對外開放。非內建方法擁有一個__code__ 屬性,該屬性儲存了相應的程式碼物件。利用內建的 compile() 方法,可以在執行期建立types.CodeType 物件。

2. globals

如果一個函式引用的變數不是在區域性定義的,而是作為引數轉入、由預設引數值提供、或者透過閉包上下文提供,則它會在 globals 字典中查詢。

內建的 globals() 方法會返回一個對當前模組的全域性符號表(global symbol table)的 引用  ,因此能被用來提供一個總是與當前表的狀態相一致的字典。傳入任意其它的字典也是可以的(FunctionType((lambda: bar).__code__, {"bar" : "baz"}, "foo")() == "baz")。

3. name(可選)

控制所返回的函式的__name__ 屬性。只真正對 lambdas 有用(由於匿名性,它們通常沒有名稱),並且重新命名函式。

4. argdefs(可選)

透過傳入一個包含任意型別的物件的元組,提供了一個方式來供應預設引數值(def foo(bar="baz"))。(FunctionType((lambda bar: bar).__code__, {}, "foo", (10,))() == 10)。

5. closure(可選)

(如果需要在 CPython(PyPy,Jython,...)以外的其它 Python VM 中執行,可能不應該觸及,因為它嚴重地依賴於實現細節)。

一個cell 物件的元組。建立 cell 物件並非完全是直截了當的,因為需要呼叫 CPython 的內部元件,但有一個庫可以令它更加方便:exalt (無恥的廣告)。(譯註:這個庫是作者開發的。)

 

>>> foo_code = compile('def foo(): return "bar"', "<string>", "exec")

compile() 是一個內建方法,因此同時也是文件豐富的。

exec 模式被用到,因為定義函式需要用多個語句。

 

>>> foo_func = FunctionType(foo_code.co_consts[0], globals(), "foo")

聚合全部內容,並將動態建立的函式指定給一個變數。

那個被前一句程式碼編譯成的函式,成為了生成的程式碼物件的第一個常量,因此僅僅指向 foo_code 是不充分的。這是 exec 模式的直接後果,因為生成的程式碼物件可以包含多個常量。

 

>>> print(foo_func())

動態生成的函式可以像其它函式一樣被呼叫。

結尾

  • 除了做實驗,需要用到動態建立函式的場景很少。
  • 玩耍(Toying around) Python 的內部構件是一種深入學習這門語言的好方法。
  • 如果需要,可以毫不費力地越過直譯器/語言的界線。

還是一如既往地: 不要濫用語言  (好吧,一點點也無妨,對吧?)

--------(譯文完)--------


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29829936/viewspace-2641364/,如需轉載,請註明出處,否則將追究法律責任。

相關文章