• 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添加属性
  • 相关阅读:
    jquery ajax get 数组参数
    xcopy中文文件名,中文件目录乱码问题
    小程序使用wxParse插件解析html标签图片间距问题
    30分钟彻底弄懂flex布局
    abp ueditor 多图以及文件无法上传
    Lubuntu 18.04 自动登录
    树莓派虚拟环境手动安装HA
    ESP-01S刷ESPEasy固件,接入HA
    百度TTS的来由
    Pi 3B+编译安装python3.6.8
  • 原文地址:https://www.cnblogs.com/pengsixiong/p/5381850.html
Copyright © 2020-2023  润新知