Python物件導向編程中的類和對象學習教程_python

來源:互聯網
上載者:User

Python中一切都是對象。類提供了建立新類型對象的機制。這篇教程中,我們不談類和物件導向的基本知識,而專註在更好地理解Python物件導向編程上。假設我們使用新風格的python類,它們繼承自object父類。
定義類

class 語句可以定義一系列的屬性、變數、方法,他們被該類的執行個體對象所共用。下面給出一個簡單類定義:
 

class Account(object):  num_accounts = 0   def __init__(self, name, balance):   self.name = name   self.balance = balance   Account.num_accounts += 1   def del_account(self):   Account.num_accounts -= 1   def deposit(self, amt):   self.balance = self.balance + amt   def withdraw(self, amt):   self.balance = self.balance - amt   def inquiry(self):   return self.balance

類定義引入了以下新對象:

    類對象
    執行個體對象
    方法對象

類對象

程式執行過程中遇到類定義時,就會建立新的命名空間,命名空間包含所有類變數和方法定義的名稱綁定。注意該命名空間並沒有建立類方法可以使用的新局部範圍,因此在方法中訪問變數需要全限定名稱。上一節的Account類示範了該特性;嘗試訪問num_of_accounts變數的方法需要使用全限定名稱Account.num_of_accounts,否則,如果沒有在__init__方法中使用全限定名稱,會引發如下錯誤:
 

class Account(object): num_accounts = 0  def __init__(self, name, balance):  self.name = name  self.balance = balance  num_accounts += 1  def del_account(self):  Account.num_accounts -= 1  def deposit(self, amt):  self.balance = self.balance + amt  def withdraw(self, amt):  self.balance = self.balance - amt  def inquiry(self):  return self.balance >>> acct = Account('obi', 10)Traceback (most recent call last): File "python", line 1, in <module> File "python", line 9, in __init__UnboundLocalError: local variable 'num_accounts' referenced before assignment

類定義執行的最後,會建立一個類對象。在進入類定義之前有效那個範圍現在被恢複了,同時類對象被綁定到類定義頭的類名上。

先偏離下話題,你可能會問如果建立的類是對象,那麼類對象的類是什麼呢?。與一切都是對象的python哲學一致,類對象確實有個類,即python新風格類中的type類。
 

>>> type(Account)<class 'type'>

讓你更迷惑一點,Account類型的類型是type。type類是個元類,用於建立其他類,我們稍後教程中再介紹。

類對象支援屬性引用和執行個體化。屬性通過標準的點文法引用,即對象後跟句點,然後是屬性名稱:obj.name。有效屬性名稱是類對象建立後類命名空間中出現的所有變數和方法名。例如:
 

>>> Account.num_accounts>>> 0>>> Account.deposit>>> <unbound method Account.deposit>

類執行個體化使用函數標記法。執行個體化會像普通函數一樣無參數調用類對象,如下文中的Account類:

>>> Account()


類對象執行個體化之後,會返回執行個體對象,如果類中定義了__init__方法,就會調用,執行個體對象作為第一個參數傳遞過去。這個方法會進行使用者自訂的初始化過程,比如執行個體變數的初始化。Account類為例,賬戶name和balance會被設定,執行個體對象的數目增加1。
執行個體對象

如果類對象是餅乾切割刀,餅乾就是執行個體化類對象的結果。執行個體對象上的全部有效操作為對屬性、資料和方法對象的引用。
方法對象

方法對象和函數對象類似。如果x是Account類的執行個體,x.deposit就是方法對象的例子。方法定義中有個附加參數,self。self指向類執行個體。為什麼我們需要把執行個體作為參數傳遞給方法?方法調用能最好地說明:
 

>>> x = Account()>>> x.inquiry()10

執行個體方法調用時發生了什嗎?你應該注意到x.inquiry()調用時沒有參數,雖然方法定義包含self參數。那麼這個參數到底發生了什嗎?

特殊之處在於方法所作用的對象被作為函數的第一個參數傳遞過去。在我們的例子中,對x.inquiry()的調用等價於Account.f(x)。一般,調用n參數的方法等同於將方法的作用對象插入到第一個參數位置。

python教程上講:

    當引用的執行個體屬性不是資料屬性時,就會搜尋類。如果名稱表示一個合法的函數對象,執行個體對象和函數對象將會被打包到一個抽象對象,即方法對象中。包含參數列表的方法對象被調用時,將會根據執行個體對象和參數列表建立一個新的參數列表,然後函數對象將會使用新的參數列表被調用。

