python - Python通过装饰器将参数传递给函数
问题描述
我正在构建一个类,该类具有一个方法,该方法接受要在该方法中调用的用户定义函数。该函数可以接受来自该函数所需的一组预定义参数的参数。声明函数时的“正常”做法是将所有参数设置为关键字参数,默认值为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)
- 请注意,外部定义的函数会传递其返回值,同时在类初始化范围内声明的函数也会传递函数本身。这有可能令人困惑。
- 如您所见,使用装饰器传递参数似乎不是最佳设计,因为不建议在类作用域中使用装饰器。我想知道是否值得继续使用这种设计,而不是接受所有默认值设置为的参数
None
,或者存在更适合我的情况的其他方法。
解决方案
据我了解,您有一些函数可以接受一组参数,并且您希望有一些东西可以代表您内联它们,这样您就不需要每次都明确地传递它们。
如果是这种情况,您就是在重新发明部分函数应用程序,这(过于简单化)归结为创建一个函数 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 路由。
推荐阅读
- vue.js - vue-cli-service:在构建项目时选择要包含/排除的文件夹
- javascript - 如何过滤(搜索)R DT(DataTable)中的格式化列
- linux - (已关闭)apt-get 更新错误(sury.org)[linux]
- react-styleguidist - react-styleguidist 可以支持导出markdown吗
- python - 如何在 AWS Lambda 函数中包含库的子目录
- spring - 动态获取 CDI bean,而不是通过注入
- azure - Cassandra 远程连接在 Ubuntu 上不起作用
- python - 以编程方式搜索 pypi 包(理想情况下按相关性排序)
- kdb - 完全限定的上下文名称(两个点)
- java - 如何避免在 MVC 架构中使用 instanceof