国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

做企業(yè)網(wǎng)站必須要座機(jī)嗎聯(lián)盟營銷平臺

做企業(yè)網(wǎng)站必須要座機(jī)嗎,聯(lián)盟營銷平臺,宣傳網(wǎng)站制作方案,南京網(wǎng)站開發(fā)xuan南京樂識18. 繼承 和面向?qū)ο缶幊套畛O嚓P(guān)的語言特性就是繼承(inheritance). 繼承值得是根據(jù)一個現(xiàn)有的類型, 定義一個修改版本的新類的能力. 本章中我會使用幾個類來表達(dá)撲克牌, 牌組以及撲克牌性, 用于展示繼承特性.如果你不玩撲克, 可以在http://wikipedia.org/wiki/Poker里閱讀相關(guān)…

18. 繼承

和面向?qū)ο缶幊套畛O嚓P(guān)的語言特性就是繼承(inheritance).
繼承值得是根據(jù)一個現(xiàn)有的類型, 定義一個修改版本的新類的能力.
本章中我會使用幾個類來表達(dá)撲克牌, 牌組以及撲克牌性, 用于展示繼承特性.如果你不玩撲克, 可以在http://wikipedia.org/wiki/Poker里閱讀相關(guān)介紹, 但其實(shí)并不必要;
我會在書中介紹練習(xí)中所需知道的東西.本章的代碼示例可以從https://github.com/AllenDowney/ThinkPython2/blob/master/code/Card.py下載.
18.1 卡片對象
一副牌里有52張牌, 共有4個花色, 每種花色13, 大小各不相同.
花色有黑桃(Spade), 紅桃(Heart), 方片(Diamond)和草花(Club)(在橋牌中, 這幾個花色是降序排列的).
每種花色的13張牌分別為: Ace, 2, 3, 4, 5, 6, 7, 7, 9, 10, Jack, Queen, King.
根據(jù)你玩的不同游戲, Ace可能比King大, 可能比2.如果我們定義一個新對象來表示卡牌, 
則其屬性顯然應(yīng)該是rank(大小)和suit(花色). 但屬性的值就不那么直觀了.
一種可能是使用字符串, 例如: 'Spade'表示花色, 'Queen'表示大小.
這種實(shí)現(xiàn)的問題之一是比較大小和花色的高低時會比較困難.另一種方案是使用整數(shù)類給大小和花色'編碼'. 在這個語境中, '編碼'意味著我們要定義一個數(shù)字到花色,
或者數(shù)字到大小的隱射. 這種編碼并不意味著它是密碼(那樣就因該稱為'加密').例如, 下表展示了花色和對應(yīng)的整數(shù)編碼:
黑桃 Spades    --> 3
紅桃 Hearts    --> 2
方片 Diamonds  --> 1
草花 Clubs     --> 0
這個編碼令我們可以很容易地比較卡牌; 因?yàn)楦蟮幕ㄉ[射到更大的數(shù)字上, 我們可以直接使用編碼來比較花色.
卡牌大小的映射相當(dāng)明顯; 每個數(shù)字形式的大小映射到相應(yīng)的整數(shù)上, 而對于花牌:
Jack  --> 11
Queen --> 12
King  --> 13
我使用'->'符號, 是為了說明這些映射并不是Python程序的一部分,
它們是程序設(shè)計的一部分, 但并不在代碼中直接表現(xiàn).
Card類的定義如下:
class Card:"""Represents a standard playing card.(代表一張標(biāo)準(zhǔn)的撲克牌.)"""def __init__(self, suit=0, rank=2):# 花色默認(rèn)為草花self.suit = suit# 大小默認(rèn)為2 self.rank = rank
和前面一樣, init方法對每個屬性定義一個可選形參. 默認(rèn)的的卡牌是花草2.
要創(chuàng)建一個Card對象, 使用你想要的花色和大小調(diào)用Card:
queen_of_diaminds = Card(1, 13)
18.2 類屬性
為了能將Card對象打印成人門容易約定的格式, 我們需要將整數(shù)編碼映射成對應(yīng)的大小和花色.
自然地做法是使用字符串列表. 我們將這些列表賦值到'類屬性':
# 在Card類里:# 花色名稱列表suit_names = ['Clubs', 'Diamonds', 'Hearts', 'Spades']# 大小名稱列表rank_names = [None, 'Ace', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King']def __str__(self):return '%s of %s' % (Card.rank_names[self.rank], Card.suit_names[self.suit])
suit_names和rank_names這樣的變量, 定義在類之中, 
但在任何方法之外的我們成為類屬性, 因?yàn)樗鼈兪呛皖悓ο驝ard相關(guān)聯(lián)的.這個術(shù)語和suit與rank之類的變量相區(qū)別. 
那些稱為'示例屬性', 因?yàn)樗麄兪呛鸵粋€特定的實(shí)例相關(guān)聯(lián)的.兩種屬性都是使用句點(diǎn)表示法訪問.
例如, 在__str__中, self是一個Card對象, 而self.rank是它的大小.
相似地, Card是一個類對象, 而Card.rank_names是關(guān)聯(lián)到這個類的一個字符串列表.每個卡牌都有它自己的suit和rank, 但總共只有一個suit_names和rank_names.綜合起來, 表達(dá)式Card.rank_names[self.rank]意思是
'使用對象self的屬性rank作為索引, 從類Card的列表rank_names中選擇對應(yīng)的字符串'.rank_names的第一個元素是None, 因?yàn)闆]有小大為0的卡牌. (讓索引對齊數(shù)字, 更加直觀.)
因?yàn)槭褂肗one占據(jù)了一個位置, 我們就可以得到從下標(biāo)2到字符串'2'這樣整齊的映射. 
如果要避免這種操作, 可以使用字典而不是列表.利用現(xiàn)有的方法, 可以創(chuàng)建并打印開牌:
>>> card1 = Card(2, 11)
>>> print(card1)
Jack of Hearts
# 完整代碼
class Card:"""Represents a standard playing card.(代表一張標(biāo)準(zhǔn)的撲克牌.)"""# 花色名稱列表suit_names = ['Clubs', 'Diamonds', 'Hearts', 'Spades']# 大小名稱列表rank_names = [None, 'Ace', '2', '3', '4', '5', '6', '7','8', '9', '10', 'Jack', 'Queen', 'King']def __init__(self, suit=0, rank=2):# 花色默認(rèn)為草花self.suit = suit# 大小默認(rèn)為2self.rank = rankdef __str__(self):return '%s of %s' % (Card.rank_names[self.rank], Card.suit_names[self.suit])card1 = Card(2, 11)
print(card1)  # Jack of Hearts
18-1展示了Card類對象和一個Card實(shí)例.
Card是一個類對象, 所以它的類型是type. card1的類型是Card. 
為了節(jié)省空間, 我沒有畫出suit_names, rank_names的內(nèi)容.

2023-04-22_00001

