Python装饰器参数

Python装饰器参数,python,Python,我熟悉python装饰程序,并对一般情况感到好奇 假设我有一个函数来生成一个人: def GeneratePerson(name, surname): return {"name": name, "surname": surname} 现在,让我们假设我想要一个装饰师添加一个年龄: def WithAge(fn): def AddAge(*args): person = fn(args[0], args[1]) person[age] = arg

我熟悉python装饰程序,并对一般情况感到好奇

假设我有一个函数来生成一个人:

def GeneratePerson(name, surname):
    return {"name": name, "surname": surname}
现在,让我们假设我想要一个装饰师添加一个年龄:

def WithAge(fn):
    def AddAge(*args):
        person = fn(args[0], args[1])
        person[age] = args[2]
        return person
    return AddAge
然后我会打电话给你

@AddAge
GeneratePerson("John", "Smith", 42)
然而,我发现这有点违反直觉。如果我查看“GeneratePerson”的签名,我会发现它只接受一个名称和一个姓氏参数。年龄(42岁)是装饰师要求的财产。我觉得语法(Java风格)如下:

可能更好

我很感激能够使用kwargs解决这个问题,将堆栈称为:

@WithAge
@AddGender
GeneratePerson(name="John", surname="Smith", age="42", gender="M")

但是有没有一种方法可以直接将参数传递给decorator函数(例如:
@Age(42)
而不是通过修饰函数间接传递参数?

注意:decorator是在定义时应用的,而不是在运行时应用的。这意味着当你可以写的时候是:

@AddAge
def GeneratePerson(name, lastname):
    ...
这足以回答您的最后一个问题:不可能将参数传递给装饰器而不是装饰函数,除非您希望装饰函数的所有调用都相同

但在Python 3中,可以向修饰函数添加一个参数:

def AddAge(param_name='age'):
    # define the new parameter
    param = inspect.Parameter(param_name,
                  inspect.Parameter.POSITIONAL_OR_KEYWORD)
    def wrapper(f):
        sig = inspect.signature(f)
        params = list(sig.parameters.values())
        params.append(param)                     # add the new parameter
        sig2 = sig.replace(parameters = params)  # to a new signature
        def wrapped(*args, **kwargs):
            bound = sig2.bind(*args, **kwargs)   # process the parameters
            args = bound.args                    #  both positional
            kwargs = bound.kwargs                #  and keywords
            if param_name in kwargs:             # extract the age
                age = kwargs[param_name]         #  either keyword
                del kwargs[param_name]
            else:
                args = list(args)                #  or positional
                age = args.pop()
            obj = f(*args, **kwargs)             # call the original function
            obj.age = age                        # add the attribute
            return obj
        wrapped.__signature__ = sig2             # attach the new signature
        return wrapped
    return wrapper
然后你可以这样使用它

>>> f = AddAge()(GeneratePerson)
>>> p = f('John', 'Smith', 42)
>>> p.name
'John'
>>> p.lastname
'Smith'
>>> p.age
42
它甚至可以在函数调用中支持关键字:

>>> p = f('John', age=42, lastname="Smith")

将给出预期的结果。唯一的限制是原始函数不应具有名为
age
的参数。如果有,您必须将不同的名称传递给装饰器。

装饰器不使用可能重复的名称。您不能向现有函数中添加参数。@Aran Fey我不是在向现有函数中添加参数,而是在向返回的objectWait中添加属性,所以所有已创建人员的年龄应该是42岁?它不应该是一个参数?我怀疑代码是否有效。在全局范围内没有
AddAge
。你是说年龄
@WithAge
?谢谢!具体地说,我考虑的更多的是应用程序dropwizard风格,其中Java方法被注释为@GET(“login”)、@POST(“comment”)等,即使两者都使用
@
,Java注释也没有相同的语义。。。
>>> p = f('John', age=42, lastname="Smith")