一:string相關:__str__(),__repr__(),__format__()
str方法更面向人類閱讀,print()使用的就是str
repr方法更面對python,目標是希望生成一個放入eval方法就能夠執行的python語句字符串
注意,不要一看到format方法就認為他是用來取代%賦值的
*在里format方法可通過后面的!r與!s來指定使用repr還是str,即此時就不是用的format方法了,而是調用的repr或者str
format有兩種參數形式:
1:"",此類可以直接用str(...)來返回
2:format(someobject, specification),
e.g:"{0:06.4f}", the 06.4f is the format specification that applies to item 0 of the argument list to be formatted
format很少用到,在此先略過(書61頁)

1 class Hand: 2 def __init__( self, name, *friends ): 3 self.name = name 4 self.friends= list(friends) 5 6 def __str__( self ): 7 return ", ".join( map(str, self.friends) ) 8 9 def __repr__( self ): 10 return "{__class__.__name__}({self.name!r}, {_cards_str})".format(__class__=self.__class__,_cards_str=", ".join( map(repr, self.friends) ),self=self) 11 12 a = Hand("pd","DK","Nancy") 13 print(str(a)) 14 print(repr(a)) 15 # 輸出: 16 # DK, Nancy, yao 17 # Hand('pd', 'DK', 'Nancy')
二:__hash__(),__eq__()
python有兩個hash庫,密碼學的hashlib,zlib的adler32()與crc32()
對於簡單數字這兩個庫都不用,直接用hash函數就行,hash函數常用於set,dict等定位其中元素
python每個對象都有個id,本質上id是其內存地址,is比較是基於id的,可用id(x)查看其值,而基類object的__hash__方法就是將其id/16取整后作為integer返回:
需要注意的是只有immutable數值類型才能用hash方法,list與dict沒有。所以,如果我們創建的是mutable的對象,就讓hash函數返回None就行了
__eq__() 方法,用於==對比,是基於hash值的。
對於immutable object,hash返回基於id的變數,eq用id相比就可以了。而mutable object寫eq,hash返回None

1 #使用默認eq與hash 2 class Card: 3 insure= False 4 def __init__( self, rank, suit, hard, soft ): 5 self.rank= rank 6 self.suit= suit 7 self.hard= hard 8 self.soft= soft 9 def __repr__( self ): 10 return "{__class__.__name__}(suit={suit!r}, rank={rank!r})".format(__class__=self.__class__, **self.__dict__) 11 def __str__( self ): 12 return "{rank}{suit}".format(**self.__dict__) 13 class NumberCard( Card ): 14 def __init__( self, rank, suit ): 15 super().__init__( str(rank), suit, rank, rank ) 16 class AceCard( Card ): 17 def __init__( self, rank, suit ): 18 super().__init__( "A", suit, 1, 11 ) 19 class FaceCard( Card ): 20 def __init__( self, rank, suit ): 21 super().__init__( {11: 'J', 12: 'Q', 13: 'K' }[rank], suit, 10, 10 ) 22 23 c1 = AceCard( 1, '?' ) 24 c2 = AceCard( 1, '?' ) 25 26 print(id(c1),id(c2)) 27 print(id(c1)/16,id(c2)/16) 28 print(hash(c1),hash(c2)) 29 print(c1==c2) 30 print(c1 is c2) 31 print( set([c1,c2]) ) 32 33 # 輸出: 34 # 42444656 42444688 35 # 2652791.0 2652793.0 36 # 2652791 2652793 37 # False 38 # False 39 # {AceCard(suit='?', rank='A'), AceCard(suit='?', rank='A')}
由上可以看出:
同一個類參數相同的兩個實例其各種比較[id, is, ==, hash,set去重]都不一樣
在現實中使用是有問題的,比如這里面如果我們要比較兩張牌則這兩張應該看為相同,所以我們需要改寫其eq方法,讓其不基於id來達到目的

