基于Disable global variable lookup in Python(和我自己的回答),使用带有可选参数的函数时遇到了问题,例如以下最小示例:
import types
def noglobal(f):
return types.FunctionType(f.__code__, {})
@noglobal
def myFunction(x=0):
pass
myFunction()
本质上,它会像这样失败:
Traceback (most recent call last):
File "SetTagValue.py", line 10, in <module>
myFunction()
TypeError: myFunction() missing 1 required positional argument: 'x'
为什么x
突然被视为必需参数?
答案 0 :(得分:3)
这是因为您没有正确复制该功能。如果看一下types.FunctionType
的签名,您会发现它接受5个参数:
class function(object)
| function(code, globals, name=None, argdefs=None, closure=None)
|
| Create a function object.
|
| code
| a code object
| globals
| the globals dictionary
| name
| a string that overrides the name from the code object
| argdefs
| a tuple that specifies the default argument values
| closure
| a tuple that supplies the bindings for free variables
您没有传递任何argdefs
,因此该函数不再具有可选参数。复制功能的正确方法是
types.FunctionType(f.__code__,
{},
f.__name__,
f.__defaults__,
f.__closure__
)
但是,这导致了另一个问题:切断对全局变量的访问也切断了对内置函数的访问。如果您尝试使用print
或open
或dict
或myFunction
中的类似内容,则会得到NameError
。因此,真正编写装饰器的正确方法是:
import builtins
import types
def noglobal(f):
return types.FunctionType(f.__code__,
{'__builtins__': builtins},
f.__name__,
f.__defaults__,
f.__closure__
)
答案 1 :(得分:2)
如果要保留默认参数值,还需要传递它们:
import types
def noglobal(f):
return types.FunctionType(f.__code__, {}, f.__name__, f.__defaults__)
@noglobal
def myFunction(x=0):
pass
myFunction()
您可以将最后一个closure
参数传递给types.FunctionType
,如果您想使函数保持闭包状态,您可能还希望从f.__closure__
继承它。