成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

yvonne / 1785人閱讀

摘要:同時(shí),有多個(gè)類級(jí)別的靜態(tài)構(gòu)造函數(shù)的方法。這個(gè)累贅,無論如何,是被傳遞到每個(gè)多帶帶的對(duì)象構(gòu)造函數(shù)表達(dá)式中。我們可能只有幾個(gè)特定的擔(dān)憂,提供額外關(guān)鍵字參數(shù)給構(gòu)造函數(shù)。

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

沒有__init__()的無狀態(tài)對(duì)象

下面這個(gè)示例,是一個(gè)簡(jiǎn)化去掉了__init__()的類。這是一個(gè)常見的Strategy設(shè)計(jì)模式對(duì)象。策略對(duì)象插入到主對(duì)象來實(shí)現(xiàn)一種算法或者決策。它可能依賴主對(duì)象的數(shù)據(jù),策略對(duì)象自身可能沒有任何數(shù)據(jù)。我們經(jīng)常設(shè)計(jì)策略類來遵循Flyweight設(shè)計(jì)模式:我們避免在Strategy對(duì)象內(nèi)部進(jìn)行存儲(chǔ)。所有提供給Strategy的值都是作為方法的參數(shù)值。Strategy對(duì)象本身可以是無狀態(tài)的。這更多是為了方法函數(shù)的集合而非其他。

在本例中,我們?yōu)?b>Player實(shí)例提供了游戲策略。下面是一個(gè)抓牌和減少其他賭注的策略示例(比較笨的策略):

class GameStrategy:
    def insurance(self, hand):
        return False
    def split(self, hand):
        return False
    def double(self, hand):
        return False
    def hit(self, hand):
        return sum(c.hard for c in hand.cards) <= 17

每個(gè)方法都需要當(dāng)前的Hand作為參數(shù)值。決策是基于可用信息的,也就是指莊家的牌和閑家的牌。

我們可以使用不同的Player實(shí)例來構(gòu)建單個(gè)策略實(shí)例,如下面代碼片段所示:

dumb = GameStrategy()

我們可以想象創(chuàng)造一組相關(guān)的策略類,在21點(diǎn)中玩家可以針對(duì)各種決策使用不同的規(guī)則。

一些額外的類定義

如前所述,一個(gè)玩家有兩個(gè)策略:一個(gè)用于下注,一個(gè)用于出牌。每個(gè)Player實(shí)例都與模擬計(jì)算執(zhí)行器有一序列的交互。我們稱計(jì)算執(zhí)行器為Table類。

Table類需要Player實(shí)例提供以下事件:

玩家必須基于下注策略來設(shè)置初始賭注。

玩家將得到一手牌。

如果手牌是可分離的,玩家必須決定是分離或不基于出牌策略。這可以創(chuàng)建額外的Hand實(shí)例。在一些賭場(chǎng),額外的一手牌也是可分離的。

對(duì)于每個(gè)Hand實(shí)例,玩家必須基于出牌策略來決定是要牌、加倍或停牌。

玩家會(huì)獲得獎(jiǎng)金,然后基于輸贏情況調(diào)整下注策略。

從這,我們可以看到Table類有許多API方法來獲得賭注,創(chuàng)建Hand對(duì)象提供分裂、分解每一手牌、付清賭注。這個(gè)對(duì)象跟蹤了一組Players的出牌狀態(tài)。

以下是處理賭注和牌的Table類:

class Table:
    def __init__(self):
        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()
        except IndexError:
            # Out of cards: need to shuffle.
            self.deck = Deck()
            return self.get_hand()
        print("Deal", self.hand)
        return self.hand
    def can_insure(self, hand):
        return hand.dealer_card.insure

Player使用Table類來接收賭注,創(chuàng)建一個(gè)Hand對(duì)象,出牌時(shí)根據(jù)這手牌來決定是否買保險(xiǎn)。使用額外方法去獲取牌并決定償還。

