Python學習手冊--第六部分(類)
面向對象編程 是最有效的軟件編寫方法之一。在面向對象編程中,你編寫表示現實世界中的事物和情景的類,并基于這些類來創建對象。編寫類時,你定義一大類對象都有的通用行為。基于類創建對象 時,每個對象都自動具備這種通用行為,然后可根據需要賦予每個對象獨特的個性。使用面向對象編程可模擬現實情景,其逼真程度達到了令你驚訝的地步。
根據類來創建對象被稱為實例化 ,這讓你能夠使用類的實例。在本章中,你將編寫一些類并創建其實例。你將指定可在實例中存儲什么信息,定義可對這些實例執行哪些操作。你還將編寫一些類來擴展既有類的功能,讓相似的類能夠高效地共享代碼。你將把自己編寫的類存儲在模塊中,并在自己的程序文件中導入其他程序員編寫的類。
理解面向對象編程有助于你像程序員那樣看世界,還可以幫助你真正明白自己編寫的代碼:不僅是各行代碼的作用,還有代碼背后更宏大的概念。了解類背后的概念可培養邏輯思維,讓你能夠通過編寫程序來解決遇到的幾乎任何問題。
創建和使用類
使用類幾乎可以模擬任何東西,下面我們來舉個例子:
class Person(): def __init__(self, name, age): self.name = name self.age = age def eat(self): print(self.name.title() + '現在在吃飯') def sleep(self): print(self.name.title() + '現在在睡覺')
1
2
3
4
5
6
7
8
9
10
這里需要注意的東西很多,我們來一一介紹。
類中的函數稱為方法,你前面學習的函數都可以叫方法。但千萬注意了,init()這個方法是非常特殊的,每當你創建Person類的實例時,Python會自動運行它。在這個方法中,開頭和末尾都有一個下劃線,這是一種約定,是不可以省略的。
我們在該方法中定義了三個形參:self、name、age,形參self必不可少,還必須位于其它形參的前面,為何必須在方法中定義形參self呢?因為Python調用這個方法來創建Person時,將自動傳入實參self,每個與類相關聯的方法調用都自動傳遞實參self,它是一個指向實例本身的引用,讓實例能夠訪問類中的屬性和方法。我們創建Person實例時,Python將調用_init_()方法,我們將通過實參向Person()傳遞名字和年齡,self會自動傳遞,因此我們不需要傳遞它,也就是說,我們只需要傳遞兩個實參即可。
class Person(): def __init__(self, name, age): self.name = name self.age = age def eat(self): print(self.name.title() + '現在在吃飯') def sleep(self): print(self.name.title() + '現在在睡覺') person = Person('張三', 20) print(person.name) print(person.age)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在上述程序段中,我們創建了一個Person的實例,并將張三和20作為實參進行傳遞,此時Python使用實參張三和20調用Person類中的_init_(),該方法通過我們傳遞過去的參數創建了一個Person類的實例,并使用我們提供的值來設置屬性name和age。方法 init()并未顯式地包含return語句,但Python會自動返回一個表示這個人的實例。
要訪問屬性,很簡單,創建了實例之后,通過句點表示法調用。
person.name persn.age
1
2
調用方法也是一樣的,使用句點表示法即可。
person.eat() person.sleep()
1
2
使用類和實例
你可以使用類來模擬現實世界中的很多情景。類編寫好后,你的大部分時間都將花在使用根據類創建的實例上。你需要執行的一個重要任務是修改實例的屬性。你可以直接修改實例的屬性,也可以編寫方法以特定的方式進行修改。
下面來編寫一個表示手機的類,它存儲了有關手機的信息,還有一個匯總這些信息的方法。
class Phone(): def __init__(self, color, money, year): self.color = color self.money = money self.year = year def get_phone_info(self): """ 返回描述信息""" info = '顏色:' + self.color + ',價格:' + self.money + ',出廠日期:' + str(self.year) return info phone = Phone('red', '5000', 2019) info = phone.get_phone_info() print(info)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
第二次來看這個類應該很簡單了吧,不多說了。
類中的每個屬性都必須有初始值,哪怕這個值是0或空字符串。在有些情況下,如設置默認值時,在方法__init__() 內指定這種初始值是可行的;如果你對某個屬性這樣做了,就無需包含為它提供初始值的形參。
下面來添加一個名為use_time的屬性,其初始值總是為0,我們還添加一個名為read_use_time的方法,用戶讀取手機的使用時間。
class Phone(): def __init__(self, color, money, year): self.color = color self.money = money self.year = year self.use_time = 0 def read_use_time(self): print('手機使用時間:' + str(self.use_time)) def get_phone_info(self): """ 返回描述信息""" info = '顏色:' + self.color + ',價格:' + self.money + ',出廠日期:' + str(self.year) return info phone = Phone('red', '5000', 2019) info = phone.get_phone_info() print(info) phone.read_use_time()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
運行程序,控制臺輸出手機使用時間為0,在現實生活中,出售時手機使用時間為0的手機可不多,因此我們需要一個修改該屬性的值得途徑。
可以以三種不同的方式修改屬性的值:直接通過實例進行修改;通過方法進行設置;通過方法進行遞增(增加特定的值)。下面依次介紹這些方法。
要修改屬性的值,最簡單的方法就是通過實例直接訪問它。
class Phone(): ...... phone = Phone('red', '5000', 2019) info = phone.get_phone_info() print(info) phone.use_time = 10 phone.read_use_time()
1
2
3
4
5
6
7
8
如果有替你更新屬性的方法,將大有裨益。這樣,你就無需直接訪問屬性,而可將值傳遞給一個方法,由它在內部進行更新。
class Phone(): ...... def update_use_time(self, use_time): self.use_time = use_time ...... phone = Phone('red', '5000', 2019) info = phone.get_phone_info() print(info) phone.update_use_time(10) phone.read_use_time()
1
2
3
4
5
6
7
8
9
10
11
有時候需要將屬性值遞增特定的量,而不是將其設置為全新的值。
class Phone(): ...... def increment_use_time(self, time): self.use_time += time ...... phone = Phone('red', '5000', 2019) info = phone.get_phone_info() print(info) phone.increment_use_time(10) phone.read_use_time()
1
2
3
4
5
6
7
8
9
10
11
繼承
編寫類時,并非總是要從空白開始。如果你要編寫的類是另一個現成類的特殊版本,可使用繼承 。一個類繼承 另一個類時,它將自動獲得另一個類的所有屬性和方法;原有的類稱為父類 ,而新類稱為子類 。子類繼承了其父類的所有屬性和方法,同時還可以定義自己的屬性和方法。
創建子類的實例時,Python首先需要完成的任務是給父類的所有屬性賦值,為此,子類的方法_init_()需要父類施以援手。
class Animal(): def __init__(self, name, age): self.name = name self.age = age def eat(self): print(self.name + '會吃飯') def sleep(self): print(self.name + '會睡覺') class Dog(Animal): def __init__(self, name, age): # 初始化父類屬性 super().__init__(name, age) def arithmetic(self): print(self.name + '會算術') dog = Dog('John', 20) print(dog.eat()) print(dog.sleep()) print(dog.arithmetic())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
首先是Animal類的代碼。創建子類時,父類必須包含在當前文件中,且位于子類前面。接下來我們定義了Dog類,定義子類時,必須在括號內指定父類的名稱,方法_init_()接收創建Animal所需的參數。
子類中的super()是一個特殊的函數,幫助Python將父類和子類關聯起來,這行代碼讓Python調用Animal的父類的方法_init_()方法,讓Animal實例包含父類的所有屬性。父類也稱為超類,名稱super因此而得名。
讓一個類繼承另一個類之后,可添加區分子類和父類所需的新屬性和方法。
下面來添加一個狗特有的屬性,以及一個描述該屬性的方法。
class Animal(): def __init__(self, name, age): self.name = name self.age = age def eat(self): print(self.name + '會吃飯') def sleep(self): print(self.name + '會睡覺') class Dog(Animal): def __init__(self, name, age): # 初始化父類屬性 super().__init__(name, age) self.food = 'bone' def describe_food(self): print('狗的食物是' + self.food) def arithmetic(self): print(self.name + '會算術') dog = Dog('John', 20) print(dog.eat()) print(dog.sleep()) print(dog.arithmetic()) print(dog.describe_food())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
對于父類的方法,只要它不符合子類模擬的實物的行為,都可對其進行重寫。為此,可在子類中定義一個這樣的方法,即它與要重寫的父類方法同名。這樣,Python將不會考慮這個父類方法,而只關注你在子類中定義的相應方法。
假如有這樣一個品種的狗,它很神奇,從來不用睡覺,那么Animal類的sleep()對它來說就毫無意義,因此你可能想重寫它。
class Animal(): ...... class Dog(Animal): ...... def sleep(self): print(self.name + '從來不睡覺') dog = Dog('John', 20) print(dog.eat()) print(dog.sleep()) print(dog.arithmetic()) print(dog.describe_food())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
現在,如果有人對Dog類調用方法sleep() ,Python將忽略Animal類中的方法sleep() ,轉而運行上述代碼。使用繼承時,可讓子類保留從父類那里繼承而來的精華,并剔除不需要的糟粕。
使用代碼模擬實物時,你可能會發現自己給類添加的細節越來越多:屬性和方法清單以及文件都越來越長。在這種情況下,可能需要將類的一部分作為一個獨立的類提取出來。你可以將大型類拆分成多個協同工作的小類。
例如,我們在不斷給Dog類添加細節時會發現,其中很多屬性都是用來描述一只狗的 外觀的,在這種情況下,我們可以將這些屬性和方法提取出來,放到另一個名為Appearance的類中,并將Appearance的實例用作Dog類的一個屬性。
class Animal(): def __init__(self, name, age): self.name = name self.age = age def eat(self): print(self.name + '會吃飯') def sleep(self): print(self.name + '會睡覺') class Appearance(): def __init__(self, color='black', height='50'): self.color = color self.height = height class Dog(Animal): def __init__(self, name, age): # 初始化父類屬性 super().__init__(name, age) self.appearance = Appearance() def describe_food(self): print('狗的食物是' + self.food) def arithmetic(self): print(self.name + '會算術') def sleep(self): print(self.name + '從來不睡覺') dog = Dog('John', 20) print(dog.appearance.color) print(dog.appearance.height)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
導入類
隨著你不斷地給類添加功能,文件可能變得很長,即便你妥善地使用了繼承亦如此。為遵循Python的總體理念,應讓文件盡可能整潔。為在這方面提供幫助,Python允許你將類存儲在模塊中,然后在主程序中導入所需的模塊。
from animal import Animal animal = Animal('John', 20) print(animal.name) print(animal.age)
1
2
3
4
5
這跟導入模塊的語法是一樣的,所以不作過多贅述。
Python標準庫
Python標準庫 是一組模塊,安裝的Python都包含它。你現在對類的工作原理已有大致的了解,可以開始使用其他程序員編寫好的模塊了。可使用標準庫中的任何函數和類,為此,只需在程序開頭包含一條簡單的import 語句。下面來看模塊collections 中的一個類——OrderedDict 。
字典讓你能夠將信息關聯起來,但它們不記錄你添加鍵—值對的順序。要創建字典并記錄其中的鍵—值對的添加順序,可使用模塊collections 中的OrderedDict 類。OrderedDict 實例的行為幾乎與字典相同,區別只在于記錄了鍵—值對的添加順序。
from collections import OrderedDict favorite_fruit = OrderedDict() favorite_fruit['zhangsan'] = 'apple' favorite_fruit['lisi'] = 'banana' favorite_fruit['wangwu'] = 'pear' for name, fruit in favorite_fruit.items(): print(name + '最喜歡的水果是' + fruit)
1
2
3
4
5
6
7
8
9
10
我們首先從collections中導入了OrderedDict類,然后創建了OrderedDict的實例,并將其保存到了favorite_fruit變量中,這局代碼的作用是創建了一個空的有序字典,接下來,我們以每次一對的方式添加鍵值對,并遍歷字典。
這是一個很不錯的類,它兼具列表和字典的主要優點(在將信息關聯起來的同時保留原來的順序)。等你開始對關心的現實情形建模時,可能會發現有序字典正好能夠滿足需求。隨著你對標準庫的了解越來越深入,將熟悉大量可幫助你處理常見情形的模塊。
Python 面向對象編程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。