這篇文章帶給大家的內容是關於Python動態定義函數的方法介紹,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
基於 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 原始碼。 (建議:python教學)
在本例中,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 的內部構件是深入學習這門語言的好方法。
如果需要,可以毫不費力地越過解釋器/語言的界線。
以上是Python動態定義函數的方法介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!