Python基礎學習之類的介紹

來源:互聯網
上載者:User
  在Python中,首字母大寫的名稱指的是類。這個類定義中的括弧是空的,因為我們要從空白建立這個類。我們編寫了一個文檔字串,對這個類的功能作了描述。類中的函數稱為 方法。

  以Student類為例,在Python中,定義類是通過class關鍵字:

class Student(object):

pass

  class後面緊接著是類名,即Student,類名通常是大寫開頭的單詞,緊接著是(object),表示該類是從哪個類繼承下來的,通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類。

9.1.1建立類

class Student(object):

def __init__(self, name, score):

self.name = name

self.score = score

  1.方法__init__() 是一個特殊的方法,建立新執行個體時,Python都會自動運行它。開頭和末尾各有兩個底線,這是一種約定,旨在避免Python預設方法與普通方法發生名稱衝突。這個方法的定義中,形參self 必不可少,還必須位於其他形參的前面

  2.每個與類相關聯的方法調用都自動傳遞實參self ,它是一個指向執行個體本身的引用,讓執行個體能夠訪問類中的屬性和方法。self 會自動傳遞,因此我們不需要傳遞它。

  3.以self 為首碼的變數都可供類中的所有方法使用,我們還可以通過類的任何執行個體來訪問這些變數。

  4.self.name= name像這樣可通過執行個體訪問的變數稱為屬性

  5.物件導向編程的一個重要特點就是資料封裝。可以直接在類的內部定義訪問資料的函數,這樣,就把“資料”給封裝起來了。這些封裝資料的函數是和Student類本身是關聯起來的,我們稱之為類的方法.

9.1.2根據類建立執行個體

  我們通常可以認為首字母大寫的名稱(如Dog )指的是類,而 小寫名稱(如my_dog )指的是根據類建立的執行個體。

  1、要訪問執行個體的屬性,可使用句點標記法,我們編寫了如下代碼來訪問my_dog 的屬性name 的值。

  my_dog.name

  句點標記法在Python中很常用,這種文法示範了Python如何獲悉屬性的值。

  2、根據Dog 類建立執行個體後,就可以使用句點標記法來調用Dog 類中定義的任何方法。

  3、可按需求根據類建立任意數量的執行個體。

9.2使用類和執行個體

  1.你需要執行的一個重要任務是修改執行個體的屬性。你可以直接修改執行個體的屬性,也可以編寫方法以特定的方式進行修改。

  2.類是建立執行個體的模板,而執行個體則是一個一個具體的對象,各個執行個體擁有的資料都互相獨立,互不影響;方法就是與執行個體綁定的函數,和普通函數不同,方法可以直接存取執行個體的資料;通過在執行個體上調用方法,我們就直接操作了對象內部的資料,但無需知道方法內部的實現細節。和靜態語言不同,Python允許對執行個體變數綁定任何資料,也就是說,對於兩個執行個體變數,雖然它們都是同一個類的不同執行個體,但擁有的變數名稱都可能不同。

9.2.1給類設定初始值

類中的每個屬性都必須有初始值,哪怕這個值是0或Null 字元串。在有些情況下,如設定預設值時,在方法__init__() 內指定這種初始值是可行的;如果你對某個屬性這樣做了,就無需包含為它提供初始值的形參。

直接在class中定義屬性,這種屬性是類屬性:

class Student(object):

name = 'Student'

  在編寫程式的時候,千萬不要把執行個體屬性和類屬性使用相同的名字,因為相同名稱的執行個體屬性將屏蔽掉類屬性,但是當你刪除執行個體屬性後,再使用相同的名稱,訪問到的將是類屬性。

9.2.2修改屬性的值

  可以以三種不同的方式修改屬性的值:

  1.直接通過執行個體進行修改;

  2.通過方法進行設定;

  3.通過方法進行遞增(增加特定的值)。

9.2.3訪問限制

  1.在Class內部,可以有屬性和方法,而外部代碼可以通過直接調用執行個體變數的方法來操作資料,這樣,就隱藏了內部的複雜邏輯。

  2.如果要讓內部屬性不被外部存取,可以把屬性的名稱前加上兩個底線__,在Python中,執行個體的變數名如果以__開頭,就變成了一個私人變數(private),只有內部可以訪問,外部不能訪問。

class Student(object):

def __init__(self, name, score):

self.__name = name

self.__score = score

def print_score(self):

print('%s: %s' % (self.__name, self.__score))

  3.改完後,對於外部代碼來說,沒什麼變動,但是已經無法從外部存取執行個體變數.__name和執行個體變數.__score了:

