国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

[譯] Python 學(xué)習(xí) —— __init__() 方法 3

leanote / 1078人閱讀

摘要:簡單復(fù)合對象復(fù)合對象也可被稱為容器。它難以序列化對象并像這樣初始化來重建。接口仍然會(huì)導(dǎo)致多種方法計(jì)算。還要注意一些不完全遵循點(diǎn)規(guī)則的方法功能。逐步增加項(xiàng)目的方法和一步加載所有項(xiàng)目的方法是一樣的。另一個(gè)方法就是之前那樣的類定義。

  

注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python

在各個(gè)子類中實(shí)現(xiàn)__init__()

當(dāng)我們看到創(chuàng)建Card對象的工廠函數(shù),再看看Card類設(shè)計(jì)。我想我們可能要重構(gòu)牌值轉(zhuǎn)換功能,因?yàn)檫@是Card類自身應(yīng)該負(fù)責(zé)的內(nèi)容。這會(huì)將初始化向下延伸到每個(gè)子類。

這需要共用的超類初始化以及特定的子類初始化。我們要謹(jǐn)遵Don"t Repeat Yourself(DRY)原則來保持代碼可以被克隆到每一個(gè)子類中。

下面的示例展示了每個(gè)子類初始化的職責(zé):

pythonclass Card:
    pass

class NumberCard(Card):
    def  __init__(self, rank, suit):
        self.suit = suit
        self.rank = str(rank)
        self.hard = self.soft = rank

class AceCard(Card):
    def  __init__(self, rank, suit):
        self.suit = suit
        self.rank = "A"
        self.hard, self.soft =  1, 11

class FaceCard(Card):
    def  __init__(self, rank, suit):
        self.suit = suit
        self.rank = {11: "J", 12: "Q", 13: "K"}[rank]
        self.hard = self.soft = 10

這仍是清晰的多態(tài)。然而,缺乏一個(gè)真正的共用初始化,會(huì)導(dǎo)致一些冗余。缺點(diǎn)在于重復(fù)初始化suit,所以必須將其抽象到超類中。各子類的__init__()會(huì)對超類的__init__()做顯式的引用。

該版本的Card類有一個(gè)超類級(jí)別的初始化函數(shù)用于各子類,如下面代碼片段所示:

pythonclass Card:
    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().__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)

我們在子類和父類都提供了__init__()函數(shù)。好處是簡化了我們的工廠函數(shù),如下面代碼片段所示:

pythondef 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")

簡化工廠函數(shù)不應(yīng)該是我們關(guān)注的焦點(diǎn)。不過我們從這可以看到一些變化,我們創(chuàng)建了比較復(fù)雜的__init__()函數(shù),而對工廠函數(shù)卻有一些較小的改進(jìn)。這是比較常見的權(quán)衡。

工廠函數(shù)封裝復(fù)雜性

在復(fù)雜的__init__()方法和工廠函數(shù)之間有個(gè)權(quán)衡。最好就是堅(jiān)持更直接,更少程序員友好的__init__()方法,并將復(fù)雜性推給工廠函數(shù)。如果你想封裝復(fù)雜結(jié)構(gòu),工廠函數(shù)可以做的很好。

簡單復(fù)合對象

復(fù)合對象也可被稱為容器。我們來看一個(gè)簡單的復(fù)合對象:一副多帶帶的牌。這是一個(gè)基本的集合。事實(shí)上它是如此基本,以至于我們不用過多的花費(fèi)心思,直接使用簡單的list做為一副牌。

在設(shè)計(jì)一個(gè)新類之前,我們需要問這個(gè)問題:使用一個(gè)簡單的list是否合適?

我們可以使用random.shuffle()來洗牌和使用deck.pop()發(fā)牌到玩家手里。

一些程序員急于定義新類就像使用內(nèi)置類一樣草率,這很容易違反面向?qū)ο蟮脑O(shè)計(jì)原則。我們要避免一個(gè)新類像如下代碼片段所示:

pythond = [card6(r+1, s) for r in range(13) for s in (Club, Diamond, Heart, Spade)]
random.shuffle(d)
hand = [d.pop(), d.pop()]

如果就這么簡單,為什么要寫一個(gè)新類?

答案并不完全清楚。一個(gè)好處是,提供一個(gè)簡化的、未實(shí)現(xiàn)接口的對象。正如我們前面提到的工廠函數(shù)一樣,但在Python中類并不是一個(gè)硬性要求。

在前面的代碼中,一副牌只有兩個(gè)簡單的用例和一個(gè)似乎并不夠簡化的類定義。它的優(yōu)勢在于隱藏實(shí)現(xiàn)的細(xì)節(jié),但細(xì)節(jié)是如此微不足道,揭露它們幾乎沒有任何意義。在本章中,我們的關(guān)注主要放在__init__()方法上,我們將看一些創(chuàng)建并初始化集合的設(shè)計(jì)。

設(shè)計(jì)一個(gè)對象集合,有以下三個(gè)總體設(shè)計(jì)策略:

