python面对对象编程------4:类基本的特殊方法__str__,__repr__,__hash__,__new__,__bool__,6大比较方法
一: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