基于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
突然被视为必需参数?
如果要保留默认参数值,还需要传递它们:
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__
。
这是因为你没有正确复制这个功能。如果你看看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__
)