[英]How to use a decorator to check arguments
我有几个要提供默认参数的函数。 由于这些默认值应该是新初始化的对象,因此我不能仅在参数列表中将它们设置为默认值。 它引入了许多笨拙的代码,以将其默认设置为“无”并签入每个函数
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__
为装饰后的函数,但它不会更改参数(除非有其他方法可以使用)。 我可以只使用一个装饰器,但这看起来不太干净,感觉应该有一个更干净的解决方案。
谢谢!
这可能是关于python中的装饰器的最佳建议:使用wrapt.decorator
。
这将使编写装饰器的任务变得更加简单(您无需在函数内的函数内部编写函数),并且避免了编写简单装饰器时每个人都会犯的所有错误。 要了解这些内容,您可能需要阅读Graham Dumpleton的博客文章 “您如何实现Python装饰器是错误的”(他非常有说服力)
您可以向包含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
为什么不更改装饰器以接受参数字典和所需的默认值? 我不明白为什么它比具有不同参数的多个修饰要干净得多。 这是一个适合我的示例。 我对您的代码进行了较小的更改以将字典反映为参数:import inspect
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.