get_hand()中展示的異常處理不是一個(gè)精確的賭場(chǎng)玩牌模型。這可能會(huì)導(dǎo)致微小的統(tǒng)計(jì)誤差。更精確的模擬需要編寫一副牌,當(dāng)空的時(shí)候可以重新洗牌,而不是拋出異常。

為了正確地交互和模擬現(xiàn)實(shí)出牌,Player類需要一個(gè)下注策略。下注策略是一個(gè)有狀態(tài)的對(duì)象,決定了初始賭注。各種下注策略調(diào)整賭注通常都是基于游戲的輸贏。

理想情況下,我們渴望有一組下注策略對(duì)象。Python的裝飾器模塊允許我們創(chuàng)建一個(gè)抽象超類。一個(gè)非正式的方法創(chuàng)建策略對(duì)象引發(fā)的異常必須由子類實(shí)現(xiàn)。

我們定義了一個(gè)抽象超類,此外還有一個(gè)具體子類定義了固定下注策略,如下所示:

class BettingStrategy:
    def bet(self):
        raise NotImplementedError("No bet method")
    def record_win(self):
        pass
    def record_loss(self):
        pass

class Flat(BettingStrategy):
    def bet(self):
        return 1

超類定義了帶有默認(rèn)值的方法。抽象超類中的基本bet()方法拋出異常。子類必須覆蓋bet()方法。其他方法可以提供默認(rèn)值。這里給上一節(jié)的游戲策略添加了下注策略,我們可以看看Player類周圍更復(fù)雜的__init__()方法。

我們可以利用abc模塊正式化抽象超類的定義。就像下面的代碼片段那樣:

import abc
class BettingStrategy2(metaclass=abc.ABCMeta):
    @abstractmethod
    def bet(self):
        return 1
    def record_win(self):
        pass
    def record_loss(self):
?       pass

這樣做的優(yōu)勢(shì)在于創(chuàng)建了BettingStrategy2的實(shí)例,不會(huì)造成任何子類bet()的失敗。如果我們?cè)噲D通過未實(shí)現(xiàn)的抽象方法來創(chuàng)建這個(gè)類的實(shí)例,它將引發(fā)一個(gè)異常來替代創(chuàng)建對(duì)象。

是的,抽象方法有一個(gè)實(shí)現(xiàn)。它可以通過super().bet()來訪問。

多策略的__init__()

我們可從各種來源創(chuàng)建對(duì)象。例如,我們可能需要復(fù)制一個(gè)對(duì)象作為創(chuàng)建備份或凍結(jié)一個(gè)對(duì)象的一部分,以便它可以作為字典的鍵或被置入集合中;這是內(nèi)置類setfrozenset背后的想法。

有幾個(gè)總體設(shè)計(jì)模式,它們有多種方法來構(gòu)建一個(gè)對(duì)象。一個(gè)設(shè)計(jì)模式就是一個(gè)復(fù)雜的__init__(),稱為多策略初始化。同時(shí),有多個(gè)類級(jí)別的(靜態(tài))構(gòu)造函數(shù)的方法。

這些都是不兼容的方法。他們有完全不同的接口。

避免克隆方法

在Python中,一個(gè)克隆方法沒必要復(fù)制一個(gè)不需要的對(duì)象。使用克隆技術(shù)表明可能是未能理解Python中的面向?qū)ο笤O(shè)計(jì)原則。

克隆方法封裝了在錯(cuò)誤的地方創(chuàng)建對(duì)象的常識(shí)。被克隆的源對(duì)象不能了解通過克隆建立的目標(biāo)對(duì)象的結(jié)構(gòu)。然而,如果源對(duì)象提供了一個(gè)合理的、得到了良好封裝的接口,反向(目標(biāo)對(duì)象有源對(duì)象相關(guān)的內(nèi)容)是可以接受的。

我們這里展示的例子是有效的克隆,因?yàn)樗鼈兒芎?jiǎn)單。我們將在下一章展開它們。然而,展示這些基本技術(shù)是用來做更多的事情,而不是瑣碎的克隆,我們看看將可變對(duì)象Hand凍結(jié)為不可變對(duì)象。

