一: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页)
class Hand:
def __init__( self, name, *friends ):
self.name = name
self.friends= list(friends) def __str__( self ):
return ", ".join( map(str, self.friends) ) def __repr__( self ):
return "{__class__.__name__}({self.name!r}, {_cards_str})".format(__class__=self.__class__,_cards_str=", ".join( map(repr, self.friends) ),self=self) a = Hand("pd","DK","Nancy")
print(str(a))
print(repr(a))
# 输出:
# DK, Nancy, yao
# 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
#使用默认eq与hash
class Card:
insure= False
def __init__( self, rank, suit, hard, soft ):
self.rank= rank
self.suit= suit
self.hard= hard
self.soft= soft
def __repr__( self ):
return "{__class__.__name__}(suit={suit!r}, rank={rank!r})".format(__class__=self.__class__, **self.__dict__)
def __str__( self ):
return "{rank}{suit}".format(**self.__dict__)
class NumberCard( Card ):
def __init__( self, rank, suit ):
super().__init__( str(rank), suit, rank, rank )
class AceCard( Card ):
def __init__( self, rank, suit ):
super().__init__( "A", suit, 1, 11 )
class FaceCard( Card ):
def __init__( self, rank, suit ):
super().__init__( {11: 'J', 12: 'Q', 13: 'K' }[rank], suit, 10, 10 ) c1 = AceCard( 1, '?' )
c2 = AceCard( 1, '?' ) print(id(c1),id(c2))
print(id(c1)/16,id(c2)/16)
print(hash(c1),hash(c2))
print(c1==c2)
print(c1 is c2)
print( set([c1,c2]) ) # 输出:
# 42444656 42444688
# 2652791.0 2652793.0
# 2652791 2652793
# False
# False
# {AceCard(suit='?', rank='A'), AceCard(suit='?', rank='A')}
使用默认的__hash__与__eq__
由上可以看出:
同一个类参数相同的两个实例其各种比较[id, is, ==, hash,set去重]都不一样
在现实中使用是有问题的,比如这里面如果我们要比较两张牌则这两张应该看为相同,所以我们需要改写其eq方法,让其不基于id来达到目的
#改进版,重写eq与hash
class Card2:
insure= False
def __init__( self, rank, suit, hard, soft ):
self.rank= rank
self.suit= suit
self.hard= hard
self.soft= soft
def __repr__( self ):
return "{__class__.__name__}(suit={suit!r}, rank={rank!r})".format(__class__=self.__class__, **self.__dict__)
def __str__( self ):
return "{rank}{suit}".format(**self.__dict__) def __eq__( self, other ):
return self.suit == other.suit and self.rank == other.rank def __hash__( self ):
return hash(self.suit) ^ hash(self.rank) class AceCard2( Card2 ):
insure= True
def __init__( self, rank, suit ):
super().__init__( "A", suit, 1, 11 ) c1 = AceCard2( 1, '?' )
c2 = AceCard2( 1, '?' ) print(id(c1),id(c2))
print(id(c1)/16,id(c2)/16)
print(hash(c1),hash(c2)) #变为相等的数字,但是需要注意的是已经不是 id/16
print(c1==c2) #变为True
print(c1 is c2)
print( set([c1,c2]) )
重写__hash__与__eq__
对于mutable object,在这里依然用card做示范,但其实是不贴切的,card应该是immutable的。注意hash返回None的写法
class Card3:
insure= False
def __init__( self, rank, suit, hard, soft ):
self.rank= rank
self.suit= suit
self.hard= hard
self.soft= soft
def __repr__( self ):
return "{__class__.__name__}(suit={suit!r}, rank={rank!r})".format(__class__=self.__class__, **self.__dict__)
def __str__( self ):
return "{rank}{suit}".format(**self.__dict__) def __eq__( self, other ):
return self.suit == other.suit and self.rank == other.rank
# and self.hard == other.hard and self.soft == other.soft __hash__ = None #!!!!!!!!! class AceCard3( Card3 ):
insure= True
def __init__( self, rank, suit ):
super().__init__( "A", suit, 1, 11 ) c1 = AceCard3( 1, '?' )
c2 = AceCard3( 1, '?' ) print(id(c1),id(c2))
print(id(c1)/16,id(c2)/16)
print(hash(c1),hash(c2)) #报错:TypeError: unhashable type: 'AceCard3'
print(c1==c2) #True
print(c1 is c2)
print( set([c1,c2]) ) #报错:TypeError: unhashable type: 'AceCard3',由于不能hash,自然不能用于set数据结构
mutable object的__hash__与_eq__
对于mutable object,若想对其实例进行数值分析,
可以写一个immutable的子类,将实例传入后完全copy下来,再对copy份进行写hash处理:
如下面的Hand类,可以写一个不可变的FrozenHand类来对其进行hash数值处理
class Hand:
def __init__( self, dealer_card, *cards ):
self.dealer_card= dealer_card
self.cards= list(cards)
def __str__( self ):
return ", ".join( map(str, self.cards) )
def __repr__( self ):
return "{__class__.__name__}({dealer_card!r}, {_cards_str})".format(__class__=self.__class__,_cards_str=", ".join( map(repr, self.cards) ),**self.__dict__ )
def __eq__( self, other ):
return self.cards == other.cards and self.dealer_card ==other.dealer_card
__hash__ = None import sys
class FrozenHand( Hand ):
def __init__( self, *args, **kw ):
if len(args) == 1 and isinstance(args[0], Hand):
# Clone a hand
other= args[0]
self.dealer_card= other.dealer_card
self.cards= other.cards
else:
# Build a fresh hand
super().__init__( *args, **kw )
def __hash__( self ):
h= 0
for c in self.cards:
h = (h + hash(c)) % sys.hash_info.modulus
return h stats = defaultdict(int)
d= Deck() #Deck是一堆牌
h = Hand( d.pop(), d.pop(), d.pop() )
h_f = FrozenHand( h )
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
#对于Deck类,添加__bool__方法:
def __bool__( self ):
return bool( self._cards ) #如果是继承 list 类,可能如下书写:
def __bool__( self ):
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)
class BlackJackCard_p:
def __init__( self, rank, suit ):
self.rank= rank
self.suit= suit def __lt__( self, other ):
print( "Compare {0} < {1}".format( self, other ) )
return self.rank < other.rank def __str__( self ):
return "{rank}{suit}".format( **self.__dict__ ) >>> two = BlackJackCard_p( 2, '?' )
>>> three = BlackJackCard_p( 3, '?' )
>>> two < three
Compare 2? < 3? (*)
True
>>> two > three (*)
Compare 3? < 2?
False
>>> two == three
False
>>> two <= three
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: BlackJackCard_p() <= BlackJackCard_p()
用扑克牌来测试__lt__
由(*)看出: two < three的比较时用的是two.__lt__(three)
two>three由于没有__gt__(),用的是three.__lt__(two)
由于这种机制,我们可以只提供四种方法来包含上面六种:
__eq__(), __ne__(), __lt__(), __le__(). 1:同类实例比较
class BlackJackCard:
def __init__( self, rank, suit, hard, soft ):
self.rank= rank
self.suit= suit
self.hard= hard
self.soft= soft
def __lt__( self, other ):
if not isinstance( other, BlackJackCard ):
return NotImplemented
return self.rank < other.rank
def __le__( self, other ):
try:
return self.rank <= other.rank
except AttributeError:
return NotImplemented
def __gt__( self, other ):
if not isinstance( other, BlackJackCard ):
return NotImplemented
return self.rank > other.rank
def __ge__( self, other ):
if not isinstance( other, BlackJackCard ):
return NotImplemented
return self.rank >= other.rank
def __eq__( self, other ):
if not isinstance( other, BlackJackCard ):
return NotImplemented
return self.rank == other.rank and self.suit == other.suit #比较==时多了对于suit的检查,而比较大小时只比较了rank
def __ne__( self, other ):
if not isinstance( other, BlackJackCard ):
return NotImplemented
"""
注意其上实现的六个比较方法中有两种检验方式:
1:explicit的用isinstance检验是不是BlackJackCard的类实例
2:implicit的用try语句,此种除非是某个类刚好有rank属性才会发生比较,
实际上第二种方法更好,因错出现刚好有rank属性的类又用来比较的概率十分小,而可以用来扩展为别的纸牌游戏的牌与之的比较
"""
>>> two = card21( 2, '?' )
>>> three = card21( 3, '?' )
>>> two_c = card21( 2, '?' ) >>> two == two_c
False
>>> two.rank == two_c.rank
True
>>> two < three
True
>>> two_c < three
True
>>> two < 2 #报错是因为__lt__()方法用isinstance检验了类型,非同类就报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Number21Card() < int()
>>> two == 2 #此地没有报错是因为遇到NotImplemented,python会交换他们.在此即是变成int.__eq__()
False
同类实例比较的实现
2:异类实例比较
#下面主要是用isinstance来判断相应的可能的异类的类型,再做处理
class Hand:
def __init__( self, dealer_card, *cards ):
self.dealer_card= dealer_card
self.cards= list(cards)
def __str__( self ):
return ", ".join( map(str, self.cards) )
def __repr__( self ):
return "{__class__.__name__}({dealer_card!r}, {_cards_str})".format(__class__=self.__class__,_cards_str=", ".join( map(repr, self.cards) ),**self.__dict__ )
def __eq__( self, other ):
if isinstance(other,int):
return self.total() == other
try:
return (self.cards == other.cards and self.dealer_card == other.dealer_card)
except AttributeError:
return NotImplemented
def __lt__( self, other ):
if isinstance(other,int):
return self.total() < other
try:
return self.total() < other.total()
except AttributeError:
return NotImplemented
def __le__( self, other ):
if isinstance(other,int):
return self.total() <= other
try:
return self.total() <= other.total()
except AttributeError:
return NotImplemented
__hash__ = None
def total( self ):
delta_soft = max( c.soft-c.hard for c in self.cards )
hard = sum( c.hard for c in self.cards )
if hard+delta_soft <= 21:
return hard+delta_soft
return hard >>> two = card21( 2, '?' )
>>> three = card21( 3, '?' )
>>> two_c = card21( 2, '?' )
>>> ace = card21( 1, '?' )
>>> cards = [ ace, two, two_c, three ]
>>> h= Hand( card21(10,'?'), *cards )
>>> print(h)
A?, 2?, 2?, 3?
>>> h.total()
18
>>> h2= Hand( card21(10,'?'), card21(5,'?'), *cards )
>>> print(h2)
5?, A?, 2?, 2?, 3?
>>> h2.total()
13
>>> h < h2
False
>>> h > h2
True
>>> h == 18
True
>>> h < 19
True
>>> h > 17
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Hand() > int()
异类实例的比较
五:immutable object的__new__
首先说明为何讲immutable object的__new__,因为mutable object可以直接在__init__中加入新参数而immutable object是不行的:
# 可变对象:
class cL(list):
def __init__(self,value,mutable):
super().__init__(value)
self.mutable = mutable
aha = cL([2,3,4],'mumu')
#注意此处的value参数,用help(list)可看到:list(iterable) -> new list initialized from iterable's items
print(aha,aha.mutable)
# 输出:[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)
class Float_Units( float ):
def __new__( cls, value, unit ):
obj= super().__new__( cls, value ) #实例化父类object,并用value初始化
obj.unit= unit #给父类object添加了属性unit,这样由于继承关系也就有了此属性
return obj #返回实例
speed= Float_Units( 6.5, "knots" )
print(speed)
print(speed.unit)
# 输出:
# 6.5
# knots 可见,能够赋值value以及添加属性unit
__new__为immutable object添加属性