>>> bart = Student('Bart Simpson', 98)

>>> bart.__name

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: 'Student' object has no attribute '__name'

  4.這樣就確保了外部代碼不能隨意修改對象內部的狀態,這樣通過訪問限制的保護,代碼更加健壯。但是如果外部代碼要擷取name和score,可以給Student類增加get_name和get_score這樣的方法:

class Student(object):

...

def get_name(self):

return self.__name

def get_score(self):

return self.__score

  5.如果又要允許外部代碼修改score,可以再給Student類增加set_score方法:

class Student(object):

...

def set_score(self, score):

self.__score = score

  6.和原來直接調用參數相比,在方法中,可以對參數做檢查,避免傳入無效的參數:

class Student(object):

...

def set_score(self, score):

if 0 <= score <= 100:

self.__score = score

else:

raise ValueError('bad score')

  7.需要注意的是,在Python中,變數名類似__xxx__的,也就是以雙底線開頭,並且以雙底線結尾的,是特殊變數,特殊變數是可以直接存取的,不是private變數,所以,不能用__name__、__score__這樣的變數名。有些時候,你會看到以一個底線開頭的執行個體變數名,比如_name,這樣的執行個體變數外部是可以訪問的,但是,按照約定俗成的規定,當你看到這樣的變數時,意思就是,“雖然我可以被訪問,但是,請把我視為私人變數,不要隨意訪問”。

  8.雙底線開頭的執行個體變數也不是一定不能從外部存取。不能直接存取__name是因為Python解譯器對外把__name變數改成了_Student__name,所以,仍然可以通過_Student__name來訪問__name變數。

9.3繼承

  1.如果一個類似另一個類的特殊版本,可以使用繼承,一個類繼承另一個類它將自動擷取另一個類的所有屬性和方法。原有的類為父類,新類為子類。

  2.子類繼承父類所有的屬性和方法,還可以定義自己的屬於和方法。在OOP程式設計中,當我們定義一個class的時候,可以從某個現有的class繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Super class)。

class Dog(Animal): #繼承Animal

pass

9.3.1子類的方法__init__()

  1.繼承需要給父類的所有屬性賦值,子類的__init__()需要父類施以援手。

  2.並且父類必須在繼承檔案中,在子類之前。

  3.定義子類時,必須在括弧中指定父類的名稱。

  4.super()特殊函數,協助python將父類和子類並聯起來。父類也稱超類,super的由來。

9.3.2定義子類的方法和屬性

  讓一個類繼承一個類後。可添加區別子類和父類的屬性和方法。

9.3.3重寫父類

  對應父類的方法,只有不符合子類的需要都可以重寫,在子類中添加新的方法描述子類的特點。去父類的去其糟怕取其精華。

9.3.4多態

  1.當子類和父類都存在相同的方法時,我們說,子類覆蓋了父類的方法,在代碼啟動並執行時候,總是會調用子類的方法。這樣,我們就獲得了繼承的另一個好處:多態

  2.所以,在繼承關係中,如果一個執行個體的資料類型是某個子類,那它的資料類型也可以被看做是父類。但是,反過來就不行。

  3.多態的好處是,當我們需要傳入Dog、Cat、Tortoise……時,我們只需要接收Animal類型就可以了,因為Dog、Cat、Tortoise……都是Animal類型,然後,按照Animal類型進行操作即可。由於Animal類型有run()方法,因此,傳入的任意類型,只要是Animal類或者子類,就會自動調用實際類型的run()方法,這就是多態的意思。

  4.對於一個變數,我們只需要知道它是Animal類型,無需確切地知道它的子類型,就可以放心地調用run()方法,而具體調用的run()方法是作用在Animal、Dog、Cat還是Tortoise對象上,由運行時該對象的確切類型決定,這就是多態真正的威力:調用方只管調用,不管細節,而當我們新增一種Animal的子類時,只要確保run()方法編寫正確,不用管原來的代碼是如何調用的。這就是著名的“開閉”原則:

  • 對擴充開放:允許新增Animal子類;

  • 對修改封閉:不需要修改依賴Animal類型的run_twice()等函數。

9.3.5使用__slots__

  為了達到限制的目的,Python允許在定義class的時候,定義一個特殊的__slots__變數,來限制該class執行個體能添加的屬性。

class Student(object):

__slots__ = ('name', 'age') # 用tuple定義允許綁定的屬性名稱

  使用__slots__要注意,__slots__定義的屬性僅對當前類執行個體起作用,對繼承的子類是不起作用的。