下面可以通過兩種方式創(chuàng)建Hand對(duì)象的示例:

class Hand3:
    def __init__(self, *args, **kw):
      if len(args) == 1 and isinstance(args[0], Hand3):
          # Clone an existing hand; often a bad idea
          other = args[0]
          self.dealer_card = other.dealer_card
          self.cards = other.cards
      else:
          # Build a fresh, new hand.
          dealer_card, *cards = args
          self.dealer_card =  dealer_card
          self.cards = list(cards)

第一種情況,從現(xiàn)有的Hand3對(duì)象創(chuàng)建Hand3實(shí)例。第二種情況,從多帶帶的Card實(shí)例創(chuàng)建Hand3對(duì)象。

frozenset對(duì)象的相似之處在于可由多帶帶的項(xiàng)目或現(xiàn)有set對(duì)象創(chuàng)建。我們將在下一章學(xué)習(xí)創(chuàng)建不可變對(duì)象。使用像下面代碼片段這樣的構(gòu)造,從現(xiàn)有的Hand創(chuàng)建一個(gè)新的Hand使得我們可以創(chuàng)建一個(gè)Hand對(duì)象的備份:

h = Hand(deck.pop(), deck.pop(), deck.pop())
memento = Hand(h)

我們保存Hand對(duì)象到memento變量中。這可以用來比較最后處理的牌與原來手牌,或者我們可以在集合或映射中使用時(shí)凍結(jié)它。

1. 更復(fù)雜的初始化選擇

為了編寫一個(gè)多策略初始化,我們經(jīng)常被迫放棄特定的命名參數(shù)。這種設(shè)計(jì)的優(yōu)點(diǎn)是靈活,但缺點(diǎn)是不透明的、毫無意義的參數(shù)命名。它需要大量的用例文檔來解釋變形。

我們還可以擴(kuò)大我們的初始化來分裂Hand對(duì)象。分裂Hand對(duì)象的結(jié)果是只是另一個(gè)構(gòu)造函數(shù)。下面的代碼片段說明了如何分裂Hand對(duì)象:

class Hand4:
    def __init__(self, *args, **kw):
        if len(args) == 1 and isinstance(args[0], Hand4):
            # Clone an existing handl often a bad idea
            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 kw:
            # Split an existing hand
            other, card = args
            self.dealer_card = other.dealer_card
            self.cards = [other.cards[kw["split"]], card]
        elif len(args) == 3:
            # Build a fresh, new hand.
            dealer_card, *cards = args
            self.dealer_card =  dealer_card
            self.cards = list(cards)
        else:
            raise TypeError("Invalid constructor args={0!r} kw={1!r}".format(args, kw))
    def __str__(self):
        return ", ".join(map(str, self.cards))

這個(gè)設(shè)計(jì)包括獲得額外的牌來建立合適的、分裂的手牌。當(dāng)我們從一個(gè)Hand4對(duì)象創(chuàng)建一個(gè)Hand4對(duì)象,我們提供一個(gè)分裂的關(guān)鍵字參數(shù),它從原Hand4對(duì)象使用Card類索引。

下面的代碼片段展示了我們?nèi)绾问褂帽环至训氖峙疲?/p>

d = Deck()
h = Hand4(d.pop(), d.pop(), d.pop())
s1 = Hand4(h, d.pop(), split=0)
s2 = Hand4(h, d.pop(), split=1)

我們創(chuàng)建了一個(gè)Hand4初始化的h實(shí)例并分裂到兩個(gè)其他Hand4實(shí)例,s1s2,并處理額外的Card類。21點(diǎn)的規(guī)則只允許最初的手牌有兩個(gè)牌值相等。

雖然這個(gè)__init__()方法相當(dāng)復(fù)雜,它的優(yōu)點(diǎn)是可以并行的方式從現(xiàn)有集創(chuàng)建fronzenset。缺點(diǎn)是它需要一個(gè)大文檔字符串來解釋這些變化。

2. 初始化靜態(tài)方法