封裝:該設(shè)計(jì)模式是現(xiàn)有的集合的定義。這可能是Facade設(shè)計(jì)模式的一個(gè)例子。

繼承:該設(shè)計(jì)模式是現(xiàn)有的集合類,是普通子類的定義。

多態(tài):從頭開始設(shè)計(jì)。我們將在第六章看看《創(chuàng)建容器和集合》。

這三個(gè)概念是面向?qū)ο笤O(shè)計(jì)的核心。在設(shè)計(jì)一個(gè)類的時(shí)候我們必須總是這樣做選擇。

1. 封裝集合類

以下是封裝設(shè)計(jì),其中包含一個(gè)內(nèi)部集合:

pythonclass Deck:
    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()

我們已經(jīng)定義了Deck,內(nèi)部集合是一個(gè)list對象。Deckpop()方法簡單的委托給封裝好的list對象。

然后我們可以通過下面這樣的代碼創(chuàng)建一個(gè)Hand實(shí)例:

pythond = Deck()
hand = [d.pop(), d.pop()]

一般來說,F(xiàn)acade設(shè)計(jì)模式或封裝好方法的類是簡單的被委托給底層實(shí)現(xiàn)類的。這個(gè)委托會(huì)變得冗長。對于一個(gè)復(fù)雜的集合,我們可以委托大量方法給封裝的對象。

2. 繼承集合類

封裝的另一種方法是繼承內(nèi)置類。這樣做的優(yōu)勢是沒有重新實(shí)現(xiàn)pop()方法,因?yàn)槲覀兛梢院唵蔚乩^承它。

pop()的優(yōu)點(diǎn)就是不用寫過多的代碼就能創(chuàng)建類。在這個(gè)例子中,繼承list類的缺點(diǎn)是提供了一些我們不需要的函數(shù)。

下面是繼承內(nèi)置listDeck定義:

pythonclass Deck2(list):
    def __init__(self):
        super().__init__(card6(r+1, s) for r in range(13) for s in (Club, Diamond, Heart, Spade))
        random.shuffle(self)

在某些情況下,為了擁有合適的類行為,我們的方法將必須顯式地使用超類。在下面的章節(jié)中我們將會(huì)看到其他相關(guān)示例。

我們利用超類的__init__()方法填充我們的list對象來初始化單副撲克牌,然后我們洗牌。pop()方法只是簡單從list繼承過來且工作完美。從list繼承的其他方法也能一起工作。

3. 更多的需求和另一種設(shè)計(jì)

在賭場中,牌通常從牌盒發(fā)出,里面有半打喜憂參半的撲克牌。這個(gè)原因使得我們有必要建立自己版本的Deck,而不是簡單、純粹的使用list對象。

此外,牌盒里的牌并不完全發(fā)完。相反,會(huì)插入標(biāo)記牌。因?yàn)橛袠?biāo)記牌,有些牌會(huì)被保留,而不是用來玩。

下面是包含多組52張牌的Deck定義:

pythonclass Deck3(list):
    def __init__(self, decks=1):
        super().__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.randint(1, 52)
        for i in range(burn): 
            self.pop()

在這里,我們使用super().__init__()來構(gòu)建一個(gè)空集合。然后,我們使用self.extend()添加多次52張牌。由于我們在這個(gè)類中沒有使用覆寫,所以我們可以使用super().extend()

我們還可以通過super().__init__(),使用更深層嵌套的生成器表達(dá)式執(zhí)行整個(gè)任務(wù)。如下面代碼片段所示:

python(card6(r+1, s) for r in range(13) for s in (Club, Diamond, Heart, Spade) for d in range(decks))

這個(gè)類為我們提供了一個(gè)Card實(shí)例的集合,我們可以使用它來模仿賭場21點(diǎn)發(fā)牌的盒子。

在賭場有一個(gè)奇怪的儀式,他們會(huì)翻開廢棄的牌。如果我們要設(shè)計(jì)一個(gè)記牌玩家策略,我們可能需要效仿這種細(xì)微差別。

復(fù)雜復(fù)合對象

以下是21點(diǎn)Hand類描述的一個(gè)例子,很適合模擬玩家策略:

pythonclass 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)

在這個(gè)例子中,我們有一個(gè)基于__init__()方法參數(shù)的self.dealer_card實(shí)例變量。self.cards實(shí)例變量是不基于任何參數(shù)的。這個(gè)初始化創(chuàng)建了一個(gè)空集合。

我們可以使用下面的代碼去創(chuàng)建一個(gè)Hand實(shí)例

pythond = Deck()
h = Hand(d.pop())
h.cards.append(d.pop())
h.cards.append(d.pop())

缺點(diǎn)就是有一個(gè)冗長的語句序列被用來構(gòu)建一個(gè)Hand的實(shí)例對象。它難以序列化Hand對象并像這樣初始化來重建。盡管我們在這個(gè)類中創(chuàng)建一個(gè)顯式的append()方法,它仍將采取多個(gè)步驟來初始化集合。

