在python2.x的版本中,支持不同類型的比較,其比較原則如下:
內置類型:
1.同一類型:
1.1)數字類型:包含int、float、long、complex、bool(bool類型是int的子類,且True=1, False=0)。就按照數字的大小進行比較,例如:
1.2)非數字類型:
1.2.1)如果類型中定義了如__cmp__、__gt__等魔法方法,就按照其魔法方法的返回值進行比較,具體如何就是這篇文章接下來要討論的內容。
1.2.2)如果沒有定義上述的魔法方法,就按照 id 函數的返回值進行比較,由於 id 函數返回的是 int,所以返回值按照數字的規則進行比較。
2.不同類型:
2.1)如果比較的對象中含有數字類型,例如:int 和 str 進行比較。數字類型將小於其他任何類型,也就是說“數字類型是最小的”(不包括None,None比數字類型還小,所以應該說是None最小,但很少會使用None類型比較,所以這里忽略None,得出數字類型最小的結論)。
2.2)如果比較的對象中不含有數字類型,例如 dict 和 str 進行比較。那么就按類名進行比較,所以 {} < '' 相當於 'dict' < 'str' ,即將類名轉換為字符串,然后按字符串的原則進行比較。
這里有幾點要補充的:
雖然內置類型基本都實現了__eq__、__ge__、__gt__等內置方法,但是這里有必要對相同類型實例直接的比較進行總結說明:
1.序列的比較:
序列的比較是按照索引順序取出元素進行比較,如果不兩個系列不等長,則可以看作是在短的序列后面填充 None ,直到兩個序列等長。
在 tuple 和 list 的討論中,我使用了打擂台的比喻。兩個比較的序列相當於按照索引順序派出選手,比賽采用的是殘酷的賽制,一旦某個選手失敗,那么那個隊伍就會輸掉。也就是說一旦某個元素小於對面,那么整個序列都會小於另一個序列。
當然這里說的是同一類型的序列,例如 str、list、tuple之間的比較是屬於不同內置類型的比較,按照上面的原則,就是按照類名進行比較。所以這里討論的是 str和 str 的比較;list 和 list 的比較;tuple 和 tuple的比較。
str 和 str 之間的比較:首先按照序列順序排出元素,而 str 中的元素就是單個字符,然后字符之間的比較是通過將其轉換為 acsll 碼后進行數字之間的比較的。python 中提供了一個內置的函數 ord 來進行這項工作:
還有一個反函數 chr 用來將數字轉換成字符:
所以,當字符串 'abc' 和字符串 'abe' 進行比較時,前兩個字符相等,而 'e' > 'c' ,所以 'abe' > 'abc'。
如果兩者不等長時,例如:'abc' 和 'ab' 進行比較,前兩字符相等,而 'c' 相對於和 None 比,None又最小,所以 'abc' > 'ab' 。
其他序列也遵循這個逐項比較的原則,例如:[1, 2, 3] 和 [1, [2], 3] 比較時,第一個元素相等,所以比第二個元素。第二元素是不同類型的,其中一個是數字,數字除None外最小,所以第二個列表比較大。如果一直比較下去都相等的話,那么就判斷兩個元素相等(== )。但是這並不意味着兩個元素是同一個對象,所以 is 判斷不一定為 True。
另外,中文是按照編碼的字符串進行比較的,例如:
'd' > 'b' 所以 '一' > '二'。但字符串的編碼是會根據編碼聲明而改變的,例如 # coding: utf-8 和 # coding= gbk 的字符串編碼就不一樣,所以比較會有差異性。但是也很少會進行中文之間的比較。
集合和序列的比較方法類似。
2.字典間的比較:
字典其實也類似於序列,也是逐個比較,但字典比較的是鍵不是值,而且字典是無序的,所以比較的順序難以預測,所以無論如何,不建議字典間的比較。
版本差異:
由於不同內置類型之間的比較不符合人的直覺,而且行為怪異,難以估計。所以python3.x中取消了不同內置類型之間比較的支持,例如:在python3.x中進行 int 和 str 之間的比較時會直接拋出異常。
自定義的類:
1.繼承自基本類型且沒有重寫相關的魔法方法,那么就按照上面內置類型的原則進行比較。
2.自定義的類,但沒有重寫相關魔法方法。那么 新式類 > 經典類;新式類之間按類名進行比較;經典類之間按 id 函數的返回值進行比較。
3.只要重寫了相應的魔法方法,那么就按照魔法方法的返回值作為結果。不管是否繼承自基本類型。
相關魔法方法:
__cmp__(self, other) :對應於python的內置函數 cmp ,在self > other 時應該返回 正整數;self == other時,返回 0;當self < other時應該返回一個 負整數。(這個方法因為和下面的方法存在冗余,所以在python3.x中被刪除了。)
例子:
class Foo(str): def __new__(cls, word): return str.__new__(cls, word) def __cmp__(self, other): if ('scolia' in self) and ('scolia' in other): return 0 elif 'scolia' in self: return 1 else: return -1 a = Foo('scolia') b = Foo('scolia123') c = Foo('good') d = Foo('Good') print cmp(a, b) print cmp(b, c) print cmp(c, d)
在這個例子中,我定義凡是兩個字符串中都有子串'scolia'的,都返回0;當個只有一個有,且有的那個作為第一個參數時,返回1;兩個都沒有,不管是什么都返回-1。
也就是說cmp函數的第一個參數就是 self ,第二個參數就是 other。
當然 cmp 還有一個隱藏特性, 當兩個參數為同一對象的時候,直接返回0而不管內部的__cmp__邏輯如果,例如:
c = Foo('good') d = c print cmp(c, d)
本來按照自己寫的代碼的邏輯的話,c 和 d 都不含字串 'scolia' ,應該返回-1才對,但是實際上得到的卻是0。說明cmp函數對於同一對象是直接給出結果而不理魔法方法的。
-
__eq__(self, other)
-
定義相等符號的行為,==
-
__ne__(self,other)
-
定義不等符號的行為,!=
-
__lt__(self,other)
-
定義小於符號的行為,<
-
__gt__(self,other)
-
定義大於符號的行為,>
-
__le__(self,other)
-
定義小於等於符號的行為,<=
-
__ge__(self,other)
-
定義大於等於符號的行為,>=
以上魔法方法都必須返回一個布爾值。
例如:
class Foo(str): def __new__(cls, word): return str.__new__(cls, word) def __eq__(self, other): if ('scolia' in self ) and ('scolia' in other): return True else: return False a = Foo('scolia') b = Foo('scolia123') print a == b
所以,這些魔法方法其實就是重載了相應的符號而已,例如這里的 == ,左邊的相當於 self,右邊的相當於 other。你可以根據自己的需要進行邏輯編排。
又例如:
class Foo(str): def __new__(cls, word): return str.__new__(cls, word) def __gt__(self, other): return len(self) > len(other) def __lt__(self, other): return len(self) < len(other) def __ge__(self, other): return len(self) >= len(other) def __le__(self, other): return len(self) <= len(other)
這里重載了多個比較符,是核心是按照字符串的長度進行比較,越長的越大。這里看似返回的是一個表達式,但是函數在真正返回的時候會將這個表達式計算出來,也就是說最終返回的其實還是布爾值。
而沒有重載的比較符,如這里沒有重載 __eq__ 即 == ,將自動調用其父類的方法,這也很符合我們繼承的概念。
如果和基本類型比較呢:
class Foo(str): def __new__(cls, word): return str.__new__(cls, word) def __eq__(self, other): if ('scolia' in self ) and ('scolia' in other): return True else: return False b = Foo('scolia') a = 'scolia123' print a == b print b == a
貌似是以我們自己寫的方法為准,不管 == 兩邊的順序如何。
如果兩個自定義的類沖突呢:
class Foo(str): def __new__(cls, word): return str.__new__(cls, word) def __eq__(self, other): if ('scolia' in self ) and ('scolia' in other): return True else: return False class Boo(str): def __new__(cls, word): return str.__new__(cls, word) def __eq__(self, other): if ('scolia' in self ) and ('scolia' in other): return False else: return True a = Foo('scolia') b = Boo('scolia') print a == b print b == a
此時,誰在 == 號的左邊,就運用誰的規則。
關於比較的魔法方法就討論到這里,歡迎大家交流。
相關的參考資料:戳這里