當(dāng)我們有多種方法來創(chuàng)建一個(gè)對(duì)象時(shí),有時(shí)會(huì)更清晰的使用靜態(tài)方法來創(chuàng)建并返回實(shí)例,而不是復(fù)雜的__init__()方法。

也可以使用類方法作為替代初始化,但是有一個(gè)實(shí)實(shí)在在的優(yōu)勢(shì)在于接收類作為參數(shù)的方法。在凍結(jié)或分裂Hand對(duì)象的情況下,我們可能需要?jiǎng)?chuàng)建兩個(gè)新的靜態(tài)方法凍結(jié)或分離對(duì)象。使用靜態(tài)方法作為代理構(gòu)造函數(shù)是一個(gè)小小的語法變化,但當(dāng)組織代碼的時(shí)候它擁有巨大的優(yōu)勢(shì)。

下面是一個(gè)有靜態(tài)方法的Hand,可用于從現(xiàn)有的Hand實(shí)例構(gòu)建新的Hand實(shí)例:

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

一個(gè)方法凍結(jié)或創(chuàng)建一個(gè)備份。另一個(gè)方法分裂Hand5實(shí)例來創(chuàng)建兩個(gè)Hand5實(shí)例。

這更具可讀性并保存參數(shù)名的使用來解釋接口。

下面的代碼片段展示了我們?nèi)绾瓮ㄟ^這個(gè)版本分裂Hand5實(shí)例:

d = Deck()
h = Hand5(d.pop(), d.pop(), d.pop())
s1, s2 = Hand5.split(h, d.pop(), d.pop())

我們創(chuàng)建了一個(gè)初始的Hand5h實(shí)例,分裂成兩個(gè)手牌,s1和s2,處理每一個(gè)額外的Card類。split()靜態(tài)方法比__init__()簡(jiǎn)單得多。然而,它不遵循從現(xiàn)有的set對(duì)象創(chuàng)建fronzenset對(duì)象的模式。

更多的__init__()技巧

我們會(huì)看看一些其他更高級(jí)的__init__()技巧。在前面的部分這些不是那么普遍有用的技術(shù)。

下面是Player類的定義,使用了兩個(gè)策略對(duì)象和table對(duì)象。這展示了一個(gè)看起來并不舒服的__init__()方法:

class Player:
    def __init__(self, table, bet_strategy, game_strategy):
        self.bet_strategy = bet_strategy
        self.game_strategy = game_strategy
        self.table = table
    def game(self):
        self.table.place_bet(self.bet_strategy.bet())
        self.hand = self.table.get_hand()
        if self.table.can_insure(self.hand):
            if self.game_strategy.insurance(self.hand):
                self.table.insure(self.bet_strategy.bet())
        # Yet more... Elided for now

Player__init__()方法似乎只是統(tǒng)計(jì)。只是簡(jiǎn)單傳遞命名好的參數(shù)到相同命名的實(shí)例變量。如果我們有大量的參數(shù),簡(jiǎn)單地傳遞參數(shù)到內(nèi)部變量會(huì)產(chǎn)生過多看似冗余的代碼。

我們可以如下使用Player類(和相關(guān)對(duì)象):

table = Table()
flat_bet = Flat()
dumb = GameStrategy()
p = Player(table, flat_bet, dumb)
p.game()

我們可以通過簡(jiǎn)單的傳遞關(guān)鍵字參數(shù)值到內(nèi)部實(shí)例變量來提供一個(gè)非常短的和非常靈活的初始化。

下面是使用關(guān)鍵字參數(shù)值構(gòu)建Player類的示例:

class Player2:
    def __init__(self, **kw):
        """Must provide table, bet_strategy, game_strategy."""
        self.__dict__.update(kw)
    def game(self):
        self.table.place_bet(self.bet_strategy.bet())
        self.hand= self.table.get_hand()
        if self.table.can_insure(self.hand):
            if self.game_strategy.insurance(self.hand):
                self.table.insure(self.bet_strategy.bet())
        # etc.

為了簡(jiǎn)潔而犧牲了大量可讀性。它跨越到一個(gè)潛在的默默無聞的領(lǐng)域。