這適用於所有的執行個體方法對象,包括__init__方法。self參數其實不是一個關鍵字,任何有效參數名都可以使用,如下Account類定義所示:
 

class Account(object): num_accounts = 0  def __init__(obj, name, balance):  obj.name = name  obj.balance = balance  Account.num_accounts += 1  def del_account(obj):  Account.num_accounts -= 1  def deposit(obj, amt):  obj.balance = obj.balance + amt  def withdraw(obj, amt):  obj.balance = obj.balance - amt  def inquiry(obj):  return obj.balance >>> Account.num_accounts>>> 0>>> x = Account('obi', 0)>>> x.deposit(10)>>> Account.inquiry(x)>>> 10

靜態和類方法

類中定義的方法預設由執行個體調用。但是,我們也可以通過對應的@staticmethod和@classmethod裝飾器來定義靜態或類方法。
靜態方法

靜態方式是類命名空間中的普通函數。引用類的靜態方法返回的是函數類型,而不是非Binder 方法類型:
 

class Account(object): num_accounts = 0  def __init__(self, name, balance):  self.name = name  self.balance = balance  Account.num_accounts += 1  def del_account(self):  Account.num_accounts -= 1  def deposit(self, amt):  self.balance = self.balance + amt  def withdraw(self, amt):  self.balance = self.balance - amt  def inquiry(self):  return "Name={}, balance={}".format(self.name, self.balance)  @staticmethod def type():  return "Current Account" >>> Account.deposit<unbound method Account.deposit>>>> Account.type<function type at 0x106893668>

使用@staticmethod裝飾器來定義靜態方法,這些方法不需要self參數。靜態方法可以更好地組織與類相關的代碼,也可以在子類中被重寫。
類方法

類方法由類自身來調用,而不是執行個體。類方法使用@classmethod裝飾器定義,作為第一個參數被傳遞給方法的是類而不是執行個體。
 

import json class Account(object): num_accounts = 0  def __init__(self, name, balance):  self.name = name  self.balance = balance  Account.num_accounts += 1  def del_account(self):  Account.num_accounts -= 1  def deposit(self, amt):  self.balance = self.balance + amt  def withdraw(self, amt):  self.balance = self.balance - amt  def inquiry(self):  return "Name={}, balance={}".format(self.name, self.balance)  @classmethod def from_json(cls, params_json):    params = json.loads(params_json)  return cls(params.get("name"), params.get("balance"))  @staticmethod def type():  return "Current Account"

類方法一個常見的用法是作為對象建立的工廠。假如Account類的資料格式有很多種,比如元組、json字串等。由於Python類只能定義一個__init__方法,所以類方法在這些情形中就很方便。以上文Account類為例,我們想根據一個json字串對象來初始化一個賬戶,我們定義一個類Factory 方法from_json,它讀取json字串對象,解析參數,根據參數建立賬戶對象。另一個類執行個體的例子是dict.fromkeys 方法,它從一組鍵和值序列中建立dict對象。
Python特殊方法

有時我們希望自訂類。這需要改變類對象建立和初始化的方法,或者對某些操作提供多態行為。多態行為允許定製在類定義中某些如+等python操作的自身實現。Python的特殊方法可以做到這些。這些方法一般都是__*__形式,其中*表示方法名。如__init__和__new__來自訂對象建立和初始化,__getitem__、__get__、__add__、__sub__來類比python內建類型,還有__getattribute__、__getattr__等來定製屬性訪問。只有為數不多的特殊方法,我們討論一些重要的特殊方法來做個簡單理解,python文檔有全部方法的列表。
進行對象建立的特殊方法

新的類執行個體通過兩階段過程建立,__new__方法建立新執行個體,__init__初始化該執行個體。使用者已經很熟悉__init__方法的定義;但使用者很少定義__new__方法,但是如果想自訂類執行個體的建立,也是可以的。
屬性訪問的特殊方法

我們可以通過實現以下方法來定製類執行個體的屬性訪問。

