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

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

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

      翻譯:《實用的Python編程》04_02_Inheritance

      目錄 | 上一節 (4.1 類) | 下一節 (4.3 特殊方法)

      4.2 繼承

      繼承(inheritance)是編寫可擴展程序程序的常用手段。本節對繼承的思想(idea)進行探討。

      簡介

      繼承用于特殊化現有對象:

      class Parent:
          ...
      
      class Child(Parent):
          ...
      

      新類 Child 稱為派生類(derived class)或子類(subclass)。類 Parent 稱為基類(base class)或超類(superclass)。在子類名后的括號 () 中指定基類(Parent),class Child(Parent):

      擴展

      使用繼承,你可以獲取現有的類,并且可以:

      • 添加新方法
      • 重新定義現有方法
      • 向實例添加新屬性

      最后,你擴展了現有代碼

      示例

      假設這是開始的類:

      class Stock:
          def __init__(self, name, shares, price):
              self.name = name
              self.shares = shares
              self.price = price
      
          def cost(self):
              return self.shares * self.price
      
          def sell(self, nshares):
              self.shares -= nshares
      

      你可以通過繼承更改 Stock 類的任何部分。

      添加新方法

      class MyStock(Stock):
          def panic(self):
              self.sell(self.shares)
      

      (譯注:“panic” 在這里表示的是“panic selling”,恐慌性拋售)

      使用示例:

      >>> s = MyStock('GOOG', 100, 490.1)
      >>> s.sell(25)
      >>> s.shares
      75
      >>> s.panic()
      >>> s.shares
      0
      >>>
      

      重新定義現有方法

      class MyStock(Stock):
          def cost(self):
              return 1.25 * self.shares * self.price
      

      使用示例:

      >>> s = MyStock('GOOG', 100, 490.1)
      >>> s.cost()
      61262.5
      >>>
      

      新的 cost() 方法代替了舊的 cost() 方法。其它的方法不受影響。

      方法覆蓋

      有時候,一個類既想擴展現有方法,同時又想在新的定義中使用原有的實現。為此,可以使用 super() 函數實現(譯注:方法覆蓋 有時也譯為 方法重寫):

      class Stock:
          ...
          def cost(self):
              return self.shares * self.price
          ...
      
      class MyStock(Stock):
          def cost(self):
              # Check the call to `super`
              actual_cost = super().cost()
              return 1.25 * actual_cost
      

      使用內置函數 super() 調用之前的版本。

      注意:在 Python 2 中,語法更加冗余,像下面這樣:

      actual_cost = super(MyStock, self).cost()
      

      __init__ 和繼承

      如果 __init__ 方法在子類中被重新定義,那么有必要初始化父類。

      class Stock:
          def __init__(self, name, shares, price):
              self.name = name
              self.shares = shares
              self.price = price
      
      class MyStock(Stock):
          def __init__(self, name, shares, price, factor):
              # Check the call to `super` and `__init__`
              super().__init__(name, shares, price)
              self.factor = factor
      
          def cost(self):
              return self.factor * super().cost()
      

      你需要使用 super 調用父類的 __init__() 方法,如前所示,這是調用先前版本的方法。

      使用繼承

      有時候,繼承用于組織相關的對象。

      class Shape:
          ...
      
      class Circle(Shape):
          ...
      
      class Rectangle(Shape):
          ...
      

      要組織相關的對象,可以考慮使用邏輯層次結構或者進行分類。然而,一種更常見(更實用)的做法是創建可重用和可擴展的代碼。例如,一個框架可能會定義一個基類,并指導你對其進行自定義。

      class CustomHandler(TCPHandler):
          def handle_request(self):
              ...
              # Custom processing
      

      基類包含了通用代碼。你的類繼承基類并自定義特殊的部分。

      “is a” 關系

      繼承建立了一種類型關系。

      class Shape:
          ...
      
      class Circle(Shape):
          ...
      

      檢查對象實例:

      >>> c = Circle(4.0)
      >>> isinstance(c, Shape)
      True
      >>>
      

      重要提示:理想情況下,任何使用父類實例能正常工作的代碼也能使用子類的實例正常工作。

      object 基類

      如果一個類沒有父類,那么有時候你會看到它們使用 object 作為基類。

      class Shape(object):
          ...
      

      在 Python 中,object 是所有對象的基類。

      注意:在技術上,它不是必需的,但是你通常會看到 object 在 Python 2 中被保留。如果省略,類仍然隱式繼承自 object

      多重繼承

      你可以通過在類定義中指定多個基類來實現多重繼承。

      class Mother:
          ...
      
      class Father:
          ...
      
      class Child(Mother, Father):
          ...
      

      Child 類繼承了兩個父類(Mother,Father)的特性。這里有一些相當棘手的細節。除非你知道你正在做什么,否則不要這樣做。雖然更多信息會在下一節給到,但是我們不會在本課程中進一步使用多重繼承。

      練習

      繼承的一個主要用途是:以各種方式編寫可擴展和可定制的代碼——尤其是在庫或框架中。要說明這點,請考慮 report.py 程序中的 print_report() 函數。它看起來應該像下面這樣:

      def print_report(reportdata):
          '''
          Print a nicely formated table from a list of (name, shares, price, change) tuples.
          '''
          headers = ('Name','Shares','Price','Change')
          print('%10s %10s %10s %10s' % headers)
          print(('-'*10 + ' ')*len(headers))
          for row in reportdata:
              print('%10s %10d %10.2f %10.2f' % row)
      

      當運行 report.py 程序,你應該會獲得像下面這樣的輸出:

      >>> import report
      >>> report.portfolio_report('Data/portfolio.csv', 'Data/prices.csv')
            Name     Shares      Price     Change
      ---------- ---------- ---------- ----------
              AA        100       9.22     -22.98
             IBM         50     106.28      15.18
             CAT        150      35.46     -47.98
            MSFT        200      20.89     -30.34
              GE         95      13.48     -26.89
            MSFT         50      20.89     -44.21
             IBM        100     106.28      35.84
      

      練習 4.5:擴展性問題

      假設你想修改 print_report() 函數,以支持各種不同的輸出格式,例如純文本,HTML, CSV,或者 XML。為此,你可以嘗試編寫一個龐大的函數來實現每一個功能。但是,這樣做可能會導致代碼非常混亂,無法維護。這是一個使用繼承的絕佳機會。

      首先,請關注創建表所涉及的步驟。在表的頂部是標題。標題的后面是數據行。讓我們使用這些步驟把它們放到各自的類中吧。創建一個名為 tableformat.py 的文件,并定義以下類:

      # tableformat.py
      
      class TableFormatter:
          def headings(self, headers):
              '''
              Emit the table headings.
              '''
      	raise NotImplementedError()
      
          def row(self, rowdata):
              '''
              Emit a single row of table data.
              '''
      	raise NotImplementedError()
      

      除了稍后用作定義其它類的設計規范,該類什么也不做。有時候,這樣的類被稱為“抽象基類”。

      請修改 print_report() 函數,使其接受一個 TableFormatter 對象作為輸入,并執行 TableFormatter 的方法來生成輸出。示例:

      # report.py
      ...
      
      def print_report(reportdata, formatter):
          '''
          Print a nicely formated table from a list of (name, shares, price, change) tuples.
          '''
          formatter.headings(['Name','Shares','Price','Change'])
          for name, shares, price, change in reportdata:
              rowdata = [ name, str(shares), f'{price:0.2f}', f'{change:0.2f}' ]
              formatter.row(rowdata)
      

      因為你在 portfolio_report() 函數中增加了一個參數,所以你也需要修改 portfolio_report() 函數。請修改 portfolio_report() 函數,以便像下面這樣創建 TableFormatter

      # report.py
      
      import tableformat
      
      ...
      def portfolio_report(portfoliofile, pricefile):
          '''
          Make a stock report given portfolio and price data files.
          '''
          # Read data files
          portfolio = read_portfolio(portfoliofile)
          prices = read_prices(pricefile)
      
          # Create the report data
          report = make_report_data(portfolio, prices)
      
          # Print it out
          formatter = tableformat.TableFormatter()
          print_report(report, formatter)
      

      運行新代碼:

      >>> ================================ RESTART ================================
      >>> import report
      >>> report.portfolio_report('Data/portfolio.csv', 'Data/prices.csv')
      ... crashes ...
      

      程序應該會馬上崩潰,并附帶一個 NotImplementedError 異常。雖然這沒有那么令人興奮,但是結果確實是我們期待的。繼續下一步部分。

      練習 4.6:使用繼承生成不同的輸出

      在 a 部分定義的 TableFormatter 類旨在通過繼承進行擴展。實際上,這就是整個思想。要說明這點,請像下面這樣定義 TextTableFormatter 類:

      # tableformat.py
      ...
      class TextTableFormatter(TableFormatter):
          '''
          Emit a table in plain-text format
          '''
          def headings(self, headers):
              for h in headers:
                  print(f'{h:>10s}', end=' ')
              print()
              print(('-'*10 + ' ')*len(headers))
      
          def row(self, rowdata):
              for d in rowdata:
                  print(f'{d:>10s}', end=' ')
              print()
      

      請像下面這樣修改 portfolio_report() 函數:

      # report.py
      ...
      def portfolio_report(portfoliofile, pricefile):
          '''
          Make a stock report given portfolio and price data files.
          '''
          # Read data files
          portfolio = read_portfolio(portfoliofile)
          prices = read_prices(pricefile)
      
          # Create the report data
          report = make_report_data(portfolio, prices)
      
          # Print it out
          formatter = tableformat.TextTableFormatter()
          print_report(report, formatter)
      

      這應該會生成和之前一樣的輸出:

      >>> ================================ RESTART ================================
      >>> import report
      >>> report.portfolio_report('Data/portfolio.csv', 'Data/prices.csv')
            Name     Shares      Price     Change
      ---------- ---------- ---------- ----------
              AA        100       9.22     -22.98
             IBM         50     106.28      15.18
             CAT        150      35.46     -47.98
            MSFT        200      20.89     -30.34
              GE         95      13.48     -26.89
            MSFT         50      20.89     -44.21
             IBM        100     106.28      35.84
      >>>
      

      但是,讓我們更改輸出為其它內容。定義一個以 CSV 格式生成輸出的 CSVTableFormatter

      # tableformat.py
      ...
      class CSVTableFormatter(TableFormatter):
          '''
          Output portfolio data in CSV format.
          '''
          def headings(self, headers):
              print(','.join(headers))
      
          def row(self, rowdata):
              print(','.join(rowdata))
      

      請像下面這樣修改主程序:

      def portfolio_report(portfoliofile, pricefile):
          '''
          Make a stock report given portfolio and price data files.
          '''
          # Read data files
          portfolio = read_portfolio(portfoliofile)
          prices = read_prices(pricefile)
      
          # Create the report data
          report = make_report_data(portfolio, prices)
      
          # Print it out
          formatter = tableformat.CSVTableFormatter()
          print_report(report, formatter)
      

      然后,你應該會看到像下面這樣的 CSV 輸出:

      >>> ================================ RESTART ================================
      >>> import report
      >>> report.portfolio_report('Data/portfolio.csv', 'Data/prices.csv')
      Name,Shares,Price,Change
      AA,100,9.22,-22.98
      IBM,50,106.28,15.18
      CAT,150,35.46,-47.98
      MSFT,200,20.89,-30.34
      GE,95,13.48,-26.89
      MSFT,50,20.89,-44.21
      IBM,100,106.28,35.84
      

      運用類似的思想,定義一個 HTMLTableFormatter 類,生成具有以下輸出的表格:

      <tr><th>Name</th><th>Shares</th><th>Price</th><th>Change</th></tr>
      <tr><td>AA</td><td>100</td><td>9.22</td><td>-22.98</td></tr>
      <tr><td>IBM</td><td>50</td><td>106.28</td><td>15.18</td></tr>
      <tr><td>CAT</td><td>150</td><td>35.46</td><td>-47.98</td></tr>
      <tr><td>MSFT</td><td>200</td><td>20.89</td><td>-30.34</td></tr>
      <tr><td>GE</td><td>95</td><td>13.48</td><td>-26.89</td></tr>
      <tr><td>MSFT</td><td>50</td><td>20.89</td><td>-44.21</td></tr>
      <tr><td>IBM</td><td>100</td><td>106.28</td><td>35.84</td></tr>
      

      請通過修改主程序來測試你的代碼。 主程序創建的是 HTMLTableFormatter 對象,而不是 CSVTableFormatter 對象。

      練習 4.7:多態

      面向對象編程(oop)的一個主要特性是:可以將對象插入程序中,并且不必更改現有代碼即可運行。例如,如果你編寫了一個預期會使用 TableFormatter 對象的程序,那么不管你給它什么類型的 TableFormatter ,它都能正常工作。這樣的行為有時被稱為“多態”。

      一個需要指出的潛在問題是:弄清楚如何讓用戶選擇它們想要的格式。像 TextTableFormatter 一樣直接使用類名通常有點煩人。因此,你應該考慮一些簡化的方法。如:你可以在代碼中嵌入 if 語句:

      def portfolio_report(portfoliofile, pricefile, fmt='txt'):
          '''
          Make a stock report given portfolio and price data files.
          '''
          # Read data files
          portfolio = read_portfolio(portfoliofile)
          prices = read_prices(pricefile)
      
          # Create the report data
          report = make_report_data(portfolio, prices)
      
          # Print it out
          if fmt == 'txt':
              formatter = tableformat.TextTableFormatter()
          elif fmt == 'csv':
              formatter = tableformat.CSVTableFormatter()
          elif fmt == 'html':
              formatter = tableformat.HTMLTableFormatter()
          else:
              raise RuntimeError(f'Unknown format {fmt}')
          print_report(report, formatter)
      

      雖然在此代碼中,用戶可以指定一個簡化的名稱(如'txt''csv')來選擇格式,但是,像這樣在 portfolio_report() 函數中使用大量的 if 語句真的是最好的思想嗎?把這些代碼移入其它通用函數中可能更好。

      tableformat.py 文件中,請添加一個名為 create_formatter(name) 的函數,該函數允許用戶創建給定輸出名(如'txt''csv',或 'html')的格式器(formatter)。請像下面這樣修改 portfolio_report() 函數:

      def portfolio_report(portfoliofile, pricefile, fmt='txt'):
          '''
          Make a stock report given portfolio and price data files.
          '''
          # Read data files
          portfolio = read_portfolio(portfoliofile)
          prices = read_prices(pricefile)
      
          # Create the report data
          report = make_report_data(portfolio, prices)
      
          # Print it out
          formatter = tableformat.create_formatter(fmt)
          print_report(report, formatter)
      

      嘗試使用不同的格式調用該函數,確保它能夠正常工作。

      練習 4.8:匯總

      請修改 report.py 程序,以便 portfolio_report() 函數使用可選參數指定輸出格式。示例:

      >>> report.portfolio_report('Data/portfolio.csv', 'Data/prices.csv', 'txt')
            Name     Shares      Price     Change
      ---------- ---------- ---------- ----------
              AA        100       9.22     -22.98
             IBM         50     106.28      15.18
             CAT        150      35.46     -47.98
            MSFT        200      20.89     -30.34
              GE         95      13.48     -26.89
            MSFT         50      20.89     -44.21
             IBM        100     106.28      35.84
      >>>
      

      請修改主程序,以便可以在命令行上指定輸出格式:

      bash $ python3 report.py Data/portfolio.csv Data/prices.csv csv
      Name,Shares,Price,Change
      AA,100,9.22,-22.98
      IBM,50,106.28,15.18
      CAT,150,35.46,-47.98
      MSFT,200,20.89,-30.34
      GE,95,13.48,-26.89
      MSFT,50,20.89,-44.21
      IBM,100,106.28,35.84
      bash $
      

      討論

      在庫和框架中,編寫可擴展程序是繼承的最常見用途之一。例如,框架指導你定義一個自己的對象,該對象繼承自已提供的基類。然后你可以添加實現各種功能的函數。

      另一個更深層次的概念是“擁有抽象的思想”。在練習中,我們定義了自己的類,用于格式化表格。你可能會看一下自己的代碼,然后告訴自己“我應該只使用格式化庫或其它人已經編寫的東西!”。不,你應該同時使用自己的類和庫。使用自己的類可以降低程序的耦合性,增加程序的靈活性。只要你的程序使用的應用接口來自于自己定義的類,那么,只要你想,你就可以更改程序的內部實現以使其按照你想的那樣工作。你可以編寫全定制(all-custom)代碼,也可以使用第三方包(package)。當發現更好的包時,你可以將一個第三方包替換為另一個包。這并不重要——只要你保留這個接口,應用程序代碼都不會中斷。這是一種強大的思想,這也是為什么應該使用繼承的原因之一。

      也就是說,設計面向對象的程序可能會非常困難。想了解更多信息,你可能應該尋找一本有關設計模式主題的書看一下(盡管理解本練習中的內容已經讓你以一種實用的方式在使用對象方面走得很遠了)。

      目錄 | 上一節 (4.1 類) | 下一節 (4.3 特殊方法)

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

      posted @ 2021-03-08 18:12  codists  閱讀(352)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 精品国产欧美一区二区三区在线| 成人免费区一区二区三区| 精品国内自产拍在线观看| 日韩精品无遮挡在线观看| 国产成熟女人性满足视频| 国内自拍av在线免费| 免费无码AV一区二区波多野结衣| 长宁区| 国产在线午夜不卡精品影院| 国产台湾黄色av一区二区| 色噜噜狠狠成人综合| 久久一日本综合色鬼综合色| 亚洲中文字幕五月五月婷| 精品国精品无码自拍自在线| 日本一区二区三区视频版| 日韩免费码中文在线观看| 在线看无码的免费网站| 少妇伦子伦精品无吗| 中国女人内谢69xxxx| av在线播放无码线| 国产二区三区不卡免费| 国产精品大片中文字幕| 亚洲欧美电影在线一区二区| 麻豆a级片| 久久人妻精品大屁股一区| 国产成人精品视频不卡| 国产精品午夜精品福利| 国产激情文学亚洲区综合| 中文字幕无码不卡免费视频| 娇妻玩4p被三个男人伺候| 国产午夜亚洲精品福利| 中文字幕在线无码一区二区三区| 久久夜色噜噜噜亚洲av| 婷婷四虎东京热无码群交双飞视频| 无码日韩做暖暖大全免费不卡| 亚洲成人午夜排名成人午夜| 男男车车的车车网站w98免费| 一区二区三区无码免费看| 成人国产永久福利看片| 国产成人午夜在线视频极速观看| 一区二区三区四区五区色|