因?yàn)?b>__init__()方法減少到一行,它消除了某種程度上“累贅”的方法。這個(gè)累贅,無論如何,是被傳遞到每個(gè)多帶帶的對(duì)象構(gòu)造函數(shù)表達(dá)式中。我們必須將關(guān)鍵字添加到對(duì)象初始化表達(dá)式中,因?yàn)槲覀儾辉偈褂梦恢脜?shù),如下面代碼片段所示:

p2 = Player2(table=table, bet_strategy=flat_bet, game_strategy=dumb)

為什么這樣做呢?

它有一個(gè)潛在的優(yōu)勢(shì)。這樣的類定義是相當(dāng)易于擴(kuò)展的。我們可能只有幾個(gè)特定的擔(dān)憂,提供額外關(guān)鍵字參數(shù)給構(gòu)造函數(shù)。

下面是預(yù)期的用例:

>>> p1 = Player2(table=table, bet_strategy=flat_bet, game_strategy=dumb)
>>> p1.game()

下面是一個(gè)額外的用例:

>>> p2 = Player2(table=table, bet_strategy=flat_bet, game_strategy=dumb, log_name="Flat/Dumb")
>>> p2.game()

我們添加了一個(gè)與類定義無關(guān)的log_name屬性。也許,這可以被用作統(tǒng)計(jì)分析的一部分。Player2.log_name屬性可以用來注釋日志或其他數(shù)據(jù)的收集。

我們能添加的東西是有限的;我們只能添加沒有與內(nèi)部使用的命名相沖突的參數(shù)。類實(shí)現(xiàn)的常識(shí)是需要的,用于創(chuàng)建沒有濫用已在使用的關(guān)鍵字的子類。由于**kw參數(shù)提供了很少的信息,我們需要仔細(xì)閱讀。在大多數(shù)情況下,比起檢查實(shí)現(xiàn)細(xì)節(jié)我們寧愿相信類是正常工作的。

在超類的定義中是可以做到基于關(guān)鍵字的初始化的,對(duì)于使用超類來實(shí)現(xiàn)子類會(huì)變得稍微的簡(jiǎn)單些。我們可以避免編寫一個(gè)額外的__init__()方法到每個(gè)子類,當(dāng)子類的唯一特性包括了簡(jiǎn)單新實(shí)例變量。

這樣做的缺點(diǎn)是,我們已經(jīng)模糊了沒有正式通過子類定義記錄的實(shí)例變量。如果只是一個(gè)小變量,整個(gè)子類可能有太多的編程開銷用于給一個(gè)類添加單個(gè)變量。然而,一個(gè)小變量常常會(huì)導(dǎo)致第二個(gè)、第三個(gè)。不久,我們將會(huì)認(rèn)識(shí)到一個(gè)子類會(huì)比一個(gè)極其靈活的超類還要更智能。

我們可以(也應(yīng)該)通過混合的位置和關(guān)鍵字實(shí)現(xiàn)生成這些,如下面的代碼片段所示:

class Player3(Player):
    def __init__(self, table, bet_strategy, game_strategy, **extras):
        self.bet_strategy = bet_strategy
        self.game_strategy = game_strategy
        self.table= table
        self.__dict__.update(extras)

這比完全開放定義更明智。我們已經(jīng)取得了所需的位置參數(shù)。我們留下任何非必需參數(shù)作為關(guān)鍵字。這個(gè)闡明了__init__()給出的任何額外的關(guān)鍵字參數(shù)的使用。

這種靈活的關(guān)鍵字初始化取決于我們是否有相對(duì)透明的類定義。這種開放的態(tài)度面對(duì)改變需要注意避免調(diào)試名稱沖突,因?yàn)殛P(guān)鍵字參數(shù)名是開放式的。

1. 初始化類型驗(yàn)證

類型驗(yàn)證很少是一個(gè)合理的要求。在某種程度上,是沒有對(duì)Python完全理解。名義目標(biāo)是驗(yàn)證所有參數(shù)是否是一個(gè)合適的類型。試圖這樣做的原因主要是因?yàn)?em>適當(dāng)的定義往往是過于狹隘以至于沒有什么真正的用途。