18.3 對比卡牌
對應(yīng)內(nèi)置類型, 我們比較操作符(<, >, ==)來比較對象并決定哪一個更大, 更小, 或者相等.
對應(yīng)用戶定義類型, 我們可以通過提供一個方法__lt__, 代碼'less than'. 來重載內(nèi)置操作符的行為.__lt__接收兩個形參, self和other, 當(dāng)?shù)谝粋€對象嚴(yán)格小于第二個對象時返回True.卡牌的正確順序并不顯而易見. 例如, 草花3和方塊2哪個更大?
一個排面數(shù)大, 另一個花色大. 為了比較卡牌, 需要決定大小和花色哪個更重要.這個問題的答案取決去你在玩哪種牌類游戲, 
但為了簡單起見, 我們隨意做一個決定, 認(rèn)為花色更重要, 于是, 所有的黑桃比所有的方片都大, 依次類推.這一點(diǎn)決定后, 我們就可以編寫__lt__函數(shù):
# 在Card類里:def __lt__(self, other):# 檢查花色if self.suit < other.suit:return Trueif self.suit > other.suit:return False# 花色相同, 檢查大小return self.rank < other.rank
使用元組比較, 可以寫得更緊湊:
# 在Card類里:def __lt__(self, other):t1 = self.suit, self.rankt2 = other.suit, other.rank# 元組作比較, 先對第一個元素做比較, 如果相同, 再對第二個元素作比較.return t1 < t2
作為練習(xí), 為時間對象編寫一個__lt__方法.
你可以使用元組比較(, , ), 也可以考慮使用整數(shù)比較(時間對象轉(zhuǎn)為十進(jìn)制秒數(shù)).
class Time:# 初始化對象.def __init__(self, name, hour=0, minute=0, second=0):self.name = nameself.hour = hourself.minute = minuteself.second = seconddef __lt__(self, other):print(self.name, other.name)t1 = self.hour, self.minute, self.secondt2 = other.hour, other.minute, other.secondreturn t1 > t2def main():t1 = Time('t1', 9, 45, 0)t2 = Time('t2')# 小于號 self是t1print(t1 < t2)# 大于號 self是t2print(t1 > t2)if __name__ == '__main__':main()
現(xiàn)在我們已經(jīng)有了卡牌(card), 下一步就是定義牌組(deck).
由于牌組是由卡牌組成的, 很自然地, 每個Deck對象因該有一個屬性包含卡牌的列表.
class Deck:def __init__(self):self.cards = []# 花色for suit in range(4):# 大小for rand in range(1, 14):# 一共抽 4 * 13 = 52張卡牌.card = Card(suit, rand)self.cards.append(card)
填充牌組最簡單的辦法是使用嵌套循環(huán).
外層循環(huán)從03遍歷各個花色. 內(nèi)層循環(huán)從113遍歷開牌大小.
每次迭代使用當(dāng)前的花色和大小創(chuàng)建一個新的Card對象, 并將它添加到self.cards中.
18.5 打印牌組
下面是一個Deck的一個__str__方法:
# 在Deck類里:def __str__(self):res = []for card in self.cards:res.append(str(card))return '\n'.join(res)
這個方案展示了一種累積構(gòu)建大字符串的方法: 先構(gòu)建一個字符串的列表, 再使用字符串方法join.
內(nèi)置函數(shù)str會對每個卡牌對每個卡牌對象調(diào)用__str__方法并返回字符串表達(dá)形式.
( str(卡牌對象), 會調(diào)用卡牌對象的__str__方法.)由于我們對一個換行符調(diào)用join函數(shù), 卡片之間用換行分隔.
下面是打印的結(jié)果:
>>> deck = Deck()
>>> print(deck)
Ace of Clubs
2 of Clubs
3 of Clubs
...
10 of Spades
Jack of Spades
Queen of Spades
King of Spades
雖然結(jié)果顯示了52, 它任然是一個包含換行符的字符串.
# 完整代碼
# 完整代碼
class Card:"""Represents a standard playing card.(代表一張標(biāo)準(zhǔn)的撲克牌.)"""# 花色名稱列表suit_names = ['Clubs', 'Diamonds', 'Hearts', 'Spades']# 大小名稱列表rank_names = [None, 'Ace', '2', '3', '4', '5', '6', '7','8', '9', '10', 'Jack', 'Queen', 'King']def __init__(self, suit=0, rank=2):# 花色默認(rèn)為草花self.suit = suit# 大小默認(rèn)為2self.rank = rankdef __str__(self):return '%s of %s' % (Card.rank_names[self.rank], Card.suit_names[self.suit])def __lt__(self, other):t1 = self.suit, self.rankt2 = other.suit, other.rank# 元組作比較, 先對第一個元素做比較, 如果相同, 再對第二個元素作比較.return t1 < t2class Deck:# 初始化牌組對象, 按順序創(chuàng)建52張卡牌.def __init__(self):self.cards = []# 花色for suit in range(4):# 大小for rand in range(1, 14):# 一共抽 4 * 13 = 52張卡牌.card = Card(suit, rand)self.cards.append(card)def __str__(self):res = []for card in self.cards:res.append(str(card))return '\n'.join(res)deck = Deck()
print(deck)
18.6 添加,刪除,洗牌和排序
為了能夠發(fā)牌, 我們需要一個方法從牌組中抽取一張牌并返回.
列表方法pop為此提供了一個方便的功能:
# 在Deck類里def pop_card(self):return self.cards.pop()
由于pop從列表中抽出最后一張牌, 我們其實(shí)從牌組的低端發(fā)牌的.
要添加一個卡牌, 我們可以使用列表方法append:
# 在Deck類里:def add_card(self, card):self.cards.append(card)
像這樣調(diào)用另一個方法, 卻不做其他更多工作的方法, 有時候稱為一個'飾面(veneer)'.
這個比喻來自于木工行業(yè), 在木工行業(yè)飾面是為了改善外觀而粘貼到便宜的木料表面的薄薄的一層優(yōu)質(zhì)木料.
(名字很高大尚, 里面沒什么..)
在這個例子里, add_card是一個'薄薄'的方法, 用更適合牌組的術(shù)語來表達(dá)一個列表操作.
它改善了實(shí)現(xiàn)的外觀(或接口).作為另一個示例, 我們可以使用random模塊的函數(shù)shuffle來編寫一個Deck方法shuffle(洗牌):
# 在Deck類里def shuffle(self):random.shuffle(self.cards)
不要忘記導(dǎo)入random模塊.
作為練習(xí), 編寫一個Deck方法sort, 使用列表方法sord來對一個Deck中的卡牌進(jìn)行排序.
sort使用我們定義的__lt__方法來決定順序.
后面這句話的解釋:
在Python中, sort方法用于對列表進(jìn)行排序. 
默認(rèn)情況下, sort方法會按照元素的大小順序來排序, 而對于用戶自定義的類,
如果想要使用sort方法進(jìn)行排序, 需要定義該類的比較方法.在本例中, 我們定義了Card類, 并在其中實(shí)現(xiàn)了__lt__方法, 該方法用于比較兩張卡牌的大小.
當(dāng)我們調(diào)用sort方法對Deck中的卡牌進(jìn)行排序時, sort方法會自動調(diào)用Card類中的__lt__方法來比較卡牌的大小,
從而實(shí)現(xiàn)對卡牌的排序.因此, 我們可以說, sort方法使用了我們定義的__lt__方法來決定卡牌的順序.
# 在Deck類里def sort(self):"""按升序排列卡片."""self.cards.sort()
18.7 繼承
繼承是一個能夠定義一個新類對現(xiàn)有的某個類稍作修改的語言特性.
作為示例, 假設(shè)我們想要一個類來表達(dá)一副'手牌', 即玩家手握的一副牌.
一副手牌和一套牌組相似: 都是由卡牌的集合組成, 并且都需要諸如增加和移除卡牌的操作.一副手牌和一套牌組也有區(qū)別: 我們期望手牌擁有的一些操作, 對牌組來說并無意義.
例如, 在撲克牌中, 我們可能想要比較兩副手牌來判斷誰獲勝了.
在橋牌中, 我們可能需要為一副手牌計算分?jǐn)?shù)以叫牌.這種類之間的關(guān)系--相似, 但不相同--讓它稱為繼承.
要定義一個繼承現(xiàn)有類的新類, 可以把現(xiàn)有類的名稱放在括號之中:
class Hand(Deck):"""Represents a hand of playing cards."""
這個定義說明Hand從Deck繼承而來;
這意味著我們可以像Deck對象那樣在Hand對象上使用pop_card和add_card方法.當(dāng)你類繼承現(xiàn)有類時, 現(xiàn)有的類被稱為'父類(parent)', 而新類則稱為'子類(child)'.在本例中, Hand也會繼承Deck的__init__方法, 但它和我們想要的并不一樣:
我們不需要填充52張卡牌, Hand的init方法應(yīng)當(dāng)初始化cards為一個空列表.如果我們?yōu)镠and類提供了一個init方法, 它會覆蓋Deck類的方法:
# 在Head類里:def __init__(self, lable=''):self.cards = []self.lable = lable
在創(chuàng)建Hand對象時, Python會調(diào)用這個init方法而不是Deck中的那個:
>>> hand = Hand('new hand')
>>> hand.cards
[]
>>> hand.label
'new hand'
其他的方法是從Deck中繼承而來的, 所以我們可以使用pop_card和add_cards來出牌.
>>> deck = Deck()
# 牌組出一張牌
>>> card = deck.pop_card()
# 手牌添加這張牌
>>> hand.add_card(card)
# 打印這張牌
print(hand)
King of Spades
下一步很自然地就是將這段代碼封裝起來成為一個方法move_cards:
# 在Deck類里:def move_cards(self, hand, num):# 發(fā)多張牌for i in range(num):hand.add_card(self.pop_card())
# 完整代碼
# 完整代碼
import randomclass Card:"""Represents a standard playing card.(代表一張標(biāo)準(zhǔn)的撲克牌.)"""# 花色名稱列表suit_names = ['Clubs', 'Diamonds', 'Hearts', 'Spades']# 大小名稱列表rank_names = [None, 'Ace', '2', '3', '4', '5', '6', '7','8', '9', '10', 'Jack', 'Queen', 'King']def __init__(self, suit=0, rank=2):# 花色默認(rèn)為草花self.suit = suit# 大小默認(rèn)為2self.rank = rankdef __str__(self):return '%s of %s' % (Card.rank_names[self.rank], Card.suit_names[self.suit])def __lt__(self, other):t1 = self.suit, self.rankt2 = other.suit, other.rank# 元組作比較, 先對第一個元素做比較, 如果相同, 再對第二個元素作比較.return t1 < t2class Deck:def __init__(self):self.cards = []# 花色for suit in range(4):# 大小for rand in range(1, 14):# 一共抽 4 * 13 = 52張卡牌.card = Card(suit, rand)self.cards.append(card)def __str__(self):res = []for card in self.cards:res.append(str(card))return '\n'.join(res)# 發(fā)牌def pop_card(self):return self.cards.pop()# 添加卡牌def add_card(self, card):self.cards.append(card)# 洗牌def shuffle(self):random.shuffle(self.cards)# 排序def sort(self):"""按升序排列卡片."""self.cards.sort()def move_cards(self, hand, num):# 發(fā)多張牌for i in range(num):hand.add_card(self.pop_card())class Hand(Deck):"""Represents a hand of playing cards.""""""當(dāng)子類未顯式調(diào)用父類的__init__()方法時, 會在子類提示該提示:Call to __init__ of super class is missed代碼并沒有錯誤, 只是提示用戶不要忘記調(diào)用父類初始化方法."""def __init__(self, lable=''):self.cards = []self.lable = labledef main():# 實(shí)例一個牌組對象deck = Deck()# 實(shí)例手牌對象, 起一個名稱hand = Hand('new hand')# 從牌組末尾發(fā)13張牌到手上.deck.move_cards(hand, 13)# 查看手上的牌(所有的黑桃牌)for card in hand.cards:print(card)if __name__ == '__main__':main()
move_cards接收兩個參數(shù), 一個Hand對象以及需要出牌的牌數(shù). 它會修改seld和hand, 返回None.有的情況下, 卡牌會從一副手牌中移除轉(zhuǎn)入到另一副手牌中, 或者從手牌中回到牌組.
你可以使用move_cards來處理全部這些操作: self即可以是一個Deck對象, 也可以是一個Hand對象.
而hand參數(shù), 雖然名字是hand卻也可以是一個Deck對象.
繼承是很有用的語言特性.
有些程序不用繼承些, 會有很多重復(fù)代碼, 使用繼承后就會更加優(yōu)雅.
繼承也能促進(jìn)代碼復(fù)用, 因?yàn)槟憧梢栽诓恍薷母割惖那疤嵯聦λ男袨檫M(jìn)行定制化.
有的情況喜愛, 繼承結(jié)構(gòu)反映了問題的自然結(jié)構(gòu), 所以也讓設(shè)計更容易理解.但另一方面, 繼承也可能會讓代碼更難讀.
有時候當(dāng)一個方法被調(diào)用時, 并不清楚到哪里能找到它的定義.
相關(guān)的代碼可能散布在幾個不同的模塊中.
并且, 很多可以用繼承實(shí)現(xiàn)的功能, 也能不用它實(shí)現(xiàn), 甚至可以實(shí)現(xiàn)得更好.
18.8 類圖
至此我們已見過用于顯示程序狀態(tài)的棧圖, 以及用于顯示對象的屬性值的對象圖.
這些圖表展示了程序運(yùn)行中的一個快照, 所以當(dāng)程序繼續(xù)運(yùn)行時它們會跟著改變.它們也極其詳細(xì); 在某些情況下, 是過于詳細(xì)了. 而類圖對象結(jié)構(gòu)的展示相對來說更加抽象.
它不會具體顯示每個對象, 而是顯示各個類以及它們之間的關(guān)聯(lián).類之間有下面幾種關(guān)聯(lián).
* 一個類的對象可以包含其他類的對象的引用. 例如, 米格Rectangle對象都包含一個到Point對象的引用, 而每一個Deck對象包含到很多Card對象的引用.這種關(guān)聯(lián)稱為'HAS-A(有一個)', 也就是說, '矩形(rectangle)中有一個點(diǎn)(Point)'.* 一個類可能繼承自另一個類. 這種關(guān)系稱為IS-A(是一個), 也就是說'一副手牌(Hand)是一個牌組(Deck)'.* 一個類可能依賴于另一個類. 也就是說, 一個類的對象接收另一個類的對象作為參數(shù), 或者使用另一個類的對象來進(jìn)行某種計算.這種關(guān)系稱為'依賴(dependency)'.類圖用圖形展示了這些關(guān)系. 例如, 下圖展示了Card, Deck和Hand之間的關(guān)系.

2023-04-24_00001

空心三角形箭頭的線代表著一個IS-A關(guān)系; 這里表示Head是繼承自Deck的.標(biāo)準(zhǔn)的箭頭表示HAS-S關(guān)系; 這里表示Deck對象中用到Card對象的引用.箭頭附近的星號(*)表示是'關(guān)聯(lián)重復(fù)標(biāo)記'; 它表示Deck中有多個Cards.
這個數(shù)可以是一個簡單的數(shù)字, 52, 或者一個范圍, 5..7, 或者一個星號, 表示Deck可以有任意數(shù)量的Card引用.上圖中沒有任何依賴關(guān)系. 依賴關(guān)系通常使用虛線箭頭表示.
或者, 如果有太多的依賴, 有時候會忽略它們.更詳細(xì)的圖可能會顯示出Deck對象實(shí)際上包含了一個Card的列表.
但在類圖中, 像列表, 字典這樣的內(nèi)置類型通常是不顯示的.
18.9 數(shù)據(jù)封裝
前幾章展示了一個我們可以稱為'面向?qū)ο笤O(shè)計'的開發(fā)計劃.
我們發(fā)現(xiàn)需要的對象, 如Point, Rectangle和Time并定義類來表達(dá)它們.
每個類都是一個對象到現(xiàn)實(shí)世界(或者最少是數(shù)學(xué)世界)中某種實(shí)體的明顯對應(yīng).但有時候你到底需要哪些對象, 它們?nèi)绾谓换?/span>, 并不那么顯而易見.
這時候你需要另一種開發(fā)計劃. 
和之前我們通過封裝和泛化來發(fā)現(xiàn)函數(shù)接口的方式相同, 我們可以通過'數(shù)據(jù)封裝'來發(fā)現(xiàn)類的接口.13.8節(jié)提供了一個很好的示例.如果從↓下載我的代碼.
https://github.com/AllenDowney/ThinkPython2/blob/master/code/markov.py
你會發(fā)現(xiàn)它使用了兩個全局變量(suffix_map和prefix)并且在多個函數(shù)中進(jìn)行讀寫.
suffix_map = {}
prefix = ()
因?yàn)檫@些變量是全局的, 我們每次只能運(yùn)行一個分析.
如果讀入兩個文本, 它們的前綴和后綴就會添加到相同的數(shù)據(jù)結(jié)構(gòu)中(最后可以用來產(chǎn)生一些有趣的文本).若要多此運(yùn)行分析, 并保證他們之間的獨(dú)立, 我們可以將每次分析的狀態(tài)信息封裝成一個對象.
下面是它的樣子:
class Markov:def __init__(self):self.suffix_map = {}self.prefix = ()
接下來我們將那些函數(shù)轉(zhuǎn)換為方法. 
例如, 下面是process_word:
def process_word(self, word, order=2):if len(self.prefix) < order:self.prefix += (word,)returntry:self.suffix_map[self.prefix].append(word)except:# 如果前綴不存在, 創(chuàng)建一項(xiàng)self.suffix_map[self.prefix] = [word]self.prefix = shift(self.prefix, word)
像這樣轉(zhuǎn)換程序--修改設(shè)計單不修改其行為--是重構(gòu)(參見4.7節(jié))的另一個示例.
這個例子給出了一個設(shè)計對象和方法的開發(fā)計劃.
* 1. 從編寫函數(shù), (如果需要的話)讀寫全局變量開始.
* 2. 一旦你的程序能夠正確運(yùn)行, 查看全局變量與使用它們的函數(shù)的關(guān)聯(lián).
* 3. 將相關(guān)的變量封裝成對象的屬性.
* 4. 將相關(guān)的函數(shù)轉(zhuǎn)換為這個新類的方法.
作為練習(xí), 從↓下載我的Markov代碼, 并按照上面描述的步驟將全局變量封裝為一個叫作Markov的新類的屬性.
https://github.com/AllenDowney/ThinkPython2/blob/master/code/markov.py
解答: https://github.com/AllenDowney/ThinkPython2/blob/master/code/Markov.py (注意M是大寫的).
解答使用這個: https://github.com/AllenDowney/ThinkPython2/blob/master/code/markov2.py
import sys
import random# global variables
suffix_map = {}  # map from prefixes to a list of suffixes
prefix = ()  # current tuple of wordsdef process_file(filename, order=2):# 實(shí)例文件對象fp = open(filename)# 跳過開頭skip_gutenberg_header(fp)# 跳過結(jié)尾for line in fp:if line.startswith('*** END OF THIS'):break# 將每一行的末尾\n去除, 再按空格切分得到單詞列表. 最后遍歷單詞.for word in line.rstrip().split():# 基于馬爾可夫分析process_word(word, order)# 跳過開頭
def skip_gutenberg_header(fp):for line in fp:if line.startswith('*** START OF THIS'):breakdef process_word(word, order=2):# 聲明prefix是全局的.global prefix# 將前綴元組填滿# 統(tǒng)計前綴元組的長度是否小于規(guī)定的前綴單詞長度if len(prefix) < order:#  如果小于則往元組內(nèi)添加單詞prefix += (word,)returntry:# 前綴為鍵, 單詞為值suffix_map[prefix].append(word)except KeyError:# 鍵不存在則新建項(xiàng)suffix_map[prefix] = [word]# 重新設(shè)置前綴prefix = shift(prefix, word)def random_text(n=100):# 從字典的鍵中隨機(jī)選一個開始的鍵start = random.choice(list(suffix_map.keys()))# 選出100個單詞for i in range(n):# 獲取這個鍵的后綴值, 如果沒有這, 則返回Nonesuffixes = suffix_map.get(start, None)# 后綴為None, 則從新執(zhí)行random_text在選一個鍵. n的次數(shù)需要減去1.if suffixes is None:# 執(zhí)行這句則不會執(zhí)行return下面幾行的代碼random_text(n - i)return# 隨機(jī)選擇一個后綴word = random.choice(suffixes)# 打印后綴print(word, end=' ')# 重新設(shè)置前綴start = shift(start, word)def shift(t, word):# 修改前綴, 前綴2改為前綴1, 單詞改為前綴2return t[1:] + (word,)# 參數(shù)1: 腳本路徑, 參數(shù)2: 打開的文件名, 參數(shù)3: 生成單詞文本的長度, 參數(shù)4: 前綴單詞個數(shù).
def main(script, filename='158-0.txt', n=100, order=2):try:# 如果用戶提供的是字符串參數(shù), 則可以使用int將純字符串的數(shù)字轉(zhuǎn)為整數(shù).n = int(n)order = int(order)# 如果發(fā)生錯誤, 提示使用方法不正確.except ValueError:print('Usage: %d filename [# of words] [prefix length]' % script)# try正常結(jié)束執(zhí)行下面的代碼.else:# 參數(shù)1: 文件名, 前綴單詞個數(shù)process_file(filename, order)# 隨機(jī)生成文本random_text(n)if __name__ == '__main__':# *sys.argv 獲取文件的地址 C:\Backup\Program\ThinkPython\t1\t1.pymain(*sys.argv)
import sys
import random# 跳過開頭
def skip_gutenberg_header(fp):for line in fp:if line.startswith('*** START OF THIS'):break# 重置前綴元組
def shift(t, word):return t[1:] + (word,)# 馬爾可夫類
class Markov:# 初始化def __init__(self):# 后綴字典self.suffix_map = {}# 前綴元組self.prefix = ()# 讀取文件的過程def process_file(self, filename, order):# 實(shí)例文件對象fp = open(filename)# 跳過開頭skip_gutenberg_header(fp)# 跳過結(jié)尾for line in fp:if line.startswith('*** END OF THIS'):break# 將每一行的末尾\n去除, 再按空格切分得到單詞列表. 最后遍歷單詞.for word in line.rstrip().split():# 基于馬爾可夫分析self.process_word(word, order)# 分析單詞的過程def process_word(self, word, order):if len(self.prefix) < order:self.prefix += (word,)returntry:self.suffix_map[self.prefix].append(word)except:# 如果前綴不存在, 創(chuàng)建一項(xiàng)self.suffix_map[self.prefix] = [word]self.prefix = shift(self.prefix, word)def random_text(self, n=100):# 從字典的鍵中隨機(jī)選一個開始的鍵start = random.choice(list(self.suffix_map.keys()))# 選出100個單詞for i in range(n):# 獲取這個鍵的后綴值, 如果沒有這, 則返回Nonesuffixes = self.suffix_map.get(start, None)# 后綴為None, 則從新執(zhí)行random_text在選一個鍵. n的次數(shù)需要減去1.if suffixes is None:# 執(zhí)行這句則不會執(zhí)行return下面幾行的代碼self.random_text(n - i)return# 隨機(jī)選擇一個后綴word = random.choice(suffixes)# 打印后綴print(word, end=' ')# 重新設(shè)置前綴start = shift(start, word)def main(script, filename='emma.txt', n=100, order=2):try:# 如果用戶提供的是字符串參數(shù), 則可以使用int將純字符串的數(shù)字轉(zhuǎn)為整數(shù).n = int(n)order = int(order)# 如果發(fā)生錯誤, 提示使用方法不正確.except ValueError:print('Usage: %d filename [# of words] [prefix length]' % script)else:# 實(shí)例馬爾可夫?qū)ο?/span>markov = Markov()# 分析文本文件markov.process_file(filename, order)# 隨機(jī)生成文本markov.random_text(n)if __name__ == '__main__':main(*sys.argv)
18.10 調(diào)試
繼承會給調(diào)試帶來新的挑戰(zhàn), 因?yàn)楫?dāng)你調(diào)用對象的方法時, 可無法知道調(diào)用的到底是哪個方法.
假設(shè)你在編寫一個操作Hand對象的函數(shù). 你可能希望能夠處理所有類型的Hand, 如PokerHands, BridgeHands等.
如果你調(diào)用一個方法, 如shuffle(排序), 可能調(diào)用的是Decck中定義的方法, 到如果任何子類重載了這個方法,
則你調(diào)用的會是那個重載的版本.一旦你無法確認(rèn)程序的運(yùn)行流程, 最簡單的解決辦法是在相關(guān)的方法開頭添加一個打印語句.
如果Deck.shuffle打印一句Running Deck.shuffle這樣的信息, 則當(dāng)程序運(yùn)行時會跟蹤運(yùn)行的流程.或者, 你也可以使用下面這個函數(shù).
它接收一個對象和一個方法名(字符串形式), 并返回提供這個方法的定義的類:
def find_defining_class(obj, meth_name):for ty in type(obj).mro():if math_name in ty.__dict__:return ty
下面是使用的示例:
>>> hand = Hand()
>>> find_defining_class(hand, 'shuffle')
<class 'Card.Deck'>
所以這個Hand對象的shuffle方法是在Deck類中定義的那個.find_defining_class使用mro方法來獲得用于搜索調(diào)用方法的類對象(類型)列表.
'MRO'意思是'method resolution order'(方法查找順序), 是Python解析方法名稱的時候搜索的類的順序.一個設(shè)計建議: 每次重載一個方法時, 新方法的接口應(yīng)當(dāng)和舊方法的一致.
它應(yīng)當(dāng)接收相同的參數(shù), 返回相同的類型, 并服從同樣的前置條件與后置條件.
如果遵循這個規(guī)則, 你會發(fā)現(xiàn)任何為如Deck這樣的父類設(shè)計的函數(shù),
都可以使用Hand或PokerHand這樣的子類的實(shí)例.如果你破壞這個也稱為'Liskov替代原則'的規(guī)則, 你的代碼可能會像一堆(不好意思)紙牌屋一樣崩塌.
18.11 術(shù)語表
編碼(encode): 使用一個集合的值來表示另一個集合的值, 需要在它們之間建立映射.類屬性(class attribute): 關(guān)聯(lián)到類對象上的屬性. 類屬性定義在類定義之中, 但在所有方法定義之外.實(shí)例屬性(instance attribute): 和類的實(shí)例關(guān)聯(lián)的屬性.飾面(veneer): 一個方法或函數(shù), 它調(diào)用另一個函數(shù), 卻不做其他計算, 只是為了提供不同的接口.繼承(inheritance): 可以定義一個新類, 它是一個現(xiàn)有的類的修改版本.父類(parent class): 被子類所繼承的類.子類(child class): 通過繼承一個現(xiàn)有的類來創(chuàng)建的新類, 也叫作'subclass'.IS-A關(guān)聯(lián)(IS-A relationship): 子類個父類之間的關(guān)聯(lián).HAS-A關(guān)聯(lián)(HAS-A relationship): 連個類之間的一種關(guān)聯(lián): 一個類包含另一個類的對象的引用.依賴(dependency): 兩個類之間的一種關(guān)聯(lián). 一個類的實(shí)例使用另一個類的實(shí)例, 但不把它們作為屬性存儲起來.類圖(class diagram): 用來展示程序中的類以及它們之間的關(guān)聯(lián)的圖.重數(shù)(multiplicity): 類圖中的一種標(biāo)記方法, 對于HAS-A關(guān)聯(lián), 用來表示一個類中有多少對另一個類的對象的引用.數(shù)據(jù)封裝(data encapsulation): 一個程序開發(fā)計劃. 先使用全局變量來進(jìn)行原型設(shè)計, 然后將全局變量轉(zhuǎn)換為實(shí)例屬性做出最終版本.
18.12 練習(xí)
1. 練習(xí)1
針對下面的程序, 畫一張UML類圖, 展示這些類以及它們之間的關(guān)聯(lián):
UML是什么?
統(tǒng)一建模語言(Unified Modeling Languag, UML)是一種為面向?qū)ο笙到y(tǒng)的產(chǎn)品進(jìn)行說明,
可視化和編制文檔的一種標(biāo)準(zhǔn)語言, 是非專利的第三代建模和規(guī)約語言.
UML是面向?qū)ο笤O(shè)計的建模工具, 獨(dú)立于任何具體程序設(shè)計語言.
class PingPongParent:passclass Ping(PingPongParent):def __init__(self, pong):self.pong = pongclass Pong(PingPongParent):def __init__(self, pings=None):if pings is None:self.pings = []else:self.pings = pingsdef add_ping(self, ping):self.pings.append(ping)pong = Pong()
ping = Ping(pong)
pong.add_ping(ping)
這些類之間的關(guān)聯(lián)可以用以下方式表示:* 1. Ping類IS-A PingPongParent類, 即Ping類是PingPongParent類的子類.
* 2. Pong類IS-A PingPongParent類, 即Pong類也是PingPongParent類的子類.
* 3. Ping類HAS-A Pong類的實(shí)例, 即Ping類具有一個名為pong的屬性, 保存一個Pong實(shí)例的引用.
* 4. Pong類HAS-A Ping類的實(shí)例列表, 即Pong類具有一個名為pings的屬性, 保存多個Ping實(shí)例的列表.(目前只有一個, 則不使用*.)綜上所述, Ping類和Pong類之間存在HAS-A關(guān)系, 
而Ping類和PingPongParent類以及Pong類和PingPongParent類之間存在IS-A關(guān)系.

2023-04-25_00001

2. 練習(xí)2
編寫一個名稱deal_hands的Deck方法, 接收兩個形參: 手牌的數(shù)量以及每副手牌的牌數(shù).
它會根據(jù)形參創(chuàng)建新的Head對象, 按照每副手牌的牌數(shù)出牌, 并返回一個Hand對象列表.
(意思就是, 發(fā)幾個人牌, 每一副牌多少張.)
# 完整代碼
# 完整代碼
import randomclass Card:"""Represents a standard playing card.(代表一張標(biāo)準(zhǔn)的撲克牌.)"""# 花色名稱列表suit_names = ['Clubs', 'Diamonds', 'Hearts', 'Spades']# 大小名稱列表rank_names = [None, 'Ace', '2', '3', '4', '5', '6', '7','8', '9', '10', 'Jack', 'Queen', 'King']def __init__(self, suit=0, rank=2):# 花色默認(rèn)為草花self.suit = suit# 大小默認(rèn)為2self.rank = rankdef __str__(self):return '%s of %s' % (Card.rank_names[self.rank], Card.suit_names[self.suit])def __lt__(self, other):t1 = self.suit, self.rankt2 = other.suit, other.rank# 元組作比較, 先對第一個元素做比較, 如果相同, 再對第二個元素作比較.return t1 < t2class Deck:def __init__(self):self.cards = []# 花色for suit in range(4):# 大小for rand in range(1, 14):# 一共抽 4 * 13 = 52張卡牌.card = Card(suit, rand)self.cards.append(card)def __str__(self):res = []for card in self.cards:res.append(str(card))return '\n'.join(res)# 發(fā)牌def pop_card(self):return self.cards.pop()# 添加卡牌def add_card(self, card):self.cards.append(card)# 洗牌def shuffle(self):random.shuffle(self.cards)# 排序def sort(self):"""按升序排列卡片."""self.cards.sort()def move_cards(self, hand, num):# 發(fā)多少張牌for i in range(num):hand.add_card(self.pop_card())# 發(fā)牌 (手牌數(shù)量, 手牌牌數(shù))def deal_hands(self, hands, cards):# 手牌對象列表hands_list = []# 對52張牌進(jìn)行洗牌self.shuffle()# for循環(huán)創(chuàng)建多個手牌對象for i in range(int(hands)):hand = Hand()self.move_cards(hand, cards)hands_list.append(hand)return hands_listclass Hand(Deck):"""Represents a hand of playing cards.""""""當(dāng)子類未顯式調(diào)用父類的__init__()方法時, 會在子類提示該提示:Call to __init__ of super class is missed代碼并沒有錯誤, 只是提示用戶不要忘記調(diào)用父類初始化方法."""def __init__(self, lable=''):self.cards = []self.lable = labledef main(hands, cards):# 超出52張牌則提示輸入錯了:if hands * cards > 52:print('超出52張牌了!')return# 創(chuàng)建牌組對象deck = Deck()# 發(fā)牌hand_list = deck.deal_hands(4, 4)for index, hand in enumerate(hand_list):index += 1print('第%d個人的牌為:' % index)print(hand)if __name__ == '__main__':main(4, 4)
"""
TypeError: add_card() missing 1 required positional argument: 'card'
類型錯誤:添加卡()失蹤1所需的位置參數(shù):“卡”
"""
3. 練習(xí)3
下面列出的是撲克牌中可能的手牌, 按照牌值大小的增序(也是可能性的降序)排列.
* 對子(pair): 兩張牌大小相同.
* 兩對(two pair): 連個對子.
* 三條(three of a Kind): 三張牌大小相同.
* 順子(straight): 五張大小相連的牌. (Acc即可以是最大的也可以是最小, 所以Acc-2-3-4-5是順子, 10-Jack-Queen-King-Acc也是, 但Queen-King-Acc-2-3不是).
* 同花(flush): 五張牌花色相同.
* 滿堂紅(full house): 三張牌大小相同, 另外兩張牌大小相同.
* 四條(four of a Kind): 四張牌大小相同.
* 同花順(straight flush): 順子(如上面的定義)里的五張牌都是花色相同的.
本練習(xí)的目標(biāo)是預(yù)測這些手牌的出牌概率.
1. 從↓下載這些文件.
https://github.com/AllenDowney/ThinkPython2/blob/master/code/Card.py
https://github.com/AllenDowney/ThinkPython2/blob/master/code/PokerHand.py
* Card.py: 本章中介紹的Card, Deck和Hand類的完整代碼.
* PokerHand.py: 表達(dá)撲克手牌的一個類, 實(shí)現(xiàn)并不完整, 包含一些測試它的代碼.
import random# 卡牌類
class Card:# 花色列表suit_names = ["Clubs", "Diamonds", "Hearts", "Spades"]# 大小列表rank_names = [None, "Ace", "2", "3", "4", "5", "6", "7","8", "9", "10", "Jack", "Queen", "King"]# 初始化(默認(rèn)卡牌為紅色2)def __init__(self, suit=0, rank=2):self.suit = suitself.rank = rankdef __str__(self):# 打印對象時展示卡牌return '%s of %s' % (Card.rank_names[self.rank],Card.suit_names[self.suit])# 在兩個對象進(jìn)行 == 比較值的時候觸發(fā) __eq__() 的執(zhí)行def __eq__(self, other):# 比較花色與大小return self.suit == other.suit and self.rank == other.rank# 在兩個對象進(jìn)行 < 小于比較值的時候觸發(fā) __lt__() 的執(zhí)行def __lt__(self, other):t1 = self.suit, self.rankt2 = other.suit, other.rankreturn t1 < t2# 牌組對象
class Deck:# 生成52張牌def __init__(self):self.cards = []for suit in range(4):for rank in range(1, 14):card = Card(suit, rank)self.cards.append(card)# 打印牌組def __str__(self):res = []for card in self.cards:res.append(str(card))return '\n'.join(res)# 添加卡牌到牌組def add_card(self, card):self.cards.append(card)# 從牌組移除某張卡牌def remove_card(self, card):self.cards.remove(card)# 從牌組中彈出出一張牌def pop_card(self, i=-1):return self.cards.pop(i)# 洗牌def shuffle(self):random.shuffle(self.cards)# 排序def sort(self):self.cards.sort()# 發(fā)牌def move_cards(self, hand, num):for i in range(num):hand.add_card(self.pop_card())# 手牌
class Hand(Deck):def __init__(self, label=''):# 手牌列表self.cards = []# 手牌名稱self.label = labelif __name__ == '__main__':# 實(shí)例牌組對象deck = Deck()# 洗牌deck.shuffle()# 生成手牌對象hand = Hand()# 牌組發(fā)5張牌到手牌中deck.move_cards(hand, 5)# 排序hand.sort()# 查看手牌print(hand)
2. 如果你運(yùn)行PokerHand.py, 它會連出7組包含7張卡片的撲克手牌, 并檢查其中有沒有順子(因該是同花).在繼續(xù)之前請仔細(xì)閱讀代碼.
# PokerHand.py
from Card import Hand, Deck# 撲克手
class PokerHand(Hand):# 花色直方圖def suit_hist(self):# 構(gòu)建一個在手中出現(xiàn)的花色的直方圖.self.suits = {}#for card in self.cards:self.suits[card.suit] = self.suits.get(card.suit, 0) + 1def has_flush(self):# 構(gòu)建一個在手中出現(xiàn)的花色的直方圖.self.suit_hist()# 取出直方圖的值for val in self.suits.values():# 手牌中同一個花色有五張或大于五張則返回True.if val >= 5:return Truereturn Falseif __name__ == '__main__':# 實(shí)例牌組對象deck = Deck()# 洗牌deck.shuffle()# 循環(huán)實(shí)例7組手牌for i in range(7):#  PokerHand調(diào)用hand的初始化方法, 實(shí)例手牌對象.hand = PokerHand()# 牌組發(fā)7張牌到手牌中deck.move_cards(hand, 7)# 手牌排序hand.sort()# 查看手牌print(hand)# 手牌上是否有同花(五張牌花色相同)print(hand.has_flush())print('')
3. 在PokerHand.py中添加方法, has_pair(對子), has_twopair(兩對).它們根據(jù)手牌時候達(dá)到相對應(yīng)的條件來返回True或False.你的代碼應(yīng)當(dāng)對任意數(shù)量的手牌都適用(雖然最常見的手牌是5或者7).
from Card import Hand, Deck# 撲克手
class PokerHand(Hand):# 花色直方圖def suit_hist(self):# 構(gòu)建一個在手中出現(xiàn)的花色的直方圖.self.suits = {}for card in self.cards:self.suits[card.suit] = self.suits.get(card.suit, 0) + 1# 大小直方圖def rank_hist(self):self.ranks = {}for card in self.cards:self.ranks[card.rank] = self.ranks.get(card.rank, 0) + 1# 同花def has_flush(self):# 構(gòu)建一個在手中出現(xiàn)的花色的直方圖.self.suit_hist()# 取出直方圖的值for val in self.suits.values():# 手牌中同一個花色有五張或大于五張則返回True.if val >= 5:return Truereturn False# 對子def has_pair(self):self.rank_hist()# 取出直方圖的值for val in self.ranks.values():# 手牌中同一個花色有五張或大于五張則返回True.if val >= 2:return Truereturn False# 連對def has_twopair(self):# 對象計算pair_count = 0self.rank_hist()# 取出直方圖的值for val in self.ranks.values():# 手牌中同一個花色有五張或大于五張則返回True.if val >= 2:pair_count += 1if pair_count >= 2:return Truereturn Falseif __name__ == '__main__':# 實(shí)例牌組對象deck = Deck()# 洗牌deck.shuffle()# 循環(huán)實(shí)例7組手牌for i in range(7):#  PokerHand調(diào)用hand的初始化方法, 實(shí)例手牌對象.hand = PokerHand()# 牌組發(fā)7張牌到手牌中deck.move_cards(hand, 7)# 手牌排序hand.sort()# 查看手牌print(hand)# 手牌上是否有同花(五張牌花色相同)print('是否有同花:', hand.has_flush())print('是否有對子:', hand.has_pair())print('是否有連對:', hand.has_twopair())print('')
4. 編寫一個函數(shù)classsify(分類), 它可以弄清楚一副手牌中出現(xiàn)最大的組合, 并設(shè)置label屬性.例如, 一副7張牌的手牌可能包含一個順子以及一個對象; 它應(yīng)當(dāng)標(biāo)記為'flush'(順子).
5. 但你確保分類方法可用時, 下一步是預(yù)料各種手牌的概率.在PolerHand.py中編寫一個函數(shù), 對一副牌進(jìn)行洗牌, 將其分成不同手牌, 對手牌進(jìn)行分類,并記錄每種分類出現(xiàn)的次數(shù).
6. 打印一個表格, 展示各種分類以及它們的概率.更多次地運(yùn)行你的程序, 直到輸出收斂到一個合理程度的正確性為止.將你的結(jié)果和http://en.wikipedia.org/wiki/Hand_rankings上的值進(jìn)行對比.解答: https://github.com/AllenDowney/ThinkPython2/blob/master/code/PokerHandSoln.py
import random# 卡牌類
class Card:# 花色列表suit_names = ["Clubs", "Diamonds", "Hearts", "Spades"]# 大小列表rank_names = [None, "Ace", "2", "3", "4", "5", "6", "7","8", "9", "10", "Jack", "Queen", "King"]# 初始化(默認(rèn)卡牌為紅色2)def __init__(self, suit=0, rank=2):self.suit = suitself.rank = rankdef __str__(self):# 打印對象時展示卡牌return '%s of %s' % (Card.rank_names[self.rank],Card.suit_names[self.suit])# 在兩個對象進(jìn)行 == 比較值的時候觸發(fā) __eq__() 的執(zhí)行def __eq__(self, other):# 比較花色與大小return self.suit == other.suit and self.rank == other.rank# 在兩個對象進(jìn)行 < 小于比較值的時候觸發(fā) __lt__() 的執(zhí)行def __lt__(self, other):t1 = self.suit, self.rankt2 = other.suit, other.rankreturn t1 < t2# 牌組對象
class Deck:# 生成52張牌def __init__(self):self.cards = []for suit in range(4):for rank in range(1, 14):card = Card(suit, rank)self.cards.append(card)# 打印牌組def __str__(self):res = []for card in self.cards:res.append(str(card))return '\n'.join(res)# 添加卡牌到牌組def add_card(self, card):self.cards.append(card)# 從牌組移除某張卡牌def remove_card(self, card):self.cards.remove(card)# 從牌組中彈出出一張牌def pop_card(self, i=-1):return self.cards.pop(i)# 洗牌def shuffle(self):random.shuffle(self.cards)# 排序def sort(self):self.cards.sort()# 發(fā)牌def move_cards(self, hand, num):for i in range(num):hand.add_card(self.pop_card())# 手牌
class Hand(Deck):def __init__(self, label=''):# 手牌列表self.cards = []# 手牌名稱self.label = labelif __name__ == '__main__':# 實(shí)例牌組對象deck = Deck()# 洗牌deck.shuffle()# 生成手牌對象hand = Hand()# 牌組發(fā)5張牌到手牌中deck.move_cards(hand, 5)# 排序hand.sort()# 查看手牌print(hand)
from Card import Hand, Deckclass Hist(dict):"""從每個項(xiàng)目(x)映射到其頻率。"""def __init__(self, seq=[]):# 從sep遍歷值, 并統(tǒng)計值出現(xiàn)的次數(shù)for x in seq:self.count(x)def count(self, x, f=1):# 設(shè)置屬性值, 值為x出現(xiàn)的次數(shù)self[x] = self.get(x, 0) + f# 數(shù)值為0, 則刪除屬性if self[x] == 0:del self[x]# 撲克手
class PokerHand(Hand):"""Represents a poker hand."""# 同花順, 四條, 滿堂紅, 同花, 順子, 三條, 兩對, 對子,  高牌(由單牌且不連續(xù)不同花的組成)all_labels = ['straightflush', 'fourkind', 'fullhouse', 'flush','straight', 'threekind', 'twopair', 'pair', 'highcard']def make_histograms(self):# 花色統(tǒng)計self.suits = Hist()# 大小統(tǒng)計self.ranks = Hist()# 遍歷撲克手的卡牌for c in self.cards:# 統(tǒng)計花色(花色為屬性名, 出現(xiàn)的次數(shù)為屬性值)self.suits.count(c.suit)# 統(tǒng)計大小(大小為屬性名, 出現(xiàn)的次數(shù)為屬性值)self.ranks.count(c.rank)# 將卡牌大小的統(tǒng)計值轉(zhuǎn)為列表, 保存到sets屬性中self.sets = list(self.ranks.values())# 降序self.sets.sort(reverse=True)# 是否為高牌def has_highcard(self):# 有一張牌即可return len(self.cards)def check_sets(self, *t):# *t收集所有的參數(shù)到元組中, self.sets 卡牌大小的統(tǒng)計值的倒序的列表. zip函數(shù)將兩個序列合并成元組列表.for need, have in zip(t, self.sets):# 當(dāng)提供的參數(shù)值 大于 類別中的值返回False, 否則返回True.if need > have:  # 這一句反正寫看起來很變扭.return Falsereturn True# 是否有對子def has_pair(self):# zip((2, ), [x]) x的值 大于 提供的參數(shù), 則說明有對子. 返回True.return self.check_sets(2)# 是否有兩對def has_twopair(self):# zip((2, 2), [x1, x2]) x1與x2的 大于 提供的參數(shù), 則說明有連個對子. 返回True.return self.check_sets(2, 2)# 三條def has_threekind(self):# zip((3, ), [x]) x的值 大于 提供的參數(shù), 則說明有對子. 返回True.return self.check_sets(3)# 四條def has_fourkind(self):# zip((4, ), [x]) x的值 大于 提供的參數(shù), 則說明有對子. 返回True.return self.check_sets(4)# 滿堂紅def has_fullhouse(self):# zip((3, 2), [x]) x的值 大于 提供的參數(shù), 則說明有對子. 返回True.return self.check_sets(3, 2)# 同花def has_flush(self):# 獲取花色直方圖的值, 如果有一個值大于或等于5則返回True.for val in self.suits.values():if val >= 5:return Truereturn False# 順子def has_straight(self):# 復(fù)制一份花色直方圖ranks = self.ranks.copy()# 添加項(xiàng) 鍵為14, 值為鍵x.ranks[14] = ranks.get(1, 0)# 順子牌計數(shù)return self.in_a_row(ranks, 5)def in_a_row(self, ranks, n=5):# 計數(shù)器count = 0# 遍歷值1-14for i in range(1, 15):# 從直方圖中按1-14取值, 取到值則計算器加1if ranks.get(i, 0):count += 1# 連續(xù)取出5張牌都有值, 則是順子返回Trueif count == n:return True# 順子中斷, 計算器清零.else:count = 0# 沒有順子返回Falsereturn False# 同花順def has_straightflush(self):# 集合(將手上的牌, 添加到集合中)s = set()# 遍歷手牌上的牌for c in self.cards:# 按(大小, 花色) 添加到集合中s.add((c.rank, c.suit))# 卡牌為1, 集合添加(14, 花色)if c.rank == 1:s.add((14, c.suit))"""對于牌面為A的牌, 因?yàn)锳可以被視為1或14, 所以在集合s中需要同時添加(1, c.suit)和(14, c.suit)兩個元素, 以便在檢查同花順時能夠正確判斷."""# (生成所有的牌組數(shù)字形式)# 遍歷0 - 3for suit in range(4):count = 0# 遍歷1-14for rank in range(1, 15):# 判斷這張牌是否在集合中if (rank, suit) in s:# 如果在則加1count += 1# 五張同花順則返回Trueif count == 5:return Trueelse:count = 0return False# 同花順-字典版本def has_straightflush(self):# 定義一個字典d = {}# 遍歷卡牌for c in self.cards:# 往字典中保存項(xiàng), (鍵為花色, 值為一個列表) PokerHand()得到一個撲克手對象,d.setdefault(c.suit, PokerHand()).add_card(c)  # add_card將卡牌添加到列表中.# 遍歷字典的值, 值時一個列表for hand in d.values():# 花色列表的值小于5則跳過if len(hand.cards) < 5:continue# 5張卡片的花色相同, 使用直方圖統(tǒng)計, 花色和大小.hand.make_histograms()# 判斷是否有順子, 如果有則返回Trueif hand.has_straight():return Truereturn Falsedef classify(self):# 對牌進(jìn)行直方圖統(tǒng)計self.make_histograms()# 設(shè)置對象的屬性為列表(可以為一副牌打上多個標(biāo)記)self.labels = []# 取出標(biāo)記for label in PokerHand.all_labels:# 取出標(biāo)記方法名(執(zhí)行9個方法)f = getattr(self, 'has_' + label)# 執(zhí)行方法名, 如果方法執(zhí)行之后返回True則將標(biāo)記添加到手牌屬性的label列表中if f():self.labels.append(label)class PokerDeck(Deck):def deal_hands(self, num_cards=5, num_hands=10):# 牌手列表hands = []# 生成7個實(shí)例for i in range(num_hands):# 實(shí)例 撲克手對象hand = PokerHand()# 從牌組發(fā)牌給撲克手self.move_cards(hand, num_cards)# 為手上的牌設(shè)置標(biāo)記hand.classify()# 將實(shí)例添加到列表hands.append(hand)# 返回實(shí)例列表return handsdef main():# 直方圖對象lhist = Hist()# 每迭代循環(huán)n次,處理7的手,每7張牌n = 10000for i in range(n):# 打印循環(huán)的次數(shù), 當(dāng)i可以整除1000時打印i的值if i % 1000 == 0:print(i)# 生成牌組deck = PokerDeck()# 洗牌deck.shuffle()# 給7個人發(fā)7張牌hands = deck.deal_hands(7, 7)# 遍歷7副牌for hand in hands:# 遍歷牌組的標(biāo)簽for label in hand.labels:# 統(tǒng)計標(biāo)簽的數(shù)量lhist.count(label)# 70000 副牌的統(tǒng)計結(jié)果total = 7.0 * nprint(total, 'hands dealt:')# 遍歷所有標(biāo)簽for label in PokerHand.all_labels:# 依據(jù)標(biāo)簽獲取屬性, 屬性記錄這在牌組中出現(xiàn)的次數(shù).freq = lhist.get(label, 0)# freq 的值為0, 跳過.if freq == 0:continue# 計算比較p = total / freq# 打印概率print('%s happens one time in %.2f' % (label, p))if __name__ == '__main__':main()
http://m.aloenet.com.cn/news/29503.html

相關(guān)文章:

  • 跨境電商獨(dú)立站運(yùn)營百度一下的網(wǎng)址
  • 深圳制作網(wǎng)站培訓(xùn)學(xué)校陜西seo快速排名
  • 網(wǎng)站開發(fā)網(wǎng)站設(shè)計案例免費(fèi)推廣工具有哪些
  • 網(wǎng)站用哪些系統(tǒng)做的比較好用如何網(wǎng)站推廣
  • 事件營銷方案模板寧波seo外包公司
  • 網(wǎng)站建設(shè)新趨勢國內(nèi)新聞大事
  • 樹形菜單的網(wǎng)站代碼網(wǎng)絡(luò)運(yùn)營推廣具體做什么工作
  • 做門戶類網(wǎng)站報價上海疫情又要爆發(fā)了
  • 網(wǎng)站服務(wù)器租用有什么好學(xué)大教育一對一收費(fèi)價格表
  • 郴州企業(yè)網(wǎng)站建設(shè)制作營銷案例100例
  • 網(wǎng)站建站建設(shè)網(wǎng)站中國企業(yè)500強(qiáng)排行榜
  • a0000網(wǎng)站建設(shè)2022年seo最新優(yōu)化策略
  • 博山網(wǎng)站建設(shè)網(wǎng)頁制作基礎(chǔ)教程
  • 四川城鄉(xiāng)住房建設(shè)廳官方網(wǎng)站seo搜索優(yōu)化公司排名
  • 新華社官網(wǎng)百度推廣怎么優(yōu)化
  • 深圳平湖網(wǎng)站建設(shè)有免費(fèi)推廣平臺
  • 東莞網(wǎng)站推廣優(yōu)化建設(shè)seo站長工具
  • 吳橋縣網(wǎng)站建設(shè)價格沈陽頭條今日頭條新聞最新消息
  • 網(wǎng)站做分站360收錄批量查詢
  • 多少網(wǎng)站域名采用中文四川全網(wǎng)推網(wǎng)絡(luò)推廣
  • 服務(wù)器的做網(wǎng)站空間北京疫情最新新聞
  • 重慶網(wǎng)站建設(shè)最大seo自然排名關(guān)鍵詞來源的優(yōu)缺點(diǎn)
  • 赤峰做網(wǎng)站的公司鄭州seo優(yōu)化顧問熱狗
  • 商城界面設(shè)計武漢seo服務(wù)多少錢
  • 網(wǎng)站改版 域名百度愛企查電話人工服務(wù)總部
  • 武漢網(wǎng)站制作模板小程序推廣方案
  • 怎么做bt爬蟲網(wǎng)站seo專員是什么職位
  • 醫(yī)美三方網(wǎng)站怎么做首頁百度
  • 做軟件跟網(wǎng)站哪個難全國各城市疫情高峰感染進(jìn)度
  • 廈門網(wǎng)站建設(shè)公司推薦windows優(yōu)化大師破解版