class Account(object): num_accounts = 0  def __init__(self, name, balance):  self.name = name  self.balance = balance  Account.num_accounts += 1  def del_account(self):  Account.num_accounts -= 1  def __getattr__(self, name):  return "Hey I dont see any attribute called {}".format(name)  def deposit(self, amt):  self.balance = self.balance + amt  def withdraw(self, amt):  self.balance = self.balance - amt  def inquiry(self):  return "Name={}, balance={}".format(self.name, self.balance)  @classmethod def from_dict(cls, params):  params_dict = json.loads(params)  return cls(params_dict.get("name"), params_dict.get("balance"))  @staticmethod def type():  return "Current Account" x = Account('obi', 0)

    __getattr__(self, name)__:這個方法只有當name既不是執行個體屬性也不能在對象的類繼承鏈中找到時才會被調用。這個方法應當返回屬性值或者引發AttributeError異常。例如,如果x是Account類的執行個體,嘗試訪問不存在的屬性將會調用這個方法。
 

>>> acct = Account("obi", 10)>>> acct.numberHey I dont see any attribute called number

注意如果 __getattr__引用不存在的執行個體屬性,可能會發生死迴圈,因為__getattr__方法不斷被調用。

2.__setattr__(self, name, value)__:這個方法當屬性賦值發生時調用。__setattr__將會把值插入到執行個體屬性字典中,而不是使用self.name=value,因為它會導致遞迴調用的死迴圈。

3.__delattr__(self, name)__:del obj發生時調用。

4.__getattribute__(self, name)__:這個方法會被一直調用以實作類別執行個體的屬性訪問。
類型類比的特殊方法

對某些類型,Python定義了某些特定文法;比如,列表和元組的元素可以通過索引標記法來訪問,數值可以通過+操作符來進行加法等等。我們可以建立自己的使用這些特殊文法的類,python解譯器遇到這些特殊文法時就會調用我們實現的方法。我們在下面用一個簡單的例子來示範這個特性,它類比python列表的基本用法。

class CustomList(object):  def __init__(self, container=None):  # the class is just a wrapper around another list to  # illustrate special methods  if container is None:   self.container = []  else:   self.container = container  def __len__(self):  # called when a user calls len(CustomList instance)  return len(self.container)  def __getitem__(self, index):  # called when a user uses square brackets for indexing  return self.container[index]  def __setitem__(self, index, value):  # called when a user performs an index assignment  if index <= len(self.container):   self.container[index] = value  else:   raise IndexError()  def __contains__(self, value):  # called when the user uses the 'in' keyword  return value in self.container  def append(self, value):  self.container.append(value)  def __repr__(self):  return str(self.container)  def __add__(self, otherList):  # provides support for the use of the + operator  return CustomList(self.container + otherList.container)

上面,CustomList是個真實列表的簡單封裝器。我們為了示範實現了一些自訂方法:

    __len__(self):對CustomList執行個體調用len()函數時被調用。

>>> myList = CustomList()>>> myList.append(1) >>> myList.append(2)>>> myList.append(3)>>> myList.append(4)>>> len(myList)4

2.__getitem__(self, value):提供CustomList類執行個體的方括弧索引用法支援:
 

>>> myList = CustomList()>>> myList.append(1) >>> myList.append(2)>>> myList.append(3)>>> myList.append(4)>>> myList[3]4

3.__setitem__(self, key, value):當對CustomList類執行個體上self[key]賦值時調用。
 

>>> myList = CustomList()>>> myList.append(1) >>> myList.append(2)>>> myList.append(3)>>> myList.append(4)>>> myList[3] = 1004>>> myList[3]100

4.__contains__(self, key):成員檢測時調用。如果包含該項就返回true,否則false。
 

>>> myList = CustomList()>>> myList.append(1) >>> myList.append(2)>>> myList.append(3)>>> myList.append(4)>>> 4 in myListTrue

5.__repr__(self):當用print列印self時調用,將會列印self的對象表示。
 

>>> myList = CustomList()>>> myList.append(1) >>> myList.append(2)>>> myList.append(3)>>> myList.append(4)>>> print myList[1, 2, 3, 4]

6.__add__(self, otherList):使用+操作符來計算兩個CustomList執行個體相加時調用。
 

>>> myList = CustomList()>>> otherList = CustomList()>>> otherList.append(100)>>> myList.append(1) >>> myList.append(2)>>> myList.append(3)>>> myList.append(4)>>> myList + otherList + otherList[1, 2, 3, 4, 100, 100]

上面的例子示範了如何通過定義某些特殊類方法來定製類行為。可以在Python文檔中查看這些自訂方法的完整列表。在接下來的教程中,我們會將特殊方法放到一起來討論,並解釋描述符這個在python物件導向編程中廣泛使用的重要功能。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.