這不同于確認(rèn)對(duì)象滿足其他條件。數(shù)字范圍檢查,例如,防止無限循環(huán)的必要。

我們可以制造問題去試圖做些什么,就像下面__init__()方法中那樣:

class ValidPlayer:
    def __init__(self, table, bet_strategy, game_strategy):
        assert isinstance(table, Table)
        assert isinstance(bet_strategy, BettingStrategy)
        assert isinstance(game_strategy, GameStrategy)
        self.bet_strategy = bet_strategy
        self.game_strategy = game_strategy
        self.table = table

isinstance()方法檢查、規(guī)避Python的標(biāo)準(zhǔn)鴨子類型。

我們寫一個(gè)賭場(chǎng)游戲模擬是為了嘗試不斷變化的GameStrategy。這些很簡(jiǎn)單(僅僅四個(gè)方法),幾乎沒有從超類的繼承中得到任何幫助。我們可以獨(dú)立的定義缺乏整體的超類。

這個(gè)示例中所示的初始化錯(cuò)誤檢查,將迫使我們通過錯(cuò)誤檢查的創(chuàng)建子類。沒有可用的代碼是繼承自抽象超類。

最大的一個(gè)鴨子類型問題就圍繞數(shù)值類型。不同的數(shù)值類型將工作在不同的上下文中。試圖驗(yàn)證類型的爭(zhēng)論可能會(huì)阻止一個(gè)完美合理的數(shù)值類型正常工作。當(dāng)嘗試驗(yàn)證時(shí),我們有以下兩個(gè)選擇在Python中:

我們編寫驗(yàn)證,這樣一個(gè)相對(duì)狹窄的集合類型是允許的,總有一天代碼會(huì)因?yàn)槁斆鞯男骂愋捅唤苟袛唷?/p>

我們避開驗(yàn)證,這樣一個(gè)相對(duì)廣泛的集合類型是允許的,總有一天代碼會(huì)因?yàn)椴宦斆鞯仡愋捅皇褂枚袛唷?/p>

注意,兩個(gè)本質(zhì)上是相同的。代碼可能有一天被中斷。要么因?yàn)榻故褂眉词顾锹斆?,要么因?yàn)椴宦斆鞯氖褂谩?/p>

讓它

一般來說,更好的Python風(fēng)格就是簡(jiǎn)單地允許使用任何類型的數(shù)據(jù)。

我們將在第4章《一致設(shè)計(jì)的基本知識(shí)》回到這個(gè)問題。

這個(gè)問題是:為什么限制未來潛在的用例?

通?;卮鹗?,沒有理由限制未來潛在的用例。

比起阻止一個(gè)聰明的,但可能是意料之外的用例,我們可以提供文檔、測(cè)試和調(diào)試日志幫助其他程序員理解任何可以處理的限制類型。我們必須提供文檔、日志和測(cè)試用例,這樣額外的工作開銷最小。

下面是一個(gè)示例文檔字符串,它提供了對(duì)類的預(yù)期:

class Player:
    def __init__(self, table, bet_strategy, game_strategy):
        """Creates a new player associated with a table,
            and configured with proper betting and play strategies
            :param table: an instance of :class:`Table`
            :param bet_strategy: an instance of :class:`BettingStrategy`
            :param  game_strategy: an instance of :class:`GameStrategy`
        """
        self.bet_strategy = bet_strategy
        self.game_strategy = game_strategy
        self.table = table

程序員使用這個(gè)類已經(jīng)被警告了限制類型是什么。其他類型的使用是被允許的。如果類型不符合預(yù)期,執(zhí)行會(huì)中斷。理想情況下,我們將使用unittestdoctest來發(fā)現(xiàn)bug。

2. 初始化、封裝和私有

一般Python關(guān)于私有的政策可以總結(jié)如下:我們都是成年人了。

面向?qū)ο蟮脑O(shè)計(jì)有顯式接口和實(shí)現(xiàn)之間的區(qū)別。這是封裝的結(jié)果。類封裝了數(shù)據(jù)結(jié)構(gòu)、算法、一個(gè)外部接口或者一些有意義的事情。這個(gè)想法是從實(shí)現(xiàn)細(xì)節(jié)封裝分離基于類的接口。

但是,沒有編程語言反映了每一個(gè)設(shè)計(jì)細(xì)節(jié)。Python中,通常情況下,并沒有考慮都用顯式代碼實(shí)現(xiàn)所有設(shè)計(jì)。

類的設(shè)計(jì),一方面是沒有完全在代碼中有私有(實(shí)現(xiàn))和公有(接口)方法或?qū)傩詫?duì)象的區(qū)別。私有的概念主要來自(c++或Java)語言,這已經(jīng)很復(fù)雜了。這些語言設(shè)置包括如私有、保護(hù)、和公有以及“未指定”,這是一種半專用的。私有關(guān)鍵字的使用不當(dāng),通常使得子類定義產(chǎn)生不必要的困難。

Python私有的概念很簡(jiǎn)單,如下

本質(zhì)上都是公有的。源代碼是可用的。我們都是成年人。沒有什么可以真正隱藏的。

一般來說,我們會(huì)把一些名字的方式公開。他們普遍實(shí)現(xiàn)細(xì)節(jié),如有變更,恕不另行通知,但是沒有正式的私有的概念。

在部分Python中,命名以_開頭的一般是非公有的。help()函數(shù)通常忽略了這些方法。Sphinx等工具可以從文檔隱藏這些名字。

Python的內(nèi)部命名是以__開始(結(jié)束)的。這就是Python保持內(nèi)部不與應(yīng)用程序的命名起沖突。這些內(nèi)部的集合名稱完全是由語言內(nèi)部參考定義的。此外,在我們的代碼中嘗試使用__試圖創(chuàng)建“超級(jí)私人”屬性或方法是沒有任何好處的。一旦Python的發(fā)行版本開始使用我們選擇內(nèi)部使用的命名,會(huì)造成潛在的問題。同樣,我們使用這些命名很可能與內(nèi)部命名發(fā)生沖突。

Python的命名規(guī)則如下:

大多數(shù)命名是公有的。

_開頭的都是非公有的。使用它們來實(shí)現(xiàn)細(xì)節(jié)是真正可能發(fā)生變化的。

__開頭或結(jié)尾的命名是Python內(nèi)部的。我們不能這樣命名;我們使用語言參考定義的名稱。

一般情況下,Python方法使用文檔和好的命名來表達(dá)一個(gè)方法(或?qū)傩?的意圖。通常,接口方法會(huì)有復(fù)雜的文檔,可能包括doctest的示例,而實(shí)現(xiàn)方法將有更多的簡(jiǎn)寫文檔,很可能沒有doctest示例。

新手Python程序員,有時(shí)奇怪私有沒有得到更廣泛的使用。而經(jīng)驗(yàn)豐富的Python程序員,卻驚訝于為了整理并不實(shí)用的私有和公有聲明去消耗大腦的卡路里,因?yàn)閺姆椒ǖ拿臀臋n中就能知道變量名的意圖。

總結(jié)

在本章中,我們回顧了__init__()方法的各種設(shè)計(jì)方案。在下一章,我們將看一看特別的以及一些高級(jí)的方法。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/45375.html

相關(guān)文章

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

    摘要:簡(jiǎn)單復(fù)合對(duì)象復(fù)合對(duì)象也可被稱為容器。它難以序列化對(duì)象并像這樣初始化來重建。接口仍然會(huì)導(dǎo)致多種方法計(jì)算。還要注意一些不完全遵循點(diǎn)規(guī)則的方法功能。逐步增加項(xiàng)目的方法和一步加載所有項(xiàng)目的方法是一樣的。另一個(gè)方法就是之前那樣的類定義。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python 在各個(gè)子類中實(shí)現(xiàn)__init_...

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

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

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

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

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

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

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

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

0條評(píng)論

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