【发布时间】:2014-11-03 08:58:26
【问题描述】:
我有几个要提供默认参数的函数。由于这些默认值应该是新初始化的对象,我不能只将它们设置为参数列表中的默认值。它引入了很多笨拙的代码来将它们默认为 None 并检查每个函数
def FunctionWithDefaults(foo=None):
if foo is None:
foo = MakeNewObject()
为了减少重复代码,我做了一个装饰器来初始化参数。
@InitializeDefaults(MakeNewObject, 'foo')
def FunctionWithDefaults(foo=None):
# If foo wasn't passed in, it will now be equal to MakeNewObject()
我写的装饰器是这样的:
def InitializeDefaults(initializer, *parameters_to_initialize):
"""Returns a decorator that will initialize parameters that are uninitialized.
Args:
initializer: a function that will be called with no arguments to generate
the values for the parameters that need to be initialized.
*parameters_to_initialize: Each arg is the name of a parameter that
represents a user key.
"""
def Decorator(func):
def _InitializeDefaults(*args, **kwargs):
# This gets a list of the names of the variables in func. So, if the
# function being decorated takes two arguments, foo and bar,
# func_arg_names will be ['foo', 'bar']. This way, we can tell whether or
# not a parameter was passed in a positional argument.
func_arg_names = inspect.getargspec(func).args
num_args = len(args)
for parameter in parameters_to_initialize:
# Don't set the default if parameter was passed as a positional arg.
if num_args <= func_arg_names.index(parameter):
# Don't set the default if parameter was passed as a keyword arg.
if parameter not in kwargs or kwargs[parameter] is None:
kwargs[parameter] = initializer()
return func(*args, **kwargs)
return _InitializeDefaults
return Decorator
如果我只需要装饰一次,那效果很好,但它会因多次装饰而中断。具体来说,假设我想初始化 foo 和 bar 以分隔变量:
@InitializeDefaults(MakeNewObject, 'foo')
@InitializeDefaults(MakeOtherObject, 'bar')
def FunctionWithDefaults(foo=None, bar=None):
# foo should be MakeNewObject() and bar should be MakeOtherObject()
这会中断,因为在第二个装饰器中,func_arg_defaults 获取第一个装饰器的参数,而不是 FunctionWithDefaults。结果,我无法判断一个参数是否已经按位置传入。
有什么干净的方法可以解决这个问题吗?
不起作用的事情:
- functools.wraps(func) 使函数的
__name__成为修饰函数,但它不会更改参数(除非有不同的方法)。 - inspect.getcallargs 不会在第二个装饰器中引发异常
- 使用 func.func_code.co_varnames 不会在第二个装饰器中提供函数的参数
我可以只使用一个装饰器,但这似乎不太干净,感觉应该有一个更清洁的解决方案。
谢谢!
【问题讨论】:
标签: python python-2.7 python-decorators