使用“重载”方法设计回调在Python中
我设计了一个验证 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 而言,始终使用三参数变体将是最干净的方法。你怎么认为?
I design a validation API where call-backs are used to check values. There a two variants of callback signatures:
def check(self, value):
pass
def check(self, value, domain_object):
pass
Example for calling the callback implementations:
for constraint in constraints:
constraint.check(value)
# or constraint.check(value, domain_object) depending on the implementation
For now I count the number of arguments reflectively before the method is called and depending on the result I pass one ore two parameters to it. But is this good style?
Would it be better to
- always use the signature with three arguments:
check(self, value, domain_object)
or - use a different name like
check_with_domain_object
for the second case?
I think in terms of oop it would be the cleanest way to always use the three argument variant. What do you think?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
最惯用的方法是首先尝试使用两个参数,如果失败,则尝试使用一个:
The most idiomatic way would be to first try with two arguments, and if it fails, try with one:
我喜欢 @Space_C0wb0y 的答案,它类似于 Raymond Hettinger 发送给我的代码,用于解决 pyparsing 中的类似情况(见下文)。对于简单的情况,尝试使用此规范化器类来包装给定的回调:
您的代码现在可以将回调包装在
_ArityNormalizer
中,并且在回调时,始终使用 2 个参数进行调用。_ArityNormalizer
只会执行一次试错“使用 2 个参数进行调用,如果失败则使用 1 个参数进行调用”逻辑,从那时起将直接转到正确的形式。在 pyparsing 中,我想支持可能定义为采用 0、1、2 或 3 个参数的回调,并编写代码,根据回调函数的签名,用几个装饰器之一包装被调用的函数。这样,在运行/回调时,我总是使用 3 个参数进行调用,而装饰器负责使用正确数量的参数进行实际调用。
我的代码做了很多脆弱/不可移植/版本敏感的签名自省来做到这一点(听起来像OP当前正在做的事情),直到Raymond Hettinger给我发送了一个很好的数量修剪方法,该方法基本上完成了@Space_C0wb0y的答案建议的内容。 RH 的代码使用了一些非常简洁的装饰器,用一个非局部变量包装来记录成功调用的数量,这样你只需要经历一次试错,而不是每次调用回调时。您可以在 SourceForge 上的 pyparsing SVN 存储库中的函数
_trim_arity
中查看他的代码 - 请注意,由于使用了“nonlocal”关键字,他的代码具有 Py2/Py3 变体。上面的
_ArityNormalizer
代码是受到RH代码的启发,在我完全理解他代码的魔力之前。I like @Space_C0wb0y's answer, it is similar to code Raymond Hettinger sent to me to address a similar situation in pyparsing (see below). For your simple case, try using this normalizer class to wrap the given callbacks:
Your code can now wrap the callbacks in an
_ArityNormalizer
, and at callback time, always call with 2 arguments._ArityNormalizer
will do the trial-and-error "call with 2 args and if that fails call with 1 arg" logic only once, and from then on will go directly to the correct form.In pyparsing, I wanted to support callbacks that may be defined to take 0, 1, 2, or 3 arguments, and wrote code that would wrap the called function with one of several decorators depending on what the callback function's signature was. This way, at run/callback time I'd just always call with 3 arguments, and the decorator took care of making the actual call with the correct number of args.
My code did a lot of fragile/non-portable/version-sensitive signature introspection to do this (sounds like what the OP is currently doing), until Raymond Hettinger sent me a nice arity-trimming method that does essentially what @Space_C0wb0y's answer proposes. RH's code used some very neat decorator wrapping with a nonlocal variable to record the arity of the successful call, so that you only have to go through the trial-and-error once, instead of every time you call the callback. You can see his code in the pyparsing SVN repository on SourceForge, in the function
_trim_arity
- note that his code has Py2/Py3 variants, due to the use of the "nonlocal" keyword.The
_ArityNormalizer
code above was inspired by RH's code, before I fully understood his code's magic.“Space_C0wb0y”使用
try ... except TypError
的想法看起来不错,但我不喜欢这样的事实,因为这可能会吞掉其他异常。 “Paul McGuire”对_ArityNormalizer
的建议本质上与良好的界面相同。最后,我决定让事情尽可能简单和面向对象,并且始终使用两个参数,即使在某些情况下第二个参数将不被使用:
实现方:
调用方:
"Space_C0wb0y"'s idea to use
try ... except TypError
looks nice, but I don't like the fact that this could swallow other exceptions. "Paul McGuire"'s suggestion with the_ArityNormalizer
is essentially the same with a nice interface.In the end I decided to keep things as simple and object oriented as possible and go always with two paramters even if there would be several cases in which the second parameter will be unused:
Implementation side:
Calling side: