<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      翻譯:《實用的Python編程》05_02_Classes_encapsulation

      目錄 | 上一節 (5.1 再談字典) | 下一節 (6 生成器)

      5.2 類和封裝

      創建類時,通常會嘗試將類的內部細節進行封裝。本節介紹 Python 編程中有關封裝的習慣用法(包括私有變量和私有屬性)。

      Public vs Private

      雖然類的主要作用之一是封裝對象的屬性和內部實現細節。但是,類還定義了外界用來操作該對象的公有接口(public interface)。實現細節與公有接口之間的區別很重要。

      問題

      在 Python 中,幾乎所有與類和對象有關的東西都是開放(open)的。

      • 可以輕松地查看對象的內部細節。
      • 可以隨意地修改。
      • 沒有訪問控制的概念(例如:私有類成員)。

      如何隔離內部實現的細節,這是一個問題。

      Python 封裝

      Python 依賴編程約定來指示某些東西的用途。這就約定基于命名。有一種普遍的態度是,程序員應該遵守規則,而不是讓語言來強制執行規則。

      私有屬性

      以下劃線 _ 開頭的任何屬性被認為是私有的(private)。

      class Person(object):
          def __init__(self, name):
              self._name = 0
      

      如前所述,這這是一種編程風格。你仍然可以對這些私有屬性進行訪問和修改。

      >>> p = Person('Guido')
      >>> p._name
      'Guido'
      >>> p._name = 'Dave'
      >>>
      

      一般來說,一個以下劃線 _ 開頭的名稱被認為是內部實現,無論該名稱是變量名、函數名還是模塊名。如果你發現自己直接使用這些名稱,那么你可能在做一些錯誤的事情。你應該尋找更高級的功能。

      簡單屬性

      考慮下面這個類:

      class Stock:
          def __init__(self, name, shares, price):
              self.name = name
              self.shares = shares
              self.price = price
      

      這里有一個讓人驚訝的特性,你可以給屬性設置任何值:

      >>> s = Stock('IBM', 50, 91.1)
      >>> s.shares = 100
      >>> s.shares = "hundred"
      >>> s.shares = [1, 0, 0]
      >>>
      

      你可能會想要對此進行檢查(譯注:例如 shares 表示的是股份數目,值應該是整數。所以給 shares 賦值時應該對值進行檢查。如果檢查發現給 shares 賦的值不是整數,那么應該觸發一個 TypeError 異常):

      s.shares = '50'     # Raise a TypeError, this is a string
      

      這時候你會怎么做?

      托管屬性

      方法一:引進訪問方法(accessor methods)。

      class Stock:
          def __init__(self, name, shares, price):
              self.name = name
      	    self.set_shares(shares)
      	    self.price = price
      
          # Function that layers the "get" operation
          def get_shares(self):
              return self._shares
      
          # Function that layers the "set" operation
          def set_shares(self, value):
              if not isinstance(value, int):
                  raise TypeError('Expected an int')
              self._shares = value
      

      糟糕的是,這破壞了我們的已有代碼。例如:之前是通過 s.shares = 50shares 賦值的,那么現在就要改成s.set_shares(50)shares 賦值,這很不好。

      特征屬性(Properties)

      方法二:

      class Stock:
          def __init__(self, name, shares, price):
              self.name = name
              self.shares = shares
              self.price = price
      
          @property
          def shares(self):
              return self._shares
      
          @shares.setter
          def shares(self, value):
              if not isinstance(value, int):
                  raise TypeError('Expected int')
              self._shares = value
      

      現在,普通屬性(normal attribute)的訪問觸發了 @property@shares.setter 下的 getter 方法和 setter 方法。

      >>> s = Stock('IBM', 50, 91.1)
      >>> s.shares         # Triggers @property
      50
      >>> s.shares = 75    # Triggers @shares.setter
      >>>
      

      使用該方法,不需要對源代碼做任何修改。在類內(包括在 __init__() 方法內)有賦值的時候,直接調用新的 setter:

      class Stock:
          def __init__(self, name, shares, price):
              ...
              # This assignment calls the setter below
              self.shares = shares
              ...
      
          ...
          @shares.setter
          def shares(self, value):
              if not isinstance(value, int):
                  raise TypeError('Expected int')
              self._shares = value
      

      特征屬性和私有名稱( private names)的使用之間經常會出現混淆。盡管特征屬性內部使用的是私有名稱,如 _shares。類的其它地方(不是特征屬性),仍可以繼續使用諸如 shares 這樣的名稱。

      特征屬性對于計算數據屬性也非常有用。

      class Stock:
          def __init__(self, name, shares, price):
              self.name = name
              self.shares = shares
              self.price = price
      
          @property
          def cost(self):
              return self.shares * self.price
          ...
      

      這允許你刪除 cost 后面的括號,隱藏 cost 是一個方法的事實:

      >>> s = Stock('GOOG', 100, 490.1)
      >>> s.shares # Instance variable
      100
      >>> s.cost   # Computed Value
      49010.0
      >>>
      

      統一訪問

      最后一個例子展示了如何在對象上放置一個更加統一的接口。如果不這樣做,對象使用起來可能會令人困惑。

      >>> s = Stock('GOOG', 100, 490.1)
      >>> a = s.cost() # Method
      49010.0
      >>> b = s.shares # Data attribute
      100
      >>>
      

      為什么 cost 后面需要加上括號 (),但是 shares 卻不需要? 特征屬性可以解決這個問題。

      裝飾器語法

      @ 語法稱為“裝飾(decoration)”。它指定了一個修飾符(modifier),應用于緊接其后的函數定義:

      ...
      @property
      def cost(self):
          return self.shares * self.price
      

      更多細節在 第 7 節 中給到。

      插槽屬性(__slots__

      你可以使用 __slots__ 限制屬性名稱集:

      class Stock:
          __slots__ = ('name','_shares','price')
          def __init__(self, name, shares, price):
              self.name = name
              ...
      

      使用其它屬性時,將會觸發錯誤:

      >>> s.price = 385.15
      >>> s.prices = 410.2
      Traceback (most recent call last):
      File "<stdin>", line 1, in ?
      AttributeError: 'Stock' object has no attribute 'prices'
      

      管這樣可以防止錯誤和限制對象的使用,但實際上使用 __slots__ 是為了提高性能,提高 Python 利用內存的效率。

      關于封裝的最終說明

      不要濫用私有屬性(private attributes),特征屬性(properties),插槽屬性(slots)等。它們有特殊的用途,你在閱讀其它 Python 代碼時可能會看到。但是,對于大多數日常編碼而言,它們不是必需的。

      練習

      練習 5.6:簡單特征屬性

      使用特征屬性是一種非常有用的給對象添加“計算屬性”的方式。雖然你在 stock.py 文件中創建了 Stock 對象,但是請注意,在 Stock 對象上 ,對于不同類型的屬性,獲取方式稍微有點不同。

      >>> from stock import Stock
      >>> s = Stock('GOOG', 100, 490.1)
      >>> s.shares
      100
      >>> s.price
      490.1
      >>> s.cost()
      49010.0
      >>>
      

      具體來說,cost 后面之所以要添加括號,是因為 cost 是一個方法。

      如果你想去掉 cost() 的括號,那么可以把該方法轉為一個特征屬性。請修改 Stock 類,使其像下面這樣計算所持有股票的總價:

      >>> ================================ RESTART ================================
      >>> from stock import Stock
      >>> s = Stock('GOOG', 100, 490.1)
      >>> s.cost
      49010.0
      >>>
      

      嘗試將 cost作為方法調用(s.cost()),你會發現,現在已經被定義為特征屬性的 cost 無法作為方法被調用。

      >>> s.cost()
      ... fails ...
      >>>
      

      這些更改很可能會破壞你之前的 pcost.py 程序,所以,你可能需要返回到 pcost.py 中去掉 cost() 方法后面的括號()

      練習 5.7:特征屬性和 Setters

      請修改 shares 屬性,以便將該值存儲在私有屬性中,并且使用屬性函數(property functions)確保賦給 shares 的值總是整數。預期行為示例:

      >>> ================================ RESTART ================================
      >>> from stock import Stock
      >>> s = Stock('GOOG',100,490.10)
      >>> s.shares = 50
      >>> s.shares = 'a lot'
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      TypeError: expected an integer
      >>>
      

      練習 5.8:添加插槽屬性(slots)

      請修改 Stock 類,以便 Stock 類擁有一個 __slots__ 屬性。然后確認無法添加新屬性:

      >>> ================================ RESTART ================================
      >>> from stock import Stock
      >>> s = Stock('GOOG', 100, 490.10)
      >>> s.name
      'GOOG'
      >>> s.blah = 42
      ... see what happens ...
      >>>
      

      使用 __slots__ 時,Python 使用更高效的對象內部表示。如果你嘗試查看實例 s 的底層字典會發生什么?

      >>> s.__dict__
      ... see what happens ...
      >>>
      

      應當指出, __slots__ 作為數據結構是類中最常用的一種優化。使用插槽屬性使程序占用更少的內存,運行更快。但是,在其它大多數類中,你應該盡可能避免使用 __slots__

      目錄 | 上一節 (5.1 再談字典) | 下一節 (6 生成器)

      注:完整翻譯見 https://github.com/codists/practical-python-zh

      posted @ 2021-03-13 22:29  codists  閱讀(244)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲av伊人久久综合性色| 视频二区中文字幕在线| 国产成人毛片无码视频软件| 在线日韩日本国产亚洲| 国产精品自拍一二三四区| 少妇人妻精品一区二区| 国产成人无码区免费内射一片色欲 | 久久月本道色综合久久| 亚洲精品一区国产精品| 亚洲AV无码精品色午夜果冻 | 天堂资源国产老熟女在线| 白城市| 午夜射精日本三级| 亚洲香蕉网久久综合影视| 午夜毛片精彩毛片| 国产亚洲精品超碰热| 做暖暖视频在线看片免费| 国产v综合v亚洲欧美久久| 日韩精品一区二区三区vr| 国产中文字幕日韩精品| 台前县| 免费国产一区二区不卡| 午夜成人精品福利网站在线观看| 樱花草在线社区WWW韩国| 国产精品一品二区三四区| 狠狠色狠狠色综合日日不卡| 亚洲精品日韩中文字幕| 国产精品久久久久影院| 国产美女被遭强高潮免费一视频| 亚洲熟妇少妇任你躁在线观看无码| 亚洲熟女乱色一区二区三区| 亚洲熟女乱综合一区二区| 日本一道一区二区视频 | 国产综合一区二区三区麻豆| 成人国产精品中文字幕| 精品亚洲国产成人av| 中年国产丰满熟女乱子正在播放| 国产成人一区二区三区在线观看| 日韩加勒比一本无码精品| 亚洲精品沙发午睡系列| 你懂的视频在线一区二区|