1 回答

TA貢獻(xiàn)1893條經(jīng)驗(yàn) 獲得超10個(gè)贊
問題
你是對的,這不起作用的原因是由于范圍問題。您可以通過檢查以下文檔eval來弄清楚發(fā)生了什么:
評估(表達(dá)式,全局=無,本地=無)
...如果兩個(gè)字典 [即全局變量和局部變量] 都被省略,則表達(dá)式在調(diào)用 eval() 的環(huán)境中執(zhí)行。
因此,可以合理地假設(shè)您遇到的問題歸結(jié)為被調(diào)用的內(nèi)容globals和locals上下文(即在 的定義(和可能的單獨(dú)模塊)內(nèi)ClassTest)eval。由于eval被調(diào)用的上下文通常不是您定義和/或?qū)氲纳舷挛?,因此就相關(guān)而言user_func, user_func2....,這些函數(shù)是未定義的eval。這一思路得到以下文檔的支持globals:
全局變量()
...這始終是當(dāng)前模塊的字典(在函數(shù)或方法中,這是定義它的模塊,而不是調(diào)用它的模塊)。
修復(fù)
對于如何修復(fù)此代碼,您有幾種不同的選擇。所有這些都將涉及l(fā)ocals從您調(diào)用的上下文傳遞,例如,ClassTest.registerClassFunc傳遞到定義該方法的上下文。此外,您應(yīng)該借此機(jī)會(huì)eval從您的代碼中排除使用(它的使用被認(rèn)為是不好的做法,它是一個(gè)巨大的安全漏洞,yadda yadda yadda)。鑒于locals這user_func是定義的范圍的字典,您可以隨時(shí)執(zhí)行以下操作:
locals['user_func']
代替:
eval('user_func')
修復(fù) #1
鏈接到此修復(fù)程序的實(shí)時(shí)版本
這將是最容易實(shí)現(xiàn)的修復(fù),因?yàn)樗恍枰獙?的方法定義進(jìn)行一些調(diào)整ClassTest(并且不需要更改任何方法簽名)。它依賴于可以inspect在函數(shù)中使用包直接獲取locals調(diào)用上下文的事實(shí):
import inspect
def dictsGet(s, *ds):
for d in ds:
if s in d:
return d[s]
# if s is not found in any of the dicts d, treat it as an undefined symbol
raise NameError("name %s is not defined" % s)
class ClassTest():
testFunc = {}
def registerClassFunc(self, funcName):
_frame = inspect.currentframe()
try:
_locals = _frame.f_back.f_locals
finally:
del _frame
ClassTest.testFunc[funcName] = dictsGet(funcName, _locals, locals(), globals())
@classmethod
def registerClassFuncOnClass(cls, funcName):
_frame = inspect.currentframe()
try:
_locals = _frame.f_back.f_locals
finally:
del _frame
cls.testFunc[funcName] = dictsGet(funcName, _locals, locals(), globals())
@staticmethod
def registerClassFuncFromStatic(funcName):
_frame = inspect.currentframe()
try:
_locals = _frame.f_back.f_locals
finally:
del _frame
ClassTest.testFunc[funcName] = dictsGet(funcName, _locals, locals(), globals())
如果您使用上面給出的 定義ClassTest,您編寫的導(dǎo)入測試現(xiàn)在將按預(yù)期運(yùn)行。
優(yōu)點(diǎn)
完全提供最初預(yù)期的功能。
不涉及對函數(shù)簽名的更改。
缺點(diǎn)
調(diào)用inspect.currentframe()可能會(huì)導(dǎo)致性能下降,因此如果您計(jì)劃ClassTest每秒調(diào)用一百萬次方法,則可能無法使用此修復(fù)程序。
inspect.currentframe()只能保證在 CPython 上工作。將此代碼與 Python 的其他實(shí)現(xiàn)一起運(yùn)行時(shí),里程可能會(huì)有所不同。
修復(fù) #2
Fix #2 與 fix #1 基本相同,除了在此版本中您在調(diào)用點(diǎn)顯式傳遞locals到 的方法ClassTest。例如,在此修復(fù)下, 的定義ClassTest.registerClassFunc將是:
def registerClassFunc(self, funcName, _locals):
ClassTest.testFunc[funcName] = dictsGet(funcName, _locals, locals(), globals())
你會(huì)像這樣在你的代碼中調(diào)用它:
a = ClassTest()
a.registerClassFunc("user_func5", locals())
優(yōu)點(diǎn)
不依賴于inspect.currentframe(),因此可能比修復(fù) #1 更具性能/便攜性。
缺點(diǎn)
您必須修改方法簽名,因此您還必須更改使用這些方法的任何現(xiàn)有代碼。
從這里開始,您必須將locals()樣板添加到每個(gè)ClassTest方法的每次調(diào)用中。
添加回答
舉報(bào)