Python 中的比较运算符与“丰富的比较”方法
Comparison operators vs “rich comparison” methods in Python
谁能给我解释一下两者之间的区别。这些通常是等价的吗?也许我在这里完全错了,但我认为每个比较运算符都必然与一个 “丰富比较” 方法相关。这是来自文档:
The correspondence between operator symbols and method names is as
follows:
x<y
calls x.__lt__(y)
, x<=y
calls x.__le__(y)
, x==y
calls x.__eq__(y)
, x!=y
calls x.__ne__(y)
, x>y
calls x.__gt__(y)
, and x>=y
calls x.__ge__(y)
.
这里有一个例子可以说明我的困惑。
Python 3.x:
dict1 = {1:1}
dict2 = {2:2}
>>> dict1 < dict2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'dict' and 'dict'
>>> dict1.__lt__(dict2)
NotImplemented
Python 2.x:
dict1 = {1:1}
dict2 = {2:2}
>>> dict1 < dict2
True
>>> dict1.__lt__(dict2)
NotImplemented
从 python 3 示例来看,不支持调用 dict1 < dict2
似乎符合逻辑。但是 Python 2 例子呢?为什么被接受?
我知道不像Python2,在Python3中,并不是所有的对象都支持比较运算符。令我惊讶的是,调用 __lt__()
.
时,两个版本 return 和 NotImplemented
单例
这依赖于 __cmp__
魔术方法,这是富比较运算符要替换的方法:
>>> dict1 = {1:1}
>>> dict2 = {2:2}
>>> dict1.__cmp__
<method-wrapper '__cmp__' of dict object at 0x10f075398>
>>> dict1.__cmp__(dict2)
-1
至于顺序逻辑,这里是Python 2.7 documentation:
Mappings (instances of dict) compare equal if and only if they have
equal (key, value) pairs. Equality comparison of the keys and values
enforces reflexivity.
Outcomes other than equality are resolved consistently, but are not
otherwise defined.
带脚注:
Earlier versions of Python used lexicographic comparison of the sorted
(key, value) lists, but this was very expensive for the common case of
comparing for equality. An even earlier version of Python compared
dictionaries by identity only, but this caused surprises because
people expected to be able to test a dictionary for emptiness by
comparing it to {}.
而且,在 Python 3.0 中,排序得到了简化。这是来自 documentation:
The ordering comparison operators (<, <=, >=, >)
raise a TypeError
exception when the operands don’t have a meaningful natural ordering.
builtin.sorted()
and list.sort()
no longer accept the cmp argument
providing a comparison function. Use the key argument instead.
The cmp()
function should be treated as gone, and the __cmp__()
special method
is no longer supported. Use __lt__()
for sorting, __eq__()
with
__hash__()
, and other rich comparisons as needed. (If you really need the cmp()
functionality, you could use the expression (a > b) - (a <> b)
as the equivalent for cmp(a, b)
.)
因此,明确地说,在 Python 2 中,由于未实现丰富的比较运算符,dict
对象将从数据模型退回到 __cmp__
documentation:
object.__cmp__(self, other)
Called by comparison operations if rich
comparison (see above) is not defined. Should return a negative
integer if self < other, zero if self == other, a positive integer
if self > other.
运算符 <
与 __lt__
的注意事项:
import types
class A:
def __lt__(self, other): return True
def new_lt(self, other): return False
a = A()
print(a < a, a.__lt__(a)) # True True
a.__lt__ = types.MethodType(new_lt, a)
print(a < a, a.__lt__(a)) # True False
A.__lt__ = types.MethodType(new_lt, A)
print(a < a, a.__lt__(a)) # False False
<
调用 class 上定义的 __lt__
; __lt__
调用在对象上定义的 __lt__
。
通常是一样的:)而且用起来非常美味:A.__lt__ = new_lt
谁能给我解释一下两者之间的区别。这些通常是等价的吗?也许我在这里完全错了,但我认为每个比较运算符都必然与一个 “丰富比较” 方法相关。这是来自文档:
The correspondence between operator symbols and method names is as follows:
x<y
callsx.__lt__(y)
,x<=y
callsx.__le__(y)
,x==y
callsx.__eq__(y)
,x!=y
callsx.__ne__(y)
,x>y
callsx.__gt__(y)
, andx>=y
callsx.__ge__(y)
.
这里有一个例子可以说明我的困惑。
Python 3.x:
dict1 = {1:1}
dict2 = {2:2}
>>> dict1 < dict2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'dict' and 'dict'
>>> dict1.__lt__(dict2)
NotImplemented
Python 2.x:
dict1 = {1:1}
dict2 = {2:2}
>>> dict1 < dict2
True
>>> dict1.__lt__(dict2)
NotImplemented
从 python 3 示例来看,不支持调用 dict1 < dict2
似乎符合逻辑。但是 Python 2 例子呢?为什么被接受?
我知道不像Python2,在Python3中,并不是所有的对象都支持比较运算符。令我惊讶的是,调用 __lt__()
.
NotImplemented
单例
这依赖于 __cmp__
魔术方法,这是富比较运算符要替换的方法:
>>> dict1 = {1:1}
>>> dict2 = {2:2}
>>> dict1.__cmp__
<method-wrapper '__cmp__' of dict object at 0x10f075398>
>>> dict1.__cmp__(dict2)
-1
至于顺序逻辑,这里是Python 2.7 documentation:
Mappings (instances of dict) compare equal if and only if they have equal (key, value) pairs. Equality comparison of the keys and values enforces reflexivity.
Outcomes other than equality are resolved consistently, but are not otherwise defined.
带脚注:
Earlier versions of Python used lexicographic comparison of the sorted (key, value) lists, but this was very expensive for the common case of comparing for equality. An even earlier version of Python compared dictionaries by identity only, but this caused surprises because people expected to be able to test a dictionary for emptiness by comparing it to {}.
而且,在 Python 3.0 中,排序得到了简化。这是来自 documentation:
The ordering comparison operators
(<, <=, >=, >)
raise a TypeError exception when the operands don’t have a meaningful natural ordering.
builtin.sorted()
andlist.sort()
no longer accept the cmp argument providing a comparison function. Use the key argument instead.The
cmp()
function should be treated as gone, and the__cmp__()
special method is no longer supported. Use__lt__()
for sorting,__eq__()
with__hash__()
, and other rich comparisons as needed. (If you really need thecmp()
functionality, you could use the expression(a > b) - (a <> b)
as the equivalent forcmp(a, b)
.)
因此,明确地说,在 Python 2 中,由于未实现丰富的比较运算符,dict
对象将从数据模型退回到 __cmp__
documentation:
object.__cmp__(self, other)
Called by comparison operations if rich comparison (see above) is not defined. Should return a negative integer if self < other, zero if self == other, a positive integer if self > other.
运算符 <
与 __lt__
的注意事项:
import types
class A:
def __lt__(self, other): return True
def new_lt(self, other): return False
a = A()
print(a < a, a.__lt__(a)) # True True
a.__lt__ = types.MethodType(new_lt, a)
print(a < a, a.__lt__(a)) # True False
A.__lt__ = types.MethodType(new_lt, A)
print(a < a, a.__lt__(a)) # False False
<
调用 class 上定义的 __lt__
; __lt__
调用在对象上定义的 __lt__
。
通常是一样的:)而且用起来非常美味:A.__lt__ = new_lt