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')
__str__&__repr__

二:__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')}
使用默认的__hash__与__eq__
  由上可以看出:
同一个类参数相同的两个实例其各种比较[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]) )
重写__hash__与__eq__

   对于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的__hash__与_eq__
  对于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
mutable object的hash处理

三:__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 )
__bool__

四: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()
用扑克牌来测试__lt__
   由(*)看出:  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属性
继承mutable object后添加属性

  __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
__new__为immutable object添加属性
原文地址:https://www.cnblogs.com/pengsixiong/p/5381850.html