python魔法方法-比較相關


  在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

  此時,誰在 == 號的左邊,就運用誰的規則。

 


 

  關於比較的魔法方法就討論到這里,歡迎大家交流。

  相關的參考資料:戳這里 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM