摘要:可以賦值對象的生命周期主要是有創建初始化銷毀。顯示而非隱式對于每個方法,都應當顯示的制定要初始化的變量。每當創建一個對象,會創建一個空對象,然后調用該對象的函數,提供了初始化的操作。以點為例作為說明。
第一部分 用特殊方法實現Python風格的類
為了實現更好的可擴展性,Python語言提供了大量的特殊方法,它們大致分為以下幾類。
特性訪問
可調用對象
集合
數字
上下文
迭代器
第一章 使用__init()__方法Python中一切事物皆對象!?。。。?!
__init__()方法記住兩點:
__init()__(初始化)是對象生命周期的開始,每個對象必須正確初始化才能夠正常的工作。
__init__()可以賦值
對象的生命周期主要是有創建、初始化、銷毀。
‘顯示而非隱式’:對于每個__init__()方法,都應當顯示的制定要初始化的變量。
每當創建一個對象,python會創建一個空對象,然后調用該對象的__init__()函數,提供了初始化的操作。
# 以21點為例作為說明。 class Card(object): def __init__(self, suit, rank): self.suit = suit self.rank = rank self.hard, self.soft = self._points() class NumberCard(Card): def _points(self): return int(self.rank), int(self.rank) class AceCard(Card): def _points(self): return 1, 11 class FaceCard(Card): def _points(self): return 10, 10 class Suit(object): def __init__(self,name,symbol): self.name = name self.symbol = symbol Club,Diamond,Heart,Spade = Suit("Club","?"),Suit("Diamond","?"),Suit("Heart","?"),Suit("Spade","?")通過工廠函數來調用__init__():
def card(rank, suit): if rank == 1: return AceCard("A", suit) elif 2 <= rank < 11: return NumberCard(str(rank), suit) elif 11 <= rank < 14: name = {11: "J", 12: "Q", 13: "K"}[rank] return FaceCard(name, suit) else: raise Exception("rank out of range")
這個函數通過傳入牌面值rank 和花色值suit來創建card對象.
deck = [card(rank, suit) for rank in range(1, 14) for suit in (Club, Diamond, Heart, Spade)] print(deck[0].rank,deck[0].suit.symbol)
這段代碼完成了52張牌對象的創建.
使用映射和類來簡化設計.由于類是第一級別的對象,從rank參數射到對象是很容易的事情.
下面的Card類工廠就是使用映射實現的版本.
def card4(rank,suit): class_ = {1:AceCard,11:FaceCard,12:FaceCard,13:FaceCard}.get(rank,NumberCard) return class_(rank,suit)
需要修改映射邏輯,除了提供Card子類,還需要提供rank對象的字符串結果.如何實現這兩部分映射,有四種常見方案.
可以建立兩個并行映射
可以映射為一個二元組.
可以映射為partial()函數.
可以考慮修改類定義的完成映射邏輯.
1.并行映射
def card5(rank,suit): class_ = {1:AceCard,11:FaceCard,12:FaceCard,13:FaceCard}.get(rank,NumberCard) rank_str = {1:"A",11: "J", 12: "Q", 13: "K"}.get(rank,str(rank)) return class_(rank_str,suit)
這樣是不值得做的,帶來映射鍵1,11,12,13的邏輯重復.
不要使用并行結構,并行結構應該被元祖或者一些更好的組合所代替
映射到一個牌面值的元組
def card6(rank,suit): class_,rank_str= { 1:(AceCard,"A"), 11:(FaceCard,"J"), 12:(FaceCard,"Q"), 13:(FaceCard,"K") }.get(rank,(NumberCard,str(rank))) return class_(rank_str,suit)
從rank值映射到類對象時很少見的,而且兩個參數只有一個用于對象的初始化.從rank映射到一個相對簡單的類或者是函數對象,而不必提供目的不明確的參數,這才是明智的選擇.
3.partial 函數設計
def card7(rank,suit): from functools import partial part_class = { 1:partial(AceCard,"A"), 11:partial(FaceCard,"J"), 12:partial(FaceCard,"Q"), 13:partial(FaceCard,"K") }.get(rank,partial(NumberCard,str(rank))) return part_class(suit)
通過調用partial()函數然后復制給part_class,完成于rank對象的管的關聯,可以使用同樣的方式來創建suit對象,并且完成最終的Card對象的創建.partial()函數的使用在函數時編程中是很常見的.當時用的是函數而非對象方法的時候就可以考慮使用.
大致上,partial()函數在面向對象編程中不是很常用,我們可以簡單的的提供構造函數不同版本來做相同的事情.partial()函數和構造對象時的流暢接口很類似.
工廠模式的流暢的API設計
有時候我們定義類中的方法必須按照特定的順序來調用.這種順序調用的方法和創建 partial() 函數的方式非常類似.
我們可以在流暢接口函數中設置可以返回self值的rank對象,然后傳入花色類從而創建Card實例/
以下是Card工廠流暢接口的定義,包含兩個函數,他們必須按照順序調用.
class CardFactory(object): def rank(self,rank): self.class_,self.rank_str = { 1:(AceCard,"A"), 11:(FaceCard,"J"), 12:(FaceCard,"Q"), 13:(FaceCard,"K") }.get(rank,(NumberCard,str(rank))) def suit(self,suit): return self.class_(self.rank_str,suit)
先使用rank()函數更新了構造函數的狀態,然后通過suit()函數創造了 最終的Card對象.
def A (rank): a,b ={ # 本身為一個字典的傳遞值.返回對應的值.是dict的get方法 1: (AceCard, "A"), 11: (FaceCard, "J"), 12: (FaceCard, "Q"), 13: (FaceCard, "K") }.get(rank, (NumberCard, str(rank))) return a,b # 返回的是一個tuple(),a 為, b 為"3" a = A(3) print(a)
我們先實例化一個工廠對象,然然后在創建Card實例,這用方式沒有利用__init__() 在Card類層級結構的作用,改變的是調用者創建創建對象的方式.
在每個子類中實現__init__()方法以下代碼演示了如何把__init__()方法提到基類Card中實現的過程.然后在子類中可以重用基類的實現.
class Card(object): def __init__(self, rank, suit, hard, soft): self.rank = rank self.suit = suit self.hard = hard self.soft = soft 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) def card10(rank,suit): if rank == 1: return AceCard(rank,suit) elif 2<= rank < 11: return NumberCard(rank,suit) elif 11<= rank <14: return FaceCard(rank,suit) else: raise Exception("Rank out of range")
在這里重構了基類中的__init__,雖然將它復雜化,但是這樣的權衡是正常的.
使用工廠函數封裝的復雜性
在 `__init__()`方法和工廠函數之間存在一些權衡,通常直接調動比"程序員友好"的`__init__()`函數并把復雜性分發給工廠函數更好.當需要封裝復雜的構造函數邏輯時,考慮使用工廠函數則更好.簡單的組合對象
一個組合對象也可以稱作容器.
如果業務邏輯相對簡單,為什么定義新類?
類的定義的一個優勢是:
類給對象提供了簡單的,不需要實現的接口.
設計集合類,通常是下面三種策略:
封裝:這個實際是基于現有集合類來定義一個新類,屬于外觀模式的一個使用場景.
擴展:這個設計是對現有集合類進行擴展,通常使用定義子類的方式來實現.
創建:即重新設計.
以上是面向對象設計的核心.
以下是對內部集合進行封裝設計.
import random class Deck(object): def __init__(self): self._cards = [card6(r+1,s) for r in range(13) for s in (Club,Diamond,Heart,Spade)] random.shuffle(self._cards) def pop(self): return self._cards.pop() d = Deck() hand = [d.pop(),d.pop()]
一般來說買外觀模式或者封裝類中的方法實現只是對底層對象相應函數的代理調用.
class Desk3(list): def __init__(self, decks=1): super(Desk3, self).__init__() for i in range(decks): self.extend(card6(r + 1, s) for r in range(13) for s in (Club, Diamond, Heart, Spade)) random.shuffle(self) burn = random.random(1,52) for i in range(burn): self.pop()
這里我們使用了基類的 __init__()函數來創建了一個空集合,然后調用了 self.extrend()來吧多副牌加載到發牌機中.
復雜的組合對象模擬打牌策略
class Hand: def __init__(self,dealer_card): self.dealer_card = dealer_card self.cards = [] def hard_total(self): return sum(c.hard for c in self.cards) def soft_total(self): return sum(c.soft for c in self.cards) d = Deck() h = Hand(d.pop()) h.cards.append(d.pop()) h.cards.append(d.pop())
需要一個一個的添加非常不方便
__init__()初始化方法應當返回一個完成的對象,當然這個是理想的情況.而這樣也帶來復雜性,因為要創建的對象內部可能包含了集合,集合里面又包含了其他對象.
通??紤]使用一個流暢的接口來完成逐個講對象添加到集合的操作,同時將集合對象作為構造函數來完成初始化.例如:
class Hand2: def __init__(self, dealer_card, *cards): self.dealer_card = dealer_card self.cards = list(cards) def hard_total(self): return sum(c.hard for c in self.cards) def soft_total(self): return sum(c.soft for c in self.cards) d = Deck() h = Hand2(d.pop(),d.pop(),d.pop(),d.pop()) print(h.cards)不帶__init__方法的無狀態對象
一個策略對象以插件的形式復合在主對象上來完成一種算法或邏輯.它或許以來主對象中的數據,策略對象自身并不攜帶任何數據.通常策略類會和亨元設計模式一起使用:在策略對象中避免內部存儲.所需要的值都從策略對象方法參數傳入.策略對象自身是無狀態的.可以把它看做是一系列函數的集合.
這里定義了一個類給Player實例提供了游戲的選擇模式,以下這個策略包括拿牌和下注.
class GameStrategy: def insurnace(self, hand): return False def split(self, hand): return False def double(self, hand): return False def hit(self, hand): return False
每個函數需要傳入已有的Hand對象,函數邏輯所需要的數據基于現有的可用信息.意味著數據來自于莊家跟玩家的手牌.
玩家有兩張策略:打牌和下注.每個Player實例回合模擬器進行很多次交互.我們這里把這個模擬器命名為Table
Table類的職責需要配合Player實例完成以下事件:
玩家必須要基于玩牌策略初始化一個牌局.
隨后玩家會得到一手牌
如果
以下是Table類中投注和牌的邏輯處理相關的代碼
class Table: def __init__(self): # 生成52張牌 self.deck = Deck() def place_bet(self, amount): print("Bet", amount) def get_hand(self): try: # self.hand = Hand2(d.pop(), d.pop(), d.pop()) # self.hole_card = d.pop() 書上是這么寫的我認為不對,改為下面寫法 self.hand = Hand2(self.deck.pop(), self.deck.pop(), self.deck.pop()) self.hole_card = self.deck.pop() except IndexError: # Out of cards: need to shuffle self.deck = Deck() return self.get_hand() print("Deal", self.hand) return self.hand # 沒有看明白hand從何而來,所以也未找到insure的方法。估計是寫錯了。 def can_insure(self, hand): return hand.dealer_card.insure class BettingStrategy: def bet(self): raise NotImplementedError("No bet method") def record_win(self): pass def record_lose(self): pass class Flat(BettingStrategy): def bet(self): return 1
上面的那一段代碼還未看懂需要以后再來看一遍.
多策略的__init__()方法class Hand4: def __init__(self, *args, **kwargs): print(len(args),args,kwargs) if len(args) == 1 and isinstance(args[0], Hand4): other = args[0] self.dealer_card = other.dealer_card self.cards = other.cards elif len(args) == 2 and isinstance(args[0], Hand4) and "split" in kwargs: # Split an existing hand other, card = args self.dealer_card = other.dealer_card self.cards = [other.cards[kwargs["split"]], card] elif len(args) == 3: # Bulid a fresh ,new hand dealer_card,*cards = args self.dealer_card = dealer_card self.cards = list(cards) else: raise TypeError("Invaild constructor args= {0!r} kw={1!r}".format(args,kwargs)) def __str__(self): return ",".join(map(str,self.cards)) d = Deck() h = Hand4(d.pop(),d.pop(),d.pop()) print(h) # s1 = Hand4(h,d.pop(),split = 0) # s2 = Hand4(h,d.pop(),split = 1)
class Hand5: def __init__(self,dealer_card,*cards): self.dealer_card = dealer_card self.cards = list(cards) @staticmethod def freeze(other): hand = Hand5(other.dealer_card,*other.cards) return hand @staticmethod def split(other,card0,card1): hand0 = Hand5(other.dealer_card,other.cards[0],card0) hand1 = Hand5(other.dealer_card,other.cards[1],card1) return hand0,hand1 def __str__(self): return ",".join(map(str,self.cards)) d = Deck() h = Hand5(d.pop(),d.pop(),d.pop()) s1,s2 = Hand5.split(h,d.pop(),d.pop())
上面這段代碼實現了:當第一輪發完牌是,dealer手牌有一張,Player手牌有兩張,當手牌的兩張牌相同的時候玩家可以選擇分牌,將手中的的兩張牌分為兩組牌,繼續進行游戲.然后發牌器會給Palyer每組牌中個發一張牌
更多的__init__()技術以下是Player類的定義,初始化使用兩個策略對象和一個table對象
略
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44466.html
摘要:原文第一章主要介紹的大概情況基本語法。通過和來引用對象屬性或數組元素的值就構成一個表達式。 原文:https://keelii.github.io/2016/06/16/javascript-definitive-guide-note-0/ 第一章 主要介紹 JavaScript 的大概情況、基本語法。之前沒有 JavaScript 基礎的看不懂也沒關系,后續章節會有進一步的詳細說明...
摘要:前段時間在網絡上看了車洪才老先生編寫阿富汗語詞典的故事。年,萬字,完稿時已斗轉星移。然而書的修成不都如此嗎前有明朝的永樂大典,近有車洪才老先生的阿富汗語詞典我還看過一部電影編舟記,講述的便是一部字典歷經十余年的修成。然而我們對此坦然以待。 我記得去年的這個時候我丟了工作,然后每天就是跑步、譯書、跑步、譯書。非常單調的日子過了兩個多月。這種日子過的時候并沒有過多地覺得什么,但是譯完交稿的...
摘要:第一章數據類型隱式方法利用快速生成類方法方法通過下標找元素自動支持切片操作可迭代方法與如果是一個自定義類的對象,那么會自己去調用其中由你實現的方法。若返回,則會返回否則返回。一個對象沒有函數,解釋器會用作為替代。 第一章 python數據類型 1 隱式方法 利用collections.namedtuple 快速生成類 import collections Card = collec...
摘要:第一章數據類型隱式方法利用快速生成字典方法方法通過下標找元素自動支持切片操作可迭代方法與如果是一個自定義類的對象,那么會自己去調用其中由你實現的方法。若返回,則會返回否則返回。一個對象沒有函數,解釋器會用作為替代。 第一章 python數據類型 1 隱式方法 利用collections.namedtuple 快速生成字典 import collections Card = coll...
摘要:本文最早為雙十一而作,原標題雙大前端工程師讀書清單,以付費的形式發布在上。發布完本次預告后,捕捉到了一個友善的吐槽讀書清單也要收費。這本書便從的異步編程講起,幫助我們設計快速響應的網絡應用,而非簡單的頁面。 本文最早為雙十一而作,原標題雙 11 大前端工程師讀書清單,以付費的形式發布在 GitChat 上。發布之后在讀者圈群聊中和讀者進行了深入的交流,現免費分享到這里,不足之處歡迎指教...
閱讀 553·2023-04-26 02:59
閱讀 691·2023-04-25 16:02
閱讀 2154·2021-08-05 09:55
閱讀 3544·2019-08-30 15:55
閱讀 4640·2019-08-30 15:44
閱讀 1797·2019-08-30 13:02
閱讀 2193·2019-08-29 16:57
閱讀 2288·2019-08-26 13:35