第二章 與Python的悟性陳繼承----基本特殊方法.
python中有有一些特殊的方法,它們允許我們的類和python更好的集成
-
__repr__()
: __str__()
__format__()
__hash__()
__bool__()
__bytes__()
__lt__()
__le__()
__eq__()
__ne__()
__gt__()
__ge__()
__new__()
__del__()
2.1 __repr__()
和__str__()
方法
- 通常
str()
方法表示的對象對用戶更加友好.這個方法是有對象的__str__()
方法實現的.
什么時候重寫 __str__()
跟__repr__()
- 非集合對象: 一個不包括其他集合對象的'簡單'對象,這類對象格式通常不會特別復雜
- 集合對象:一個包括集合的對象,這類對象的格式化會非常復雜.
非集合對象的__repr__()
和__str__()
問題1: 什么是集合?
class Card(object):
def __init__(self, rank, suit):
self.suit = suit
self.rank = rank
self.hard, self.soft = self._points()
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 _points(self):
return int(self.rank), int(self.rank)
x = NumberCard('2', '?')
# 注意下面是重寫后的
print(str(x)) # 2?
print(x) # 2?
print(repr(x)) # NumberCard(suit = '?' , rank = '2')
__format__()
方法
注意:‘{0!r}'.format()
和'{0!s}'.format()
并不會調用__format__()
方法,它們會調用__repr__()
或者__str__()
.
__hash__()
方法
內置hash()
函數調用了__hash__()
方法.哈希是一種將復雜的值簡化為小整數的計算方式.
python有兩個哈希庫:
- hashlib
- zip 有兩個搞笑的哈希函數
adler32()
和crc32()
hasd()函數主要被用來創建set,forzenset,dict這些集合類的鍵.這些集合利用了不可變對象的哈希值來高效的查找集合中的對象
決定hash的對象
并非每個對象都需要提供一個哈希值,尤其是,當我們創建一個包含有狀態,可改變對象的類時.這類不應該返回哈希值,__hash__
的定義 應該是None
等價比較有三個層次:
- 哈希值相等:意味著兩個結果可能相等.哈希值是判斷兩個對象有可能相等的快捷方式,如果哈希值不同,兩個對象不可能相等,也不可能是同一個對象.
- 比較結果相等:意味著兩個對象的哈希值已經是相等的,這個比較用的是
==
運算符.如果結果相等,那么兩個對象的有可能是同一個. - IDD 相等:這意味著兩個對象是同一個對象,它們的哈希值仙童,并且使用
==
的比較結果相等,這個比較是用的是is運算符.
**基本哈希法(Fundametal Law of Hash):比較相等的對象的哈希值一定相等.
有關不可變對象和繼承的默認行為
class Card(object):
def __init__(self, rank, suit, hard, soft):
self.suit = suit
self.rank = rank
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(AceCard, self).__init__("A", suit, 1, 11)
class FaceCard(Card):
def __init__(self, rank, suit):
super(FaceCard, self).__init__({11: 'J',
12: 'Q',
13: 'K'}[rank], suit, 10, 10)
c1 = AceCard(1,'?')
c2 = AceCard(1,'?')
print(id(c1),id(c2)) # 52067024 52067120
id()
值不同意味著是不同的對象.
**is測試
基于id()
的值,哈希值根據id()
值來計算的`
重載不可變對象
下面是一個重載了__hash__()
和__eq__()
定義的簡單類.
class Card2(object):
def __init__(self, rank, suit, hard, soft):
self.suit = suit
self.rank = rank
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)) # id 是不相同的
print(c1 is c2) # False
print(hash(c1), hash(c2)) # hash是相同的
print(c1 == c2) # True
print(set([c1,c2])) # {AceCard2(suit = '?' , rank = 'A')}
重載可變對象
下面的類層級結構中,我們沖在了可變對象的 __hash__()
和__eq__()