我设计了一个验证API,其中回调用于检查值。回调签名有两种变体:
def check(self, value):
pass
def check(self, value, domain_object):
pass
调用回调实现的示例:
for constraint in constraints:
constraint.check(value)
# or constraint.check(value, domain_object) depending on the implementation
现在我在调用方法之前反复计算参数的数量,并根据结果向其传递一两个参数。但这是好风格吗?
会更好吗?
check(self, value, domain_object)
或check_with_domain_object
?我认为就oop来说,这将是最常用的三种参数变体。你觉得怎么样?
答案 0 :(得分:2)
最常用的方法是首先尝试使用两个参数,如果失败,请尝试使用一个:
try:
callback(value_param, domain_object_param)
except TypeError:
callback(value_param)
答案 1 :(得分:2)
我喜欢@ Space_C0wb0y的答案,它类似于代码Raymond Hettinger发送给我来解决类似情况的pyparsing(见下文)。对于您的简单情况,请尝试使用此normalizer类来包装给定的回调:
class _ArityNormalizer(object):
def __init__(self, fn):
self.baseFn = fn
self.wrapper = None
def __call__(self, value, domain_object):
if self.wrapper is None:
try:
self.wrapper = self.baseFn
return self.baseFn(value, domain_object)
except TypeError:
self.wrapper = lambda v,d: self.baseFn(v)
return self.baseFn(value)
else:
return self.wrapper(value, domain_object)
您的代码现在可以将回调包装在_ArityNormalizer
中,并且在回调时,始终使用2个参数调用。 _ArityNormalizer
将进行反复试验“使用2个args调用,如果失败则调用1个arg”逻辑只执行一次,从那时起将直接转到正确的形式。
在pyparsing中,我想支持可以定义为0,1,2或3个参数的回调,并编写代码,根据回调函数的签名,用几个装饰器中的一个包装被调用的函数。这样,在运行/回调时,我总是用3个参数调用,而装饰器负责用正确数量的args进行实际调用。
我的代码做了很多脆弱/非便携/版本敏感的签名内省来做这件事(听起来就像OP目前正在做的那样),直到Raymond Hettinger发给我一个很好的arity-trimming方法,基本上做了什么@ Space_C0wb0y的答案提出。 RH的代码使用一些非常整洁的装饰器包装和非局部变量来记录成功调用的arity,这样你只需要经历一次试错,而不是每次调用回调。您可以在函数_trim_arity
中的SourceForge上的pyparsing SVN存储库中查看他的代码 - 请注意,由于使用了“nonlocal”关键字,他的代码具有Py2 / Py3变体。
上面的_ArityNormalizer
代码的灵感来自RH的代码,在我完全理解他的代码的魔力之前。
答案 2 :(得分:0)
“Space_C0wb0y”使用try ... except TypError
的想法看起来不错,但我不喜欢这样可以吞下其他异常的事实。 “Paul McGuire”对_ArityNormalizer
的建议基本上与一个不错的界面相同。
最后,我决定尽可能保持简单和面向对象,并且总是有两个参数,即使有几种情况下第二个参数将被闲置:
实施方:
def check(self, value, domain_object):
pass
致电方:
constraint.check(value, domain_object)