1 #改進版,重寫eq與hash 2 class Card2: 3 insure= False 4 def __init__( self, rank, suit, hard, soft ): 5 self.rank= rank 6 self.suit= suit 7 self.hard= hard 8 self.soft= soft 9 def __repr__( self ): 10 return "{__class__.__name__}(suit={suit!r}, rank={rank!r})".format(__class__=self.__class__, **self.__dict__) 11 def __str__( self ): 12 return "{rank}{suit}".format(**self.__dict__) 13 14 def __eq__( self, other ): 15 return self.suit == other.suit and self.rank == other.rank 16 17 def __hash__( self ): 18 return hash(self.suit) ^ hash(self.rank) 19 20 class AceCard2( Card2 ): 21 insure= True 22 def __init__( self, rank, suit ): 23 super().__init__( "A", suit, 1, 11 ) 24 25 c1 = AceCard2( 1, '?' ) 26 c2 = AceCard2( 1, '?' ) 27 28 print(id(c1),id(c2)) 29 print(id(c1)/16,id(c2)/16) 30 print(hash(c1),hash(c2)) #變為相等的數字,但是需要注意的是已經不是 id/16 31 print(c1==c2) #變為True 32 print(c1 is c2) 33 print( set([c1,c2]) )
對於mutable object,在這里依然用card做示范,但其實是不貼切的,card應該是immutable的。注意hash返回None的寫法

1 class Card3: 2 insure= False 3 def __init__( self, rank, suit, hard, soft ): 4 self.rank= rank 5 self.suit= suit 6 self.hard= hard 7 self.soft= soft 8 def __repr__( self ): 9 return "{__class__.__name__}(suit={suit!r}, rank={rank!r})".format(__class__=self.__class__, **self.__dict__) 10 def __str__( self ): 11 return "{rank}{suit}".format(**self.__dict__) 12 13 def __eq__( self, other ): 14 return self.suit == other.suit and self.rank == other.rank 15 # and self.hard == other.hard and self.soft == other.soft 16 17 __hash__ = None #!!!!!!!!! 18 19 class AceCard3( Card3 ): 20 insure= True 21 def __init__( self, rank, suit ): 22 super().__init__( "A", suit, 1, 11 ) 23 24 25 c1 = AceCard3( 1, '?' ) 26 c2 = AceCard3( 1, '?' ) 27 28 print(id(c1),id(c2)) 29 print(id(c1)/16,id(c2)/16) 30 print(hash(c1),hash(c2)) #報錯:TypeError: unhashable type: 'AceCard3' 31 print(c1==c2) #True 32 print(c1 is c2) 33 print( set([c1,c2]) ) #報錯:TypeError: unhashable type: 'AceCard3',由於不能hash,自然不能用於set數據結構
對於mutable object,若想對其實例進行數值分析,
可以寫一個immutable的子類,將實例傳入后完全copy下來,再對copy份進行寫hash處理:
如下面的Hand類,可以寫一個不可變的FrozenHand類來對其進行hash數值處理

1 class Hand: 2 def __init__( self, dealer_card, *cards ): 3 self.dealer_card= dealer_card 4 self.cards= list(cards) 5 def __str__( self ): 6 return ", ".join( map(str, self.cards) ) 7 def __repr__( self ): 8 return "{__class__.__name__}({dealer_card!r}, {_cards_str})".format(__class__=self.__class__,_cards_str=", ".join( map(repr, self.cards) ),**self.__dict__ ) 9 def __eq__( self, other ): 10 return self.cards == other.cards and self.dealer_card ==other.dealer_card 11 __hash__ = None 12 13 import sys 14 class FrozenHand( Hand ): 15 def __init__( self, *args, **kw ): 16 if len(args) == 1 and isinstance(args[0], Hand): 17 # Clone a hand 18 other= args[0] 19 self.dealer_card= other.dealer_card 20 self.cards= other.cards 21 else: 22 # Build a fresh hand 23 super().__init__( *args, **kw ) 24 def __hash__( self ): 25 h= 0 26 for c in self.cards: 27 h = (h + hash(c)) % sys.hash_info.modulus 28 return h 29 30 stats = defaultdict(int) 31 d= Deck() #Deck是一堆牌 32 h = Hand( d.pop(), d.pop(), d.pop() ) 33 h_f = FrozenHand( h ) 34 stats[h_f] += 1
三:__bool__
使用:
if xxx:
... #True
else:
... #False
python認為為False的情況:
1:False
2: 0
3:空:‘’,【】,(),{}
注:自帶的bool函數可用於測定並返回True還是False,
如 bool(0),bool([]),bool('')都返回False