9.3.6多重繼承

  1.通過多重繼承,一個子類就可以同時獲得多個父類的所有功能。

  2.設計類的繼承關係時,通常,主線都是單一繼承下來的,例如,Ostrich(鴕鳥)繼承自Bird。但是,如果需要“混入”額外的功能,通過多重繼承就可以實現,比如,讓Ostrich除了繼承自Bird外,再同時繼承Runnable。這種設計通常稱之為MixIn。MixIn的目的就是給一個類增加多個功能,這樣,在設計類的時候,我們優先考慮通過多重繼承來組合多個MixIn的功能,而不是設計多層次的複雜的繼承關係。

  3.這樣我們就不需要複雜而龐大的繼承鏈,只要選擇組合不同的類的功能,就可以快速構造出所需的子類。由於Python允許使用多重繼承,因此,MixIn就是一種常見的設計。只允許單一繼承的語言(如Java)不能使用MixIn的設計。

9.3.7定製類

  1.Python的class中還有許多這樣有特殊用途的函數,可以協助我們定製類。

__str__

  定義好__str__()方法,可以返回一個好看的字串就:

>>> class Student(object):

... def __init__(self, name):

... self.name = name

... def __str__(self):

... return 'Student object (name: %s)' % self.name

...

>>> print(Student('Michael'))

Student object (name: Michael)

  這樣列印出來的執行個體,不但好看,而且容易看出執行個體內部重要的資料。

  直接敲變數不用print,列印出來的執行個體還是不好看:

>>> s = Student('Michael')

>>> s

<__main__.Student object at 0x109afb310>

  這是因為直接顯示變數調用的不是__str__(),而是__repr__(),兩者的區別是__str__()返回使用者看到的字串,而__repr__()返回程式開發人員看到的字串,也就是說,__repr__()是為調試服務的。

  解決辦法是再定義一個__repr__()。但是通常__str__()和__repr__()代碼都是一樣的,所以,有個偷懶的寫法:

class Student(object):

def __init__(self, name):

self.name = name

def __str__(self):

return 'Student object (name=%s)' % self.name

__repr__ = __str__

__iter__

  如果一個類想被用於for ... in迴圈,類似list或tuple那樣,就必須實現一個__iter__()方法,該方法返回一個迭代對象,然後,Python的for迴圈就會不斷調用該迭代對象的__next__()方法拿到迴圈的下一個值,直到遇到StopIteration錯誤時退出迴圈。

我們以斐波那契數列為例,寫一個Fib類,可以作用於for迴圈:

class Fib(object):

def __init__(self):

self.a, self.b = 0, 1 # 初始化兩個計數器a,b

def __iter__(self):

return self # 執行個體本身就是迭代對象,故返回自己

def __next__(self):

self.a, self.b = self.b, self.a + self.b # 計算下一個值

if self.a > 100000: # 退出迴圈的條件

raise StopIteration()

return self.a # 返回下一個值

  現在,試試把Fib執行個體作用於for迴圈:

>>> for n in Fib():

... print(n)

...

1

1

2

3

5

...

46368

75025

__getitem__

  Fib執行個體雖然能作用於for迴圈,看起來和list有點像,但是,把它當成list來使用還是不行,比如,取第5個元素:

>>> Fib()[5]

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: 'Fib' object does not support indexing

  要表現得像list那樣按照下標取出元素,需要實現__getitem__()方法:

class Fib(object):

def __getitem__(self, n):

a, b = 1, 1

for x in range(n):

a, b = b, a + b

return a

__getattr__

  Python還有另一個機制,那就是寫一個__getattr__()方法,動態返回一個屬性。當調用不存在的屬性時,比如score,Python解譯器會試圖調用__getattr__(self, 'score')來嘗試獲得屬性,這樣,我們就有機會返回score的值。已有的屬性,比如name,不會在__getattr__中尋找。

__call__

  1.任何類,只需要定義一個__call__()方法,就可以直接對執行個體進行調用。

  2.通過callable()函數,我們就可以判斷一個對象是否是“可調用”對象。

9.3.8枚舉類

  為這樣的枚舉類型定義一個class類型,然後,每個常量都是class的一個唯一執行個體。Python提供了Enum類來實現這個功能:

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

9.3.9元類

type()

  要建立一個class對象,type()函數依次傳入3個參數:

  1. class的名稱;

  2. 繼承的父類集合,注意Python支援多重繼承,如果只有一個父類,別忘了tuple的單元素寫法;

  3. class的方法名稱與函數綁定,這裡我們把函數fn綁定到方法名hello上。

