Python通过装饰器将参数传递给函数

时间:2020-08-10 10:52:36

标签: python architecture

我正在构建一个类,该类具有一个方法,该方法带有一个在该方法内调用的用户定义函数。该函数可以接受该函数必需的一组预定义参数中的参数。声明函数时的“常规”做法是将所有参数设置为关键字参数,默认值为None,并仅使用所需的参数。但是,我正在寻找一种更优雅的解决方案,该解决方案不需要明确列出所有参数。

因此,我在类中编写了一些装饰器函数,用户在声明该函数以通知类需要传递参数时可以使用这些装饰器函数(请注意,下面的代码纯粹是示例,用于显示概念)

class MyClass:
    def __init__(self):
        self.a = None
        self.b = None
    
    def use_a(self, func):
        def wrapper(*args, **kwargs):
            kwargs.update({"a": self.a})
            return func(*args, **kwargs)
        return wrapper

    def use_b(self, func):
        def wrapper(*args, **kwargs):
            kwargs.update({"b": self.b})
            return func(*args, **kwargs)
        return wrapper

    def run(self, func):
        for x in range(10):
            self.a = x
            self.b = x * 10
            func()


g = MyClass()

@g.use_a
@g.use_b
def my_custom_function(a, b):
    print(a, b)

myfunc = my_custom_function
g.run(myfunc)

我认为的唯一问题是,必须在类初始化之后声明用户定义的函数,如果我想在另一个模块中定义函数,这将成为一个问题。因此,为了缓解这种情况,我决定创建一个工厂函数,该函数接受一个实例作为其参数,并返回修饰后的函数:

# defined in another module
def external_function(instance):
    @instance.use_a
    @instance.use_b
    def custom_func_b(a, b):
        print(a, b)
    return custom_func_b

from mymodule import external_function
g = MyClass()
myfunc = external_function(g)
g.run(myfunc)
  1. 请注意,外部定义的函数将传递其返回值,与此同时,在类初始化范围内声明的函数将传递函数本身。这有可能引起混淆。
  2. 如您所见,使用修饰符传递参数似乎不是最佳设计,因为不建议在类范围内使用修饰符。我想知道是否值得继续使用这种设计,而不是接受默认值设置为None的所有参数,或者存在其他更适合我的情况的方法。

1 个答案:

答案 0 :(得分:0)

据我了解,您有一些函数可以接受一些参数,并且您希望有一些可以代表您内联的参数,这样您就不必每次都明确地传递它们。

如果是这种情况,您就是在重塑部分函数应用程序,这种方法(过于简化)归结为创建了一个函数B,该函数将调用函数A并传递一些已经传入的A参数。创建自己的partial实现或使用functools模块。在代码中:

# The base function
def base_function(a, b, c=None):
    print(a, b, c)

# The function that implements partial application
def my_partial(func, *args, **kwargs):
    positional_args = args
    keyword_args = kwargs
    def wrapped(*args, **kwargs):
        all_pos_args = positional_args + args
        all_kw_args = {**keyword_args, **kwargs}
        return func(*all_pos_args, **all_kw_args)
    return wrapped

# Usage
new_func = my_partial(base_function, 1, c=3)
new_func(2) # Outputs: 1 2 3

# Now using functools partial
from functools import partial
new_func2 = partial(base_function, 1, c=3)
new_func2(2) # Again: 1 2 3

出于两个原因,我通常会选择部分解决方案而不是更复杂的解决方案。首先是您的类装饰器的状态隐藏了调用装饰后的函数可能导致错误的结果,并且必须在使用装饰器之前初始化该类。第二个是,显式传递关键字参数使代码意图更清晰。

作为接收参数的旁注装饰器也有其位置,例如,您可以检查Flask服务器库如何使用@app.route("/my/route")装饰器实现URL路由。