如果用Python键入duck,是否应该测试isinstance?
您有一个Python类,它需要一个equals测试。Python应该使用duck类型,但是在eq函数中包含或排除isinstance测试(更好/更准确)吗?例如:如果用Python键入duck,是否应该测试isinstance?,python,equality,duck-typing,Python,Equality,Duck Typing,您有一个Python类,它需要一个equals测试。Python应该使用duck类型,但是在eq函数中包含或排除isinstance测试(更好/更准确)吗?例如: class Trout(object): def __init__(self, value): self.value = value def __eq__(self, other): return isinstance(other, Trout) and self.value == o
class Trout(object):
def __init__(self, value):
self.value = value
def __eq__(self, other):
return isinstance(other, Trout) and self.value == other.value
在
方法中使用isinstance非常常见。其原因是,如果\uuuuuuuuuuuuuuuuuuuu
方法失败,它可以从另一个对象回退到\uuuuuuuueq
方法。大多数普通的方法都是显式调用的,但是\uuuuuuuuuuueq\uuuuuuu
是隐式调用的,所以需要三思而后行,才能更频繁地跳转
编辑(感谢提醒,Sven Marnach):
要使其回退,可以返回NotImplemented singleton,如下例所示:
class Trout(object):
def __init__(self, value):
self.value = value
def __eq__(self, other):
if isinstance(other, Trout):
return self.value == other.value
else:
return NotImplemented
假设一条彩虹鳟鱼
知道如何将自己与一条鳟鱼
或另一条彩虹鳟鱼
进行比较,但鳟鱼
只知道如何将自己与一条鳟鱼
进行比较。在本例中,如果您测试mytrout==myrainbowtrout
,Python将首先调用mytrout.\uuuueq\uuuuu(myrainbowtrout)
,请注意它失败了,然后调用myrainbowtrout.\uueq\uuuuuuuuu(mytrout)
,成功了。“duck-typing”原则是您不在乎其他是什么,只要它具有值
属性。因此,除非您的属性共享名称且语义冲突,否则我建议您这样做:
def __eq__(self, other):
try:
return self.value == other.value
except AttributeError:
return False # or whatever
(或者,您可以测试other
是否具有value
属性,但“请求原谅比获得许可更容易”)使用isintsance()。如果isinstance()
检查失败,则不应立即返回False
,但最好返回NotImplemented
以提供其他。
执行的机会:
def __eq__(self, other):
if isinstance(other, Trout):
return self.x == other.x
return NotImplemented
这在多个类定义\uuuu eq\uuu()
的类层次结构中尤为重要:
如果您像在原始代码中那样立即返回False
,您将在A(3)==B(3,4)
和B(3,4)==A(3)
之间失去对称性。我认为这是isinstance
的合法用例。这可能会导致一些非常愚蠢的事情,比如鳟鱼(“彩虹”)==Lorikeet(“彩虹”)虽然几乎没有人会认为鱼和鸟一样。“令牌,那样的话,鸭子打字对你来说就不合适了。”顺便说一句,如果它们都在属性“value”中存储“rainbow”,那么结果将是相等的。。。。您可以根据OP的需要定义他们的\uuuu eq\uuu
方法,只查看值
字段。但是是的,这是鸭子打字的原则。不管你喜欢它还是离开它。这种方法的一个相当明显的缺点是try/except要慢得多(当我做测试时大约60%)。请看,try/except更像Pythonic,但我宁愿使用快速的非Pythonic代码,也不愿使用缓慢的Pythonic代码。:)Python异常的设计速度非常快,但它们当然不会像简单的类型查找那样快。但是,除非您将其用于数字处理,否则我更担心的是优化程序员的工作效率(阅读:扩展友好设计),而不是减少毫秒。虽然这个答案是正确的,但它并没有解释如何以一种实际上有助于回退到另一个对象的\ueq()的方式实现__()
。
class A(object):
def __init__(self, x):
self.x = x
def __eq__(self, other):
if isinstance(other, A):
return self.x == other.x
return NotImplemented
class B(A):
def __init__(self, x, y):
A.__init__(self, x)
self.y = y
def __eq__(self, other):
if isinstance(other, B):
return self.x, self.y == other.x, other.y
return NotImplemented