>>classPizza(object):...def__init__(self,size):...self.size=size...defget_size(self):...returnself.size...>>>Pizza.get_sizePython在告訴你,屬性_get_size是類Pizza的一個未綁定方法。這是什么意思呢?很快我們" />

欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

深入理解Python中各種方法的運作原理

系統 1617 0

方法在Python中是如何工作的

方法就是一個函數,它作為一個類屬性而存在,你可以用如下方式來聲明、訪問一個函數:
?

            
>>> class Pizza(object):
...   def __init__(self, size):
...     self.size = size
...   def get_size(self):
...     return self.size
...
>>> Pizza.get_size

            
            
          

Python在告訴你,屬性_get_size是類Pizza的一個未綁定方法。這是什么意思呢?很快我們就會知道答案:
?

            
>>> Pizza.get_size()
Traceback (most recent call last):
 File "
            
              ", line 1, in 
              
                
TypeError: unbound method get_size() must be called with Pizza instance as first argument (got nothing instead)

              
            
          

我們不能這么調用,因為它還沒有綁定到Pizza類的任何實例上,它需要一個實例作為第一個參數傳遞進去(Python2必須是該類的實例,Python3中可以是任何東西),嘗試一下:
?

            
>>> Pizza.get_size(Pizza(42))

          

太棒了,現在用一個實例作為它的的第一個參數來調用,整個世界都清靜了,如果我說這種調用方式還不是最方便的,你也會這么認為的;沒錯,現在每次調用這個方法的時候我們都不得不引用這個類,如果不知道哪個類是我們的對象,長期看來這種方式是行不通的。

那么Python為我們做了什么呢,它綁定了所有來自類_Pizza的方法以及該類的任何一個實例的方法。也就意味著現在屬性get_size是Pizza的一個實例對象的綁定方法,這個方法的第一個參數就是該實例本身。
?

            
>>> Pizza(42).get_size

            
              <__main__.Pizza object at 0x7f3138827910>>
>>> Pizza(42).get_size()
42

            
          

和我們預期的一樣,現在不再需要提供任何參數給_get_size,因為它已經是綁定的,它的self參數會自動地設置給Pizza實例,下面代碼是最好的證明:
?

            
>>> m = Pizza(42).get_size
>>> m()
42

          

更有甚者,你都沒必要使用持有Pizza對象的引用了,因為該方法已經綁定到了這個對象,所以這個方法對它自己來說是已經足夠了。

也許,如果你想知道這個綁定的方法是綁定在哪個對象上,下面這種手段就能得知:
?

            
>>> m = Pizza(42).get_size
>>> m.__self__
<__main__.Pizza object at 0x7f3138827910>
>>> 
# You could guess, look at this:
...
>>> m == m.__self__.get_size
True

          

顯然,該對象仍然有一個引用存在,只要你愿意你還是可以把它找回來。

在Python3中,依附在類上的函數不再當作是未綁定的方法,而是把它當作一個簡單地函數,如果有必要它會綁定到一個對象身上去,原則依然和Python2保持一致,但是模塊更簡潔:
?

            
>>> class Pizza(object):
...   def __init__(self, size):
...     self.size = size
...   def get_size(self):
...     return self.size
...
>>> Pizza.get_size

            
            
          

靜態方法

靜態方法是一類特殊的方法,有時你可能需要寫一個屬于這個類的方法,但是這些代碼完全不會使用到實例對象本身,例如:
?

            
class Pizza(object):
  @staticmethod
  def mix_ingredients(x, y):
    return x + y
 
  def cook(self):
    return self.mix_ingredients(self.cheese, self.vegetables)

          

這個例子中,如果把_mix_ingredients作為非靜態方法同樣可以運行,但是它要提供self參數,而這個參數在方法中根本不會被使用到。這里的@staticmethod裝飾器可以給我們帶來一些好處:

??? Python不再需要為Pizza對象實例初始化一個綁定方法,綁定方法同樣是對象,但是創建他們需要成本,而靜態方法就可以避免這些。
?

            
>>> Pizza().cook is Pizza().cook
False
>>> Pizza().mix_ingredients is Pizza.mix_ingredients
True
>>> Pizza().mix_ingredients is Pizza().mix_ingredients
True

          

