如何在Python库函数的eval()中调用外部定义的函数?
问题:adv_prod函数无法识别外部自定义函数
问题重现
我开发的PyPI库中有一个使用eval()的adv_prod函数:
def adv_prod(start,end,rule): ending_seq = list_prod([eval(rule) for i in range(start,end+1)]) return ending_seq
当传入库内定义的函数(如同一文件中的prime(i))时可以正常运行:
from supMath import adv_prod,prime for x in range(1,11): print(adv_prod(1,x,"prime(i)"))
输出:
2 6 30 210 2310 30030 510510 9699690 223092870 6469693230
但传入外部自定义函数时会触发NameError:
from supMath import adv_prod def fib(n): if n==1: return 1 if n==2: return 1 return fib(n-2)+fib(n-1) for x in range(1,11): print(adv_prod(1,x,"fib(i)"))
输出:
NameError: name 'fib' is not defined
原因分析
eval()默认在adv_prod函数的局部作用域中执行代码,外部自定义函数fib不在这个作用域内,所以无法被识别。而库内的prime函数属于adv_prod所在模块的全局作用域,因此可以被找到。
解决方案
方案1:传递函数对象(推荐,安全且简洁)
修改adv_prod,让它直接接收函数对象而非字符串规则,彻底避免eval()的安全风险和作用域问题:
def adv_prod(start, end, rule_func): # 直接调用传入的函数,传入i作为参数 ending_seq = list_prod([rule_func(i) for i in range(start, end+1)]) return ending_seq
调用方式改为直接传入函数名:
from supMath import adv_prod def fib(n): if n==1: return 1 if n==2: return 1 return fib(n-2)+fib(n-1) for x in range(1,11): print(adv_prod(1,x,fib)) # 直接传fib函数对象
方案2:传递作用域给eval(不推荐,存在安全风险)
如果必须保留字符串规则的调用方式,可以让eval()访问调用者的作用域,以下是两种实现方式:
方式A:显式传递作用域参数
修改adv_prod,允许传入调用者的全局/局部变量:
def adv_prod(start, end, rule, globals=None, locals=None): ending_seq = list_prod([eval(rule, globals, locals) for i in range(start, end+1)]) return ending_seq
调用时传入当前作用域:
from supMath import adv_prod def fib(n): if n==1: return 1 if n==2: return 1 return fib(n-2)+fib(n-1) for x in range(1,11): print(adv_prod(1,x,"fib(i)", globals(), locals()))
方式B:自动获取调用者作用域(依赖inspect)
使用inspect模块获取调用者的栈帧,从而拿到其作用域:
import inspect def adv_prod(start, end, rule): # 获取调用者的栈帧 caller_frame = inspect.currentframe().f_back try: # 使用调用者的全局和局部作用域执行eval ending_seq = list_prod([eval(rule, caller_frame.f_globals, caller_frame.f_locals) for i in range(start, end+1)]) finally: # 手动删除栈帧引用,避免内存泄漏 del caller_frame return ending_seq
调用方式和原来一致:
from supMath import adv_prod def fib(n): if n==1: return 1 if n==2: return 1 return fib(n-2)+fib(n-1) for x in range(1,11): print(adv_prod(1,x,"fib(i)"))
注意:这种方式依赖Python的栈帧实现,可能在某些优化环境中失效,且eval()执行外部传入的字符串存在代码注入风险,不建议在公开库中使用。
内容的提问来源于stack exchange,提问作者landee
相关产品推荐
相关产品推荐

