如何在Python中使用装饰器向函数传递参数

3

我有一组用Python编写的函数,这些函数共同接收两个参数以及其他参数。

def myMethodA (param1, param2, specificParm)
    do code

 def myMethodB (param1, param2, specificParm1 specificParam2)
    do code

我想创建一个装饰器,以代替调用函数时需要使用前两个参数的需求,通过在调用函数之前推送参数来实现:

@withParams()
def myMethodA (specificParam)
        do code

但是如果我省略这些参数,装饰器就不能调用它了,如果我保留它们,那么调用者也需要指定它们。有没有什么方法可以解决这个问题? 我可以在args*中做一些事情,但仍然要为specificParam指定命名参数吗?另外,如何在myMethodA内引用param1和param2?

也可以看一下 functools.partial - Markus Meskanen
4个回答

1

看起来你可能需要使用functools.partial。它会返回一个已经指定了一些参数的新函数:

import functools

def withParams(func):
    return functools.partial(func,1,2)

@withParams
def myMethodA (param1, param2, specificParam):
    print(param1,param2,specificParam)

@withParams
def myMethodB (param1, param2, specificParam1, specificParam2):
    print(param1,param2,specificParam1, specificParam2)

myMethodA(10)
myMethodB(12,13)
1 2 10
1 2 12 13

这看起来很不错,但如果签名是myMethodA(param1,param2,specificParam),那么myMethodA(10)如何工作? - Eran Witkon
两个选项都可以解决问题,都可以使用。我选择了部分,因为语法更清晰。 - Eran Witkon
这就是装饰器的作用。将一个新的函数对象分配给函数名称。在这种情况下,新函数需要少两个参数,并使用提供的两个参数加上任何其他参数来调用原始函数。 - Mark Tolonen
@EranWitkon 无论如何,我认为Marktolonen的回答非常合适。+1 - Moses Koledoye

0

当您在装饰器的包装函数中调用函数时,可以将这两个参数作为kwargs的一部分添加。所有装饰的函数在调用之前都会自动将这两个参数作为关键字参数传递:

def withParams(func):
    def wrapper(*args, **kwargs):
        kwargs['param1'] = 1
        kwargs['param2'] = 2
        return func(*args, **kwargs)
    return wrapper

@withParams
def add(specificparam, *args, **kwargs):
      print(specificparam)  # 3
      print(args)           # (4,)
      print(kwargs)         # {'param2': 2, 'param1': 1}
      return specificparam + sum(args) + sum(kwargs.values())

print(add(3,4)) # 10

如果你从主函数调用中传递的是每个函数的具体参数,那么你可以省略args部分。


1
这正是我所期望的,但是装饰器不只是函数调用吗?如果给一个不希望得到两个参数的函数添加两个参数,会怎么样呢?我预计结果会是类似于“方法预期1个参数,但提供了3个”的提示。 - Eran Witkon
此外,这样做的话,我无法在myMethodA中引用param1和param2,因为它们没有被定义。 - Eran Witkon
是的,这很好,但我希望仍然可以使用具有明确名称的特定参数。def add(spesificParm1 spesificParam2, *args, **kwargs): - Eran Witkon
@EranWitkon 假设 param1param2 可以在 specificParam2 之后出现,但请记住您已经声明它们是位置参数,对吧?另一方面,您可以将 param1param2 定义为关键字参数。 - Moses Koledoye
1
@EranWitkon 如果您将这些要求更新到您的问题中,我可以改进答案来符合它们 :) - Moses Koledoye
显示剩余2条评论

0
我建议您采用老派的方式,只需使用functools.partial重新分配名称即可:
# module foo

def func1(common_1, common_2, specific_1):
    pass

def func2(common_1, common_2, specific_2, specific_3):
    pass

然后在其他地方(或者文件中的较低位置,如果您喜欢),您可以这样做:

import functools

import foo

common_args = (1, 'fred')
foo.func1 = functools.partial(foo.func1, *common_args)
foo.func2 = functools.partial(foo.func2, *common_args)

# ...

foo.func1('special')

foo.func2('equally special', 'but different')

0

听起来你需要使用functools.partial,来预先填充“常量”参数:

>>> from functools import partial

>>> def myMethodA(param1, param2, specificParm):
        print("myMethodA(", param1, param2, specificParm, ")")

>>>> def myMethodB (param1, param2, specificParm1, specificParam2):
        print("myMethodB(", param1, param2, specificParm1, specificParam2, ")")

>>> preMethodA = partial(myMethodA, "p1", "p2")
>>> preMethodB = partial(myMethodB, "p1", "p2")
>>> preMethodA(34)
myMethodA( p1 p2 34 )
>>> preMethodB(8, 9)
myMethodB( p1 p2 8 9 )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接