??? 可讀性更好的代碼,看到@staticmethod我們就知道這個方法并不需要依賴對象本身的狀態。
??? 可以在子類中被覆蓋,如果是把mix_ingredients作為模塊的頂層函數,那么繼承自Pizza的子類就沒法改變pizza的mix_ingredients了如果不覆蓋cook的話。

類方法

話雖如此,什么是類方法呢?類方法不是綁定到對象上,而是綁定在類上的方法。
?

            
>>> class Pizza(object):
...   radius = 42
...   @classmethod
...   def get_radius(cls):
...     return cls.radius
... 
>>> 
>>> Pizza.get_radius

            
              
                >
>>> Pizza().get_radius

                
                  
                    >
>>> Pizza.get_radius is Pizza().get_radius
True
>>> Pizza.get_radius()
42

                  
                
              
            
          

無論你用哪種方式訪問這個方法,它總是綁定到了這個類身上,它的第一個參數是這個類本身(記住:類也是對象)。

什么時候使用這種方法呢?類方法通常在以下兩種場景是非常有用的:

??? 工廠方法:它用于創建類的實例,例如一些預處理。如果使用@staticmethod代替,那我們不得不硬編碼Pizza類名在函數中,這使得任何繼承Pizza的類都不能使用我們這個工廠方法給它自己用。
?

            
class Pizza(object):
  def __init__(self, ingredients):
    self.ingredients = ingredients
 
  @classmethod
  def from_fridge(cls, fridge):
    return cls(fridge.get_cheese() + fridge.get_vegetables())

          

??? 調用靜態類:如果你把一個靜態方法拆分成多個靜態方法,除非你使用類方法,否則你還是得硬編碼類名。使用這種方式聲明方法,Pizza類名明永遠都不會在被直接引用,繼承和方法覆蓋都可以完美的工作。

?

            
class Pizza(object):
  def __init__(self, radius, height):
    self.radius = radius
    self.height = height
 
  @staticmethod
  def compute_area(radius):
     return math.pi * (radius ** 2)
 
  @classmethod
  def compute_volume(cls, height, radius):
     return height * cls.compute_area(radius)
 
  def get_volume(self):
    return self.compute_volume(self.height, self.radius)

          

抽象方法

抽象方法是定義在基類中的一種方法,它沒有提供任何實現,類似于Java中接口(Interface)里面的方法。

在Python中實現抽象方法最簡單地方式是:
?

            
class Pizza(object):
  def get_radius(self):
    raise NotImplementedError

          

任何繼承自_Pizza的類必須覆蓋實現方法get_radius,否則會拋出異常。

這種抽象方法的實現有它的弊端,如果你寫一個類繼承Pizza,但是忘記實現get_radius,異常只有在你真正使用的時候才會拋出來。
?

            
>>> Pizza()
<__main__.Pizza object at 0x7fb747353d90>
>>> Pizza().get_radius()
Traceback (most recent call last):
 File "
            
              ", line 1, in 
              
                
 File "
                
                  ", line 3, in get_radius
NotImplementedError

                
              
            
          

還有一種方式可以讓錯誤更早的觸發,使用Python提供的abc模塊,對象被初始化之后就可以拋出異常:
?

            
import abc
 
class BasePizza(object):
  __metaclass__ = abc.ABCMeta
 
  @abc.abstractmethod
  def get_radius(self):
     
"""Method that should do something."""

          

使用abc后,當你嘗試初始化BasePizza或者任何子類的時候立馬就會得到一個TypeError,而無需等到真正調用get_radius的時候才發現異常。
?

            
>>> BasePizza()
Traceback (most recent call last):
 File "
            
              ", line 1, in 
              
                
TypeError: Can't instantiate abstract class BasePizza with abstract methods get_radius

              
            
          

混合靜態方法、類方法、抽象方法

當你開始構建類和繼承結構時,混合使用這些裝飾器的時候到了,所以這里列出了一些技巧。