1 #對於Deck類,添加__bool__方法: 2 def __bool__( self ): 3 return bool( self._cards ) 4 5 #如果是繼承 list 類,可能如下書寫: 6 def __bool__( self ): 7 return super().__bool__( self )
四:6大比較方法
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)
x>=y calls x.__ge__(y)

1 class BlackJackCard_p: 2 def __init__( self, rank, suit ): 3 self.rank= rank 4 self.suit= suit 5 6 def __lt__( self, other ): 7 print( "Compare {0} < {1}".format( self, other ) ) 8 return self.rank < other.rank 9 10 def __str__( self ): 11 return "{rank}{suit}".format( **self.__dict__ ) 12 13 >>> two = BlackJackCard_p( 2, '?' ) 14 >>> three = BlackJackCard_p( 3, '?' ) 15 >>> two < three 16 Compare 2? < 3? (*) 17 True 18 >>> two > three (*) 19 Compare 3? < 2? 20 False 21 >>> two == three 22 False 23 >>> two <= three 24 Traceback (most recent call last): 25 File "<stdin>", line 1, in <module> 26 TypeError: unorderable types: BlackJackCard_p() <= BlackJackCard_p()
由(*)看出: two < three的比較時用的是two.__lt__(three)
two>three由於沒有__gt__(),用的是three.__lt__(two)
由於這種機制,我們可以只提供四種方法來包含上面六種:
__eq__(), __ne__(), __lt__(), __le__().
1:同類實例比較

1 class BlackJackCard: 2 def __init__( self, rank, suit, hard, soft ): 3 self.rank= rank 4 self.suit= suit 5 self.hard= hard 6 self.soft= soft 7 def __lt__( self, other ): 8 if not isinstance( other, BlackJackCard ): 9 return NotImplemented 10 return self.rank < other.rank 11 def __le__( self, other ): 12 try: 13 return self.rank <= other.rank 14 except AttributeError: 15 return NotImplemented 16 def __gt__( self, other ): 17 if not isinstance( other, BlackJackCard ): 18 return NotImplemented 19 return self.rank > other.rank 20 def __ge__( self, other ): 21 if not isinstance( other, BlackJackCard ): 22 return NotImplemented 23 return self.rank >= other.rank 24 def __eq__( self, other ): 25 if not isinstance( other, BlackJackCard ): 26 return NotImplemented 27 return self.rank == other.rank and self.suit == other.suit #比較==時多了對於suit的檢查,而比較大小時只比較了rank 28 def __ne__( self, other ): 29 if not isinstance( other, BlackJackCard ): 30 return NotImplemented 31 """ 32 注意其上實現的六個比較方法中有兩種檢驗方式: 33 1:explicit的用isinstance檢驗是不是BlackJackCard的類實例 34 2:implicit的用try語句,此種除非是某個類剛好有rank屬性才會發生比較, 35 實際上第二種方法更好,因錯出現剛好有rank屬性的類又用來比較的概率十分小,而可以用來擴展為別的紙牌游戲的牌與之的比較 36 """ 37 >>> two = card21( 2, '?' ) 38 >>> three = card21( 3, '?' ) 39 >>> two_c = card21( 2, '?' ) 40 41 >>> two == two_c 42 False 43 >>> two.rank == two_c.rank 44 True 45 >>> two < three 46 True 47 >>> two_c < three 48 True 49 >>> two < 2 #報錯是因為__lt__()方法用isinstance檢驗了類型,非同類就報錯 50 Traceback (most recent call last): 51 File "<stdin>", line 1, in <module> 52 TypeError: unorderable types: Number21Card() < int() 53 >>> two == 2 #此地沒有報錯是因為遇到NotImplemented,python會交換他們.在此即是變成int.__eq__() 54 False
2:異類實例比較

