Python騷操作之動(dòng)態(tài)定義函數(shù)
在 Python 中,沒(méi)有可以在運(yùn)行時(shí)簡(jiǎn)化函數(shù)定義的語(yǔ)法糖。然而,這并不意味著它就不可能,或者是難以實(shí)現(xiàn)。
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
剖析
逐行檢視代碼,你會(huì)發(fā)現(xiàn)語(yǔ)言/解釋器的屏障是多么脆弱。
>>> from types import FunctionType
Python 文檔通常不會(huì)列出那些非用于手動(dòng)創(chuàng)建的類(lèi)的特征(這是完全合理的)。有三種方法可以解決這個(gè)問(wèn)題:help()、inspect(無(wú)法查看內(nèi)置方法)、以及最后的解決方案,即查看 CPython 源代碼。
在本例中,help() 與 inspect 都可以完成工作,但是查看實(shí)際的源代碼,則會(huì)揭示出關(guān)于數(shù)據(jù)類(lèi)型的更多細(xì)節(jié)。
>>> from inspect import signature >>> signature(FunctionType) <Signature (code, globals, name=None, argdefs=None, closure=None)>
1. code
內(nèi)部是一個(gè)PyCodeobject,作為types.CodeType對(duì)外開(kāi)放。非內(nèi)置方法擁有一個(gè)__code__屬性,該屬性保存了相應(yīng)的代碼對(duì)象。利用內(nèi)置 compile() 方法,可以在運(yùn)行期創(chuàng)建types.CodeType對(duì)象。
2. globals
如果一個(gè)函數(shù)引用的變量不是在局部定義的,而是作為參數(shù)轉(zhuǎn)入、由默認(rèn)參數(shù)值提供、或者通過(guò)閉包上下文提供,則它會(huì)在 globals 字典中查找。
內(nèi)置的 globals() 方法會(huì)返回一個(gè)對(duì)當(dāng)前模塊的全局符號(hào)表(global symbol table)的引用 ,因此能被用來(lái)提供一個(gè)總是與當(dāng)前表的狀態(tài)相一致的字典。傳入任意其它的字典也是可以的(FunctionType((lambda: bar).__code__, {"bar" : "baz"}, "foo")() == "baz")。
3. name(可選)
控制所返回的函數(shù)的__name__ 屬性。只真正對(duì) lambdas 有用(由于匿名性,它們通常沒(méi)有名稱(chēng)),并且重命名函數(shù)。
4. argdefs(可選)
通過(guò)傳入一個(gè)包含任意類(lèi)型的對(duì)象的元組,提供一個(gè)方式來(lái)供應(yīng)默認(rèn)參數(shù)值(def foo(bar="baz"))。(FunctionType((lambda bar: bar).__code__, {}, "foo", (10,))() == 10)。
5. closure(可選)
(如果需要在 CPython(PyPy,Jython,…)以外的其它 Python VM 中執(zhí)行,可能不應(yīng)該觸及,因?yàn)樗鼑?yán)重地依賴(lài)于實(shí)現(xiàn)細(xì)節(jié))。
一個(gè)cell 對(duì)象的元組。創(chuàng)建 cell 對(duì)象并非完全是直截了當(dāng)?shù)?,因?yàn)樾枰{(diào)用 CPython 的內(nèi)部組件,但有一個(gè)庫(kù)可以令它更加方便:exalt(無(wú)恥的廣告)。(譯注:這個(gè)庫(kù)是作者開(kāi)發(fā)的。)
>>> foo_code = compile('def foo(): return "bar"', "<string>", "exec")
compile() 是一個(gè)內(nèi)置方法,因此同時(shí)也是文檔豐富的。
exec 模式被用到,因?yàn)槎x函數(shù)需用多個(gè)語(yǔ)句。
>>> foo_func = FunctionType(foo_code.co_consts[0], globals(), "foo")
聚合全部?jī)?nèi)容,并將動(dòng)態(tài)創(chuàng)建的函數(shù)指定給一個(gè)變量。
那個(gè)被前一句代碼編譯成的函數(shù),成為了生成的代碼對(duì)象的第一個(gè)常量,因此僅僅指向 foo_code 是不充分的。這是 exec 模式的直接后果,因?yàn)樯傻拇a對(duì)象可以包含多個(gè)常量。
>>> print(foo_func())
動(dòng)態(tài)生成的函數(shù)可以像其它函數(shù)一樣被調(diào)用。
結(jié)尾
除了做實(shí)驗(yàn),需要用到動(dòng)態(tài)創(chuàng)建函數(shù)的場(chǎng)景很少。
玩耍(Toying around) Python 的內(nèi)部構(gòu)件是一種深入學(xué)習(xí)這門(mén)語(yǔ)言的好方法。
如果需要,可以毫不費(fèi)力地越過(guò)解釋器/語(yǔ)言的界線(xiàn)。
還是一如既往地:不要濫用語(yǔ)言 (好吧,一點(diǎn)點(diǎn)也無(wú)妨,對(duì)吧?)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
在樹(shù)莓派2或樹(shù)莓派B+上安裝Python和OpenCV的教程
這篇文章主要介紹了在樹(shù)莓派2或樹(shù)莓派B+上安裝Python和OpenCV的教程,主要基于GTK庫(kù),并以Python2.7和OpenCV 2.4.X版本的安裝作為示例,需要的朋友可以參考下2015-03-03
Python制作簡(jiǎn)單的網(wǎng)頁(yè)爬蟲(chóng)
自己寫(xiě)的一個(gè)爬蟲(chóng),模仿了python核心編程書(shū)里的程序,有詳細(xì)的注釋。 是我一個(gè)理解學(xué)習(xí)的過(guò)程吧。 有需要的小伙伴可以參考下2015-11-11
Python基于pygame實(shí)現(xiàn)的彈力球效果(附源碼)
這篇文章主要介紹了Python基于pygame實(shí)現(xiàn)的彈力球效果,涉及pygame圖形動(dòng)態(tài)操作的相關(guān)的技巧,并附帶了完整的源碼供讀者下載參考,需要的朋友可以參考下2015-11-11
Python任務(wù)自動(dòng)化工具tox使用教程
這篇文章主要介紹了Python任務(wù)自動(dòng)化工具tox使用教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
python直接訪(fǎng)問(wèn)私有屬性的簡(jiǎn)單方法
下面小編就為大家?guī)?lái)一篇python直接訪(fǎng)問(wèn)私有屬性的簡(jiǎn)單方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07
python網(wǎng)絡(luò)編程之TCP通信實(shí)例和socketserver框架使用例子
這篇文章主要介紹了python網(wǎng)絡(luò)編程之TCP通信實(shí)例和socketserver框架使用例子,需要的朋友可以參考下2014-04-04