記住,聲明一個抽象的方法,不會固定方法的原型,這就意味著雖然你必須實現它,但是我可以用任何參數列表來實現:
?

            
import abc
 
class BasePizza(object):
  __metaclass__ = abc.ABCMeta
 
  @abc.abstractmethod
  def get_ingredients(self):
     
"""Returns the ingredient list."""
 
class Calzone(BasePizza):
  def get_ingredients(self, with_egg=False):
    egg = Egg() if with_egg else None
    return self.ingredients + egg

          

這樣是允許的,因為Calzone滿足BasePizza對象所定義的接口需求。同樣我們也可以用一個類方法或靜態方法來實現:
?

            
import abc
 
class BasePizza(object):
  __metaclass__ = abc.ABCMeta
 
  @abc.abstractmethod
  def get_ingredients(self):
     
"""Returns the ingredient list."""
 
class DietPizza(BasePizza):
  @staticmethod
  def get_ingredients():
    return None

          

這同樣是正確的,因為它遵循抽象類BasePizza設定的契約。事實上get_ingredients方法并不需要知道返回結果是什么,結果是實現細節,不是契約條件。

因此,你不能強制抽象方法的實現是一個常規方法、或者是類方法還是靜態方法,也沒什么可爭論的。從Python3開始(在Python2中不能如你期待的運行,見issue5867),在abstractmethod方法上面使用@staticmethod和@classmethod裝飾器成為可能。
?

            
import abc
 
class BasePizza(object):
  __metaclass__ = abc.ABCMeta
 
  ingredient = ['cheese']
 
  @classmethod
  @abc.abstractmethod
  def get_ingredients(cls):
     
"""Returns the ingredient list."""
     return cls.ingredients

          

別誤會了,如果你認為它會強制子類作為一個類方法來實現get_ingredients那你就錯了,它僅僅表示你實現的get_ingredients在BasePizza中是一個類方法。

可以在抽象方法中做代碼的實現?沒錯,Python與Java接口中的方法相反,你可以在抽象方法編寫實現代碼通過super()來調用它。(譯注:在Java8中,接口也提供的默認方法,允許在接口中寫方法的實現)
?

            
import abc
 
class BasePizza(object):
  __metaclass__ = abc.ABCMeta
 
  default_ingredients = ['cheese']
 
  @classmethod
  @abc.abstractmethod
  def get_ingredients(cls):
     
"""Returns the ingredient list."""
     return cls.default_ingredients
 
class DietPizza(BasePizza):
  def get_ingredients(self):
    return ['egg'] + super(DietPizza, self).get_ingredients()

          

這個例子中,你構建的每個pizza都通過繼承BasePizza的方式,你不得不覆蓋get_ingredients方法,但是能夠使用默認機制通過super()來獲取ingredient列表。


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 日韩在线精品 | 多男操一女视频 | 亚洲精品欧美视频 | 精品久久久久一区 | 日操夜操天天操 | 精品中文字幕一区二区 | 天天夜夜操操 | 国产精品久久久久一区二区 | 欧美一级免费 | japanesemature乱子在线 | 久久国产精品免费一区二区三区 | 久久伊人在 | 亚洲成在人线中文字幕 | 国产免费一区二区 | 亚洲成网 | 91丁香亚洲综合社区 | 日韩精品一区二区三区 | 毛片免费观看 | 免费99热在线观看 | 国产亚洲精品久久久久久久网站 | 国产综合一区二区 | 青娱乐网站 | 91香蕉视频在线看 | 亚洲午夜网未来影院 | 亚洲综合五月天激动情网 | 中文字幕三区 | 久久精品网 | 久草在线视频精品 | 欧美大胆一级视频 | 麻豆网站入口 | 亚洲免费色 | 大象一区 | 天天操,夜夜操 | 视频一区二区三区四区五区 | 九九综合九九综合 | 喷潮网站 | 午夜资源网 | 大片毛片 | 日本精品a在线观看 | 国产精品思瑞在线观看 | 5月婷婷6月丁香 |