它是什么?
1 2 |
>>> type(NotImplemented) <type 'NotImplementedType'> |
NotImplemented
是Python在内置命名空间中的六个常数之一。其他有 False
、True
、None
、Ellipsis
和 __debug__
。和 Ellipsis
很像,NotImplemented
能被重新赋值(覆盖)。对它赋值,甚至改变属性名称, 不会产生 SyntaxError
。所以它不是一个真正的“真”常数。当然,我们应该永远不改变它。 但是为了完整性:
1 2 3 4 5 6 7 8 |
>>> None = 'hello' ... SyntaxError: can't assign to keyword >>> NotImplemented NotImplemented >>> NotImplemented = 'do not' >>> NotImplemented 'do not' |
它有什么用?什么时候用?
NotImplemented
是个特殊值,它能被二元特殊方法返回(比如__eq__()
、__lt__()
、__add__()
、__rsub__()
等),表明某个类型没有像其他类型那样实现这些操作。同样,它或许会被原地处理(in place)的二元特殊方法返回(比如__imul__()
、__iand__()
等)。还有,它的实际值为True
:
1 2 |
>>> bool(NotImplemented) True |
你也许会问自己,“但我认为当这个操作没有实现时,我应该产生个NotImpementedError
”。我们会看些例子,关于为什么当实现二元特殊方法时不是这么回事儿。
让我们看看NotImplemented
常数的用法,通过__eq__()
对于两个非常基本(且没用)的类 A
和 B
的编码。[对于这个简单的例子,为了避免干扰,不会实现__ne__()
,但是总的说来,每次实现__eq__()
时,__ne__()
也应该被实现,除非,有个足够充分的理由去不实现它。]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# example.py class A(object): def __init__(self, value): self.value = value def __eq__(self, other): if isinstance(other, A): print('Comparing an A with an A') return other.value == self.value if isinstance(other, B): print('Comparing an A with a B') return other.value == self.value print('Could not compare A with the other class') return NotImplemented class B(object): def __init__(self, value): self.value = value def __eq__(self, other): if isinstance(other, B): print('Comparing a B with another B') return other.value == self.value print('Could not compare B with the other class') return NotImplemented |
现在,在解释器中:
1 2 3 |
>>> from example import A, B >>> a1 = A(1) >>> b1 = B(1) |
我们现在可以实验下对于 __eq__()
不同的调用,看看发生了什么。作为提醒,在Python中,a == b
会调用a.__eq__(b)
:
1 2 3 |
>>> a1 == a1 Comparing an A with an A True |
正如所望,a1
等于a1
(自己),使用类A
中的__eq__()
来进行这个比较的。比较b1
和它自己也会产生类似结果:
1 2 3 |
>>> b1 == b1 Comparing a B with another B True |
现在,那要是我们比较a1
和b1
呢?由于在A
的__eq__()
会检查other
是不是B
的一个实例,我们想要a1.__eq__(b1)
去处理这个比较并返回True
:
1 2 3 |
>>> a1 == b1 Comparing an A with a B True |
就是这样。现在,如果我们比较b1
和a1
(即调用b1.__eq__(a1)
),我们会想要返回NotImplemented
。这是因为B
的__eq__()只
和其他B
的实例进行比较。来看看发生了什么:
1 2 3 4 |
>>> b1 == a1 Could not compare B against the other class Comparing an A with a B True |
聪明!b1.__eq__(a1)
方法返回NotImplemented
,这样会导致调用A
中的__eq__()
方法。而且由于在A
中的__eq__()
定义了A
和B
之间的比较,所以就得到了正确的结果(True
)。
这就是返回了NotImplemented
的所做的。NotImplemented
告诉运行时,应该让其他对象来完成某个操作。在表达b1 == a1
中,b1.__eq__(a1)
返回了NotImplemented
,这说明Python试着用a1.__eq__(b1)
。由于a1
足够可以返回True
,因此这个表达可以成功。如果A
中的__eq__()
也返回NotImplemented
,那么运行时会退化到使用内置的比较行为,即比较对象的标识符(在CPython中,是对象在内存中的地址)。
注意:如果在调用b1.__eq__(a1)
时抛出NotImpementedError
,而不进行处理,就会中断代码的执行。而NotImplemented
无法抛出,仅仅是用来进一步测试是否有其他方法可供调用。