它是什麼?
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
無法丟擲,僅僅是用來進一步測試是否有其他方法可供呼叫。