我有几个函数要提供默认参数。由于这些默认值应该是新初始化的对象,因此我不能在参数列表中将它们设置为默认值。它引入了许多笨重的代码来将它们默认为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的参数。结果,我无法判断参数是否已经在位置上传递过。
有没有什么干净的方法来解决这个问题?
不起作用的事情:
__name__
成为装饰函数,但它不会更改参数(除非有不同的方法)。我可以使用一个装饰器,但这看起来不太干净,感觉应该有更清洁的解决方案。
谢谢!
答案 0 :(得分:2)
这可能是您在python中获得有关装饰器的最佳建议:使用wrapt.decorator
。
这将使编写装饰器的任务变得更加简单(您不需要在函数内部的函数内编写函数),并且可以避免在编写甚至简单的装饰器时所有人都犯的错误。要了解它们是什么,您可能需要阅读Graham Dumpleton的blog posts标题为&#34;您如何实现您的Python装饰器是错误的&#34; (他非常有说服力)
答案 1 :(得分:1)
您可以向包含argspec的包装函数添加变量(例如__argspec
):
def Decorator(func):
# Retrieve the original argspec
func_arg_names = getattr(func, '__argspec', inspect.getargspec(func).args)
def _InitializeDefaults(*args, **kwargs):
# yada yada
# Expose the argspec for wrapping decorators
_InitializeDefaults.__argspec = func_arg_names
return _InitializeDefaults
答案 2 :(得分:0)
为什么不更改装饰器以接受参数字典和您想要的默认值?我不明白为什么它会比具有不同参数的多个装饰更不干净。这是一个适合我的样本。我对您的代码进行了小的更改,以反映字典为参数: 导入检查
class Person:
def __init__(self):
self.name = 'Personality'
class Human(Person):
def __init__(self):
self.name = 'Human'
class Animal(Person):
def __init__(self):
self.name = 'Animal'
#def InitializeDefaults(**initializerDict):
def InitializeDefaults(**initializerDict):
"""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 initializerDict:
# 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] = initializerDict[parameter]()
return func(*args, **kwargs)
return _InitializeDefaults
return Decorator
#@InitializeDefaults(Human, 'foo')
@InitializeDefaults(foo = Human, bar = Animal)
def FunctionWithDefaults(foo=None, bar=None):
print foo.name
print bar.name
#test by calling with no arguments
FunctionWithDefaults()
#program output
Human
Animal