metaclass

  metaclass,直譯為元類,簡單的解釋就是:當我們定義了類以後,就可以根據這個類建立出執行個體,所以:先定義類,然後建立執行個體。但是如果我們想建立出類呢?那就必鬚根據metaclass建立出類,所以:先定義metaclass,然後建立類。串連起來就是:先定義metaclass,就可以建立類,最後建立執行個體。

__new__()方法接收到的參數依次是:

  1. 當前準備建立的類的對象;

  2. 類的名字;

  3. 類繼承的父類集合;

  4. 類的方法集合。

9.3.10將執行個體用作屬性

  使用代碼類比實物時,當你添加越來越多的特點和細節是,會發現屬性和方法以及代碼檔案都越來越長,這時候可以把其中一部分分離,重新組成一個類。可以將一個大類分成許多小類。

9.3.11類比實物

  類比較複雜的實物,需要從較高的邏輯層次考慮。為了編寫效率更高,更簡潔準確的代碼,可能需要不停的重組類。

9.4匯入類

  1.隨著不斷對類的添加,即使很優秀的繼承也會是代碼變得很長,所以python可以讓你把類匯入模組。並在主程式中匯入使用所需要的模組。

  2.為自己的每個模組寫文檔字串,說明模組的作用,對內容進行簡要敘述。

  3.從一個模組匯入多個類,用逗號隔開,並根據自己的需要建立執行個體。

  4.也可以匯入整個類,並在執行個體前加上類名,和訪問需要的類。

  5.在一個模組中匯入另一個模組。

9.5python標準庫(模組)

  Python標準庫是一組模組,只需要import語句就可以匯入使用。

  字典讓你把資訊關聯起來,但不記錄你添加索引值對的順序,要建立字典並記錄添加索引值對的順序,可使用模組collections中的orderedDict類。OrderedDict的執行個體和字典一樣,不過記錄了添加索引值對的添加順序。

  OrderdDict很不錯,兼具列表和字典的特點,有時候很需要。

random模組

  包含各種方式產生隨機數的函數。其中randint()返回一個位於指定範圍內的整數。

datetime是模組

  datetime模組還包含一個datetime類,通過from datetime import datetime匯入的才是datetime這個類。如果僅匯入import datetime,則必須引用全名datetime.datetime。

  datetime.now()返回當前日期和時間,其類型是datetime。

常用的第三方庫

  還有MySQL的驅動:mysql-connector-python,用於科學計算的NumPy庫:numpy,用於產生文本的模板工具Jinja2,等等。

  1.urlparse 模組,urlpasrse 模組提供了一些準系統,用於處理 URL 字串。這些功能包括 urlparse()、 urlunparse()和 urljoin()。

  2.urlparse()將 urlstr 解析成一個 6 元組(prot_sch, net_loc, path, params, query, frag)。前面已 經描述了這裡的每個組件。

  3.urlunparse()的功能與 urlpase()完全相反,其將經 urlparse()處理的 URL 產生 urltup 這個 6 元組(prot_sch, net_loc, path, params, query, frag),拼接成 URL 並返回。

  4.urllib 模組提供了許多函數,可用於從指定 URL 下載資料,同時也可以對字串進行編 碼、解碼工作,以便在 URL 中以正確的形式顯示出來。下面將要介紹的函數包括 urlopen()、 urlretrieve()、quote()、unquote()、quote_plus()、unquote_plus()和 urlencode()。

  5.urlopen()開啟一個給定 URL 字串表示的 Web 連線,並返迴文件類型的對象。

  6.urlretrieve()不是用來以檔案的形式訪問並開啟 URL,而是用於下載完整的 HTML,把另存新檔檔案。

9.6類編碼風格

  熟悉與類相關的編碼風格,當程式複雜時更應該這樣。

  1.類名應採用駝峰命名法,即類的首字母大寫,不含底線,而執行個體名和模組名都首字母小寫,並用底線串連單詞。

  2.在每個類定義後面添加文檔字串說明,簡要描述類的功能作用,並且在每個模組下面都要添加文檔字串說明,對模組下的類可以做什麼進行簡要說明。

  3.用空行來組織代碼,但不要濫用,在類中可用一個空行分隔方法,用兩個空行分隔類。

  4.需要同時匯入標準庫中的模組和自己編寫的模組時,先編寫匯入標準庫模組的import語句,用一個空行隔開,在匯入自己編寫的模組import語句。在包含多個import語句時,可以讓人容易弄明白程式中各個模組的由來。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.