我們可以嘗試創(chuàng)建一個(gè)接口,但這并不是一件簡單的事情,對于Hand對象它只是在語法上發(fā)生了變化。接口仍然會(huì)導(dǎo)致多種方法計(jì)算。當(dāng)我們看到第2部分中的《序列化和持久化》,我們傾向于使用接口,一個(gè)類級(jí)別的函數(shù),理想情況下,應(yīng)該是類的構(gòu)造函數(shù)。我們將在第9章的《序列化和存儲(chǔ)——JSON、YAML、Pickle、CSV和XML》深入研究。

還要注意一些不完全遵循21點(diǎn)規(guī)則的方法功能。在第二章《通過Python無縫地集成——基本的特殊方法》中我們會(huì)回到這個(gè)問題。

1. 復(fù)雜復(fù)合對象初始化

理想情況下,__init__()方法會(huì)創(chuàng)建一個(gè)對象的完整實(shí)例。這是一個(gè)更復(fù)雜的容器,當(dāng)你在創(chuàng)建一個(gè)包含內(nèi)部其他對象集合的完整實(shí)例的時(shí)候。如果我們可以一步就能構(gòu)建這個(gè)復(fù)合對象,它將是非常有幫助的。

逐步增加項(xiàng)目的方法和一步加載所有項(xiàng)目的方法是一樣的。

例如,我們可能有如下面的代碼片段所示的類:

pythonclass 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)

這個(gè)初始化一步就設(shè)置了所有實(shí)例變量。另一個(gè)方法就是之前那樣的類定義。我們可以有兩種方式構(gòu)建一個(gè)Hand2對象。第一個(gè)示例一次加載一張牌到Hand2對象:

pythond = Deck()
P = Hand2(d.pop())
p.cards.append(d.pop())
p.cards.append(d.pop())

第二個(gè)示例使用*cards參數(shù)一步加載一序列的Card類:

pythond = Deck()
h = Hand2(d.pop(), d.pop(), d.pop())

對于單元測試,在一個(gè)聲明中使用這種方式通常有助于構(gòu)建復(fù)合對象。更重要的是,這種簡單、一步的計(jì)算來構(gòu)建復(fù)合對象有利于下一部分的序列化技術(shù)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/45381.html

相關(guān)文章

  • [] Python 學(xué)習(xí) —— __init__() 方法 1

    摘要:第一是在對象生命周期中初始化是最重要的一步每個(gè)對象必須正確初始化后才能正常工作。第二是參數(shù)值可以有多種形式。基類對象的方法對象生命周期的基礎(chǔ)是它的創(chuàng)建初始化和銷毀。在某些情況下,這種默認(rèn)行為是可以接受的。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python __init__()方法意義重大的原因有兩個(gè)。第一是在對象生命...

    MobService 評(píng)論0 收藏0
  • [] Python 學(xué)習(xí) —— __init__() 方法 2

    摘要:工廠類的函數(shù)就是包裝一些目標(biāo)類層次結(jié)構(gòu)和復(fù)雜對象的構(gòu)造。連貫的工廠類接口在某些情況下,我們設(shè)計(jì)的類在方法使用上定義好了順序,按順序求方法的值很像函數(shù)。這個(gè)工廠類可以像下面這樣使用首先,我們創(chuàng)建一個(gè)工廠實(shí)例,然后我們使用那個(gè)實(shí)例創(chuàng)建實(shí)例。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python 通過工廠函數(shù)對 __init_...

    xiaotianyi 評(píng)論0 收藏0
  • [] Python 學(xué)習(xí) —— __init__() 方法 4

    摘要:同時(shí),有多個(gè)類級(jí)別的靜態(tài)構(gòu)造函數(shù)的方法。這個(gè)累贅,無論如何,是被傳遞到每個(gè)單獨(dú)的對象構(gòu)造函數(shù)表達(dá)式中。我們可能只有幾個(gè)特定的擔(dān)憂,提供額外關(guān)鍵字參數(shù)給構(gòu)造函數(shù)。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python 沒有__init__()的無狀態(tài)對象 下面這個(gè)示例,是一個(gè)簡化去掉了__init__()的類。這是一個(gè)常見...

    yvonne 評(píng)論0 收藏0
  • []PEP 380--子生成器的語法

    摘要:提議以下的新的生成器語法將被允許在生成器的內(nèi)部使用其中表達(dá)式作用于可迭代對象,從迭代器中提取元素。子迭代器而非生成器的語義被選擇成為生成器案例的合理泛化。建議如果關(guān)閉一個(gè)子迭代器時(shí),引發(fā)了帶返回值的異常,則將該值從調(diào)用中返回給委托生成器。 導(dǎo)語: PEP(Python增強(qiáng)提案)幾乎是 Python 社區(qū)中最重要的文檔,它們提供了公告信息、指導(dǎo)流程、新功能的設(shè)計(jì)及使用說明等內(nèi)容。對于學(xué)習(xí)...

    fevin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<