可选参数如何变成必需参数?

How can an optional parameter become required?

基于(以及我自己的回答),我在使用带有可选参数的函数时遇到了问题,例如在这个最小示例中:

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__
                   )

但是,这会导致另一个问题:切断对全局变量的访问也会切断对内置函数的访问。如果您尝试在 myFunction 中使用 printopendict 或类似的东西,您将得到 NameError。所以 真正 编写装饰器的正确方法是这样的:

import builtins
import types

def noglobal(f):
    return types.FunctionType(f.__code__,
                              {'__builtins__': builtins},
                              f.__name__,
                              f.__defaults__,
                              f.__closure__
                              )