Python 方法链接时保留参数默认值
如果我必须包装一个现有的方法,比如说包装器()来自一个新方法,并且包装器()为一些参数提供了默认值,那么如何在不引入不必要的依赖关系和维护的情况下保留其语义呢?比方说,目标是能够使用wrapper()代替wrapee(),而无需更改客户端代码。例如,如果wrapee()定义为:Python 方法链接时保留参数默认值,python,Python,如果我必须包装一个现有的方法,比如说包装器()来自一个新方法,并且包装器()为一些参数提供了默认值,那么如何在不引入不必要的依赖关系和维护的情况下保留其语义呢?比方说,目标是能够使用wrapper()代替wrapee(),而无需更改客户端代码。例如,如果wrapee()定义为: def wrapee(param1, param2="Some Value"): # Do something 然后,定义wrapper()的一种方法是: 但是,wrapper()必须对param2的默认值进行假设
def wrapee(param1, param2="Some Value"):
# Do something
然后,定义wrapper()的一种方法是:
但是,wrapper()必须对param2的默认值进行假设,这是我不喜欢的。如果我有wrapee()上的控件,我会这样定义它:
def wrapee(param1, param2=None):
param2 = param2 or "Some Value"
# Do something
def wrapper(param1, *args, **argv):
# Do something
wrapee(param1, *args, **argv)
# Do something else.
然后,wrapper()将更改为:
def wrapper(param1, param2=None):
# Do something
wrapee(param1, param2)
# Do something else.
如果我无法控制wrappe()的定义方式,那么如何最好地定义wrapper()?想到的一个选项是使用非None参数创建dict,并将其作为字典参数传递,但它似乎不必要地单调乏味
更新:
解决方案是同时使用列表和字典参数,如下所示:
def wrapee(param1, param2=None):
param2 = param2 or "Some Value"
# Do something
def wrapper(param1, *args, **argv):
# Do something
wrapee(param1, *args, **argv)
# Do something else.
然后,以下所有调用均有效:
wrapper('test1')
wrapper('test1', 'test2')
wrapper('test1', param2='test2')
wrapper(param2='test2', param1='test1')
查看Python文档
>>> def wrapper(param1, *stuff, **kargs):
... print(param1)
... print(stuff)
... print(args)
...
>>> wrapper(3, 4, 5, foo=2)
3
(4, 5)
{'foo': 2}
然后要传递参数,请执行以下操作:
wrapee(param1, *stuff, **kargs)
*stuff
是数量可变的非命名参数,**kargs
是数量可变的命名参数。这就是您想要的吗
def wrapper(param1, param2=None):
if param2:
wrapee(param1, param2)
else:
wrapee(param1)
#!/usr/bin/python
from functools import wraps
def my_decorator(f):
@wraps(f)
def wrapper(*args, **kwds):
print 'Calling decorated function'
return f(*args, **kwds)
return wrapper
def f1(x, y):
print x, y
def f2(x, y="ok"):
print x, y
my_decorator(f1)(1,2)
my_decorator(f2)(1,2)
my_decorator(f2)(1)
根据改编,我很难说这并不乏味,但我能想到的唯一方法是反思正在包装的函数,以确定其任何参数是否具有默认值。您可以获取参数列表,然后确定哪个参数是第一个具有默认值的参数:
from inspect import getargspec
method_signature = getargspec(method)
param_names = method_signature[0]
default_values = method_signature[3]
params = []
# If any of method's parameters has default values, we need
# to know the index of the first one that does.
param_with_default_loc = -1
if default_values is not None and len(default_values) > 0:
param_slice_index = len(default_values) * -1
param_with_default = param_names[param_slice_index:][0]
param_with_default_loc = param_names.index(param_with_default)
此时,您可以迭代参数名,复制到传递给wrappee的dict中。一旦您的索引>=param_with_default_loc,您就可以通过在default_values列表中查找索引-param_with_default_loc的索引来获得默认值
这有什么意义吗
当然,要使其通用,您需要将其定义为包装函数,添加另一层包装。这不会使新方法完全兼容。虽然我可以说wrappe(param2=“value”,param1),但我不能对wrapper()做同样的操作。如果将wrapper()定义为
def wrapper(**args)
,则args
将是提供的所有命名参数的字典。这对假定param2位置的调用方不起作用,例如wrappe(param1,param2)(它们被强制使用命名参数)。wrappee(param2=“value”,param1)
甚至无效(在kwarg之后是非kwarg)。你能提供一个你所说的例子吗?事实上,这应该是可行的,我没有看到你同时使用*和**形式。我在第一条评论中也犯了一个错误,你必须传递第二个参数,也命名为,否则它会变成语法错误。虽然这对于这个特定的例子是可行的,但我正在寻找一个泛型解决方案(例如,如果有几个默认参数怎么办?)。非常有用的方法内省,谢谢分享。