1 #下面主要是用isinstance來判斷相應的可能的異類的類型,再做處理 2 class Hand: 3 def __init__( self, dealer_card, *cards ): 4 self.dealer_card= dealer_card 5 self.cards= list(cards) 6 def __str__( self ): 7 return ", ".join( map(str, self.cards) ) 8 def __repr__( self ): 9 return "{__class__.__name__}({dealer_card!r}, {_cards_str})".format(__class__=self.__class__,_cards_str=", ".join( map(repr, self.cards) ),**self.__dict__ ) 10 def __eq__( self, other ): 11 if isinstance(other,int): 12 return self.total() == other 13 try: 14 return (self.cards == other.cards and self.dealer_card == other.dealer_card) 15 except AttributeError: 16 return NotImplemented 17 def __lt__( self, other ): 18 if isinstance(other,int): 19 return self.total() < other 20 try: 21 return self.total() < other.total() 22 except AttributeError: 23 return NotImplemented 24 def __le__( self, other ): 25 if isinstance(other,int): 26 return self.total() <= other 27 try: 28 return self.total() <= other.total() 29 except AttributeError: 30 return NotImplemented 31 __hash__ = None 32 def total( self ): 33 delta_soft = max( c.soft-c.hard for c in self.cards ) 34 hard = sum( c.hard for c in self.cards ) 35 if hard+delta_soft <= 21: 36 return hard+delta_soft 37 return hard 38 39 >>> two = card21( 2, '?' ) 40 >>> three = card21( 3, '?' ) 41 >>> two_c = card21( 2, '?' ) 42 >>> ace = card21( 1, '?' ) 43 >>> cards = [ ace, two, two_c, three ] 44 >>> h= Hand( card21(10,'?'), *cards ) 45 >>> print(h) 46 A?, 2?, 2?, 3? 47 >>> h.total() 48 18 49 >>> h2= Hand( card21(10,'?'), card21(5,'?'), *cards ) 50 >>> print(h2) 51 5?, A?, 2?, 2?, 3? 52 >>> h2.total() 53 13 54 >>> h < h2 55 False 56 >>> h > h2 57 True 58 >>> h == 18 59 True 60 >>> h < 19 61 True 62 >>> h > 17 63 Traceback (most recent call last): 64 File "<stdin>", line 1, in <module> 65 TypeError: unorderable types: Hand() > int()
五:immutable object的__new__
首先說明為何講immutable object的__new__,因為mutable object可以直接在__init__中加入新參數而immutable object是不行的:

1 # 可變對象: 2 class cL(list): 3 def __init__(self,value,mutable): 4 super().__init__(value) 5 self.mutable = mutable 6 aha = cL([2,3,4],'mumu') 7 #注意此處的value參數,用help(list)可看到:list(iterable) -> new list initialized from iterable's items 8 print(aha,aha.mutable) 9 # 輸出:[2, 3, 4] mumu,可見多了個mutable屬性
__new__()默認就是staticmethod,不需要寫@satticmethod
對於不可變對象的子類並不能夠直接通過__init__方法創造更多的屬性,只有通過__new__方法才行
__new__( cls, *args, **kw ).
cls:即將要實例化的類,此參數在實例化時由Python解釋器自動提供
*arg & **kw: with the exception of the cls argument, will be passed to __init__() as part of the standard Python behavior.
__new__必須要有返回值,返回實例化出來的實例,且在后一步的__init__方法中就是對這個返回的實例進行操作
默認的__new__()方法調用的是super().__new__(cls)

1 class Float_Units( float ): 2 def __new__( cls, value, unit ): 3 obj= super().__new__( cls, value ) #實例化父類object,並用value初始化 4 obj.unit= unit #給父類object添加了屬性unit,這樣由於繼承關系也就有了此屬性 5 return obj #返回實例 6 speed= Float_Units( 6.5, "knots" ) 7 print(speed) 8 print(speed.unit) 9 # 輸出: 10 # 6.5 11 # knots 可見,能夠賦值value以及添加屬性unit