做者 | 弗拉德
來源 | 弗拉德(公衆號:fulade_me)python
使用類幾乎能夠模擬任何東西。下面來編寫一個表示小狗的簡單類Dog——它表示的不是特定的小狗,而是任何小狗。對於大多數寵物狗,咱們都知道些什麼呢?它們都有名字和年齡,咱們還知道,大多數小狗還會蹲下和打滾。因爲大多數小狗都具有上述兩項信息(名字和年齡)和兩種行爲(蹲下和打滾),咱們的Dog類將包含它們。這個類讓Python知道如何建立表示小狗的對象。編寫這個類後,咱們將使用它來建立表示特定小狗的實例。git
根據Dog類建立的每一個實例都將存儲名字和年齡。咱們賦予了每條小狗蹲下(sit()
)和打滾(roll_over()
)的能力:github
class Dog(): """一次模擬小狗的簡單嘗試""" def __init__(self, name, age): """初始化屬性name和age""" self.name = name self.age = age def sit(self): """模擬小狗被命令時蹲下""" print(self.name.title() + " is now sitting.") def roll_over(self): """模擬小狗被命令時打滾""" print(self.name.title() + " rolled over!")
首先咱們定義了一個名爲Dog的類。根據約定,在Python中,首字母大寫的名稱指的是類。 這個類定義中的括號是空的,由於咱們要從空白建立這個類。
而後,咱們編寫了一個文檔字符串,對這個類的功能做了描述。dom
類中的函數稱爲方法,咱們來第一個,方法__init__()
是一個特殊的方法,每當你根據Dog類建立新實例時,Python都會自動運行它。在這個方法的名稱中,開頭和末尾各有兩個下劃線,這是一種約定,旨在避免Python默認方法與普通方法發生名稱衝突。 ide
咱們將方法__init__()
定義成了包含三個形參:self
、name
和age
。在這個方法的定義中,形參self必不可少,還必須位於其餘形參的前面。由於 Python調用這個__init__()
方法來建立Dog實例時,將自動傳入實參self。每一個與類相關聯的方法調用都自動傳遞實參self,它是一個指向實例自己的引用,讓實例可以訪問類中的屬性和方法。咱們建立Dog實例時,Python將調用Dog類的方法__init__()
。咱們將經過實參向Dog()
傳遞名字和年齡。self會自動傳遞,所以咱們不須要傳遞它。每當咱們根據Dog類建立實例時,都只需給最後兩個形參(name
和age
)提供值。函數
__init__()
內的兩個變量都有前綴self。以self爲前綴的變量均可供類中的全部方法使用,咱們還能夠經過類的任何實例來訪問這些變量。self.name = name
獲取存儲在形參name
中的值,並將其存儲到變量name
中,而後該變量被關聯到當前建立的實例。self.age = age
的做用與此相似。像這樣可經過實例訪問的變量稱爲屬性。 動畫
Dog類還定義了另外兩個方法:sit()
和roll_over()
。因爲這些方法不須要額外的信
息,如名字或年齡,所以它們只有一個形參self
。咱們後面將建立的實例可以訪問這些方法,換句話說,它們都會蹲下和打滾。當前,sit()
和roll_over()
所作的有限,它們只是打印一條消息,指出小狗正蹲下或打滾。但能夠擴展這些方法以模擬實際狀況:若是這個類包含在一個計算機遊戲中,這些方法將包含建立小狗蹲下和打滾動畫效果的代碼。若是這個類是用於控制機器狗的,這些方法將引導機器狗作出蹲下和打滾的動做。code
可將類視爲有關如何建立實例的說明。Dog類是一系列說明,讓Python知道如何建立表示特定小狗的實例。
下面來建立一個表示特定小狗的實例:對象
my_dog = Dog('willie', 6) print("My dog's name is " + my_dog.name.title() + ".") print("My dog is " + str(my_dog.age) + " years old.")
這裏使用的是前一個示例中編寫的Dog類。咱們讓Python建立一條名字爲'willie'、 年齡爲6的小狗。遇到這行代碼時,Python使用實參'willie'和6調用Dog類中的方法__init__()
。 方法__init__()
建立一個表示特定小狗的示例,並使用咱們提供的值來設置屬性name
和age
。方法__init__()
並未顯式地包含return
語句,但Python自動返回一個表示這條小狗的實例。咱們將這個實例存儲在變量my_dog
中。在這裏,命名約定頗有用,咱們一般能夠認爲首字母大寫的名稱(如 Dog)指的是類,而小寫的名稱(如my_dog)指的是根據類建立的實例。遊戲
要訪問實例的屬性,可以使用句點表示法。咱們編寫了以下代碼來訪問my_dog
的屬性name的值:
my_dog.name
句點表示法在Python中很經常使用,這種語法演示了Python如何獲悉屬性的值。在這裏,Python先找到實例my_dog
,再查找與這個實例相關聯的屬性name
。在Dog類中引用這個屬性時,使用的是self.name
。而後咱們使用一樣的方法來獲取屬性age的值。
輸出以下:
My dog's name is Willie. My dog is 6 years old.
根據Dog類建立實例後,就可使用句點表示法來調用Dog類中定義的任何方法。下面來讓小狗蹲下和打滾:
my_dog = Dog('willie', 6) my_dog.sit() my_dog.roll_over()
要調用方法,可指定實例的名稱(這裏是my_dog)和要調用的方法,並用句點分隔它們。執行代碼my_dog.sit()時,Python在類Dog中查找方法sit()
並運行其代碼。Python以一樣的方式解讀代碼my_dog.roll_over()
。
這種語法頗有用。若是給屬性和方法指定了合適的描述性名稱,如name
、age
、sit()
和roll_over()
,即使是從未見過的代碼塊,咱們也可以輕鬆地推斷出它是作什麼的。
可按需求根據類建立任意數量的實例。下面再建立一個名爲your_dog
的實例:
my_dog = Dog('willie', 6) your_dog = Dog('lucy', 3) print("My dog's name is " + my_dog.name.title() + ".") print("My dog is " + str(my_dog.age) + " years old.") my_dog.sit() print("\nYour dog's name is " + your_dog.name.title() + ".") print("Your dog is " + str(your_dog.age) + " years old.") your_dog.sit()
在這個實例中,咱們建立了兩條小狗,它們分別名爲Willie和Lucy。每條小狗都是一個獨立的實例,有本身的一組屬性,可以執行相同的操做:
My dog's name is Willie. My dog is 6 years old. Willie is now sitting. Your dog's name is Lucy. Your dog is 3 years old. Lucy is now sitting.
就算咱們給第二條小狗指定一樣的名字和年齡,Python依然會根據Dog類建立另外一個實例。 你可按需求根據一個類建立任意數量的實例,條件是將每一個實例都存儲在不一樣的變量中,或佔用 列表或字典的不一樣位置。
你可使用類來模擬現實世界中的不少情景。類編寫好後,你的大部分時間都將花在使用根據類建立的實例上。你須要執行的一個重要任務是修改實例的屬性。你能夠直接修改實例的屬性,也能夠編寫方法以特定的方式進行修改。
下面來編寫一個表示汽車的類,它存儲了有關汽車的信息,還有一個彙總這些信息的方法:
class Car(): """一次模擬汽車的簡單嘗試""" def __init__(self, make, model, year): """初始化描述汽車的屬性""" self.make = make self.model = model self.year = year def get_descriptive_name(self): """返回整潔的描述性信息""" long_name = str(self.year) + ' ' + self.make + ' ' + self.model return long_name.title() my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name())
咱們定義了方法__init__()
。與前面的Dog類中同樣,這個方法的第一個形參爲self,咱們還在這個方法中包含了另外三個形參:make
、model
和year
。方法__init__()
接受這些形參的值,並將它們存儲在根據這個類建立的實例的屬性中。建立新的Car實例時,咱們須要指定其製造商、型號和生產年份。
而後咱們又定義了一個名爲get_descriptive_name()
的方法,它使用屬性year
、make
和model
建立一個對汽車進行描述的字符串,讓咱們無需分別打印每一個屬性的值。爲在這個方法中訪問屬性的值,咱們使用了self.make
、self.model
和self.year
。咱們根據Car類建立了一個實例,並將其存儲到變量my_new_car
中。接下來,咱們調用方法get_descriptive_name()
,指出咱們擁有的是一輛什麼樣的汽車:
2016 Audi A4
類中的每一個屬性都必須有初始值,哪怕這個值是0或空字符串。在有些狀況下,如設置默認值時,在方法__init__()
內指定這種初始值是可行的,若是你對某個屬性這樣作了,就無需包含爲它提供初始值的形參。
下面來添加一個名爲odometer_reading
的屬性,其初始值老是爲0。咱們還添加了一個名爲read_odometer()
的方法,用於讀取汽車的里程錶:
class Car(): def __init__(self, make, model, year): """初始化描述汽車的屬性""" self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): """返回整潔的描述性信息""" long_name = str(self.year) + ' ' + self.make + ' ' + self.model return long_name.title() def read_odometer(self): """打印一條指出汽車裏程的消息""" print("This car has " + str(self.odometer_reading) + " miles on it.") my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name()) my_new_car.read_odometer()
如今,當Python調用方法__init__()
來建立新實例時,將像前一個示例同樣以屬性的方式存儲製造商、型號和生產年份。接下來,Python將建立一個名爲odometer_reading
的屬性,並將其初始值設置爲0。咱們還定義了一個名爲read_odometer()
的方法,它讓你可以輕鬆地獲悉汽車的里程。
一開始汽車的里程爲0:
2016 Audi A4 This car has 0 miles on it.
出售時里程錶讀數爲0的汽車並很少,所以咱們須要一個修改該屬性的值的途徑。
能夠以三種不一樣的方式修改屬性的值:直接經過實例進行修改;經過方法進行設置;經過方 法進行遞增(增長特定的值)。下面依次介紹這些方法。
1. 直接修改屬性的值
要修改屬性的值,最簡單的方式是經過實例直接訪問它。下面的代碼直接將里程錶讀數設置爲23:
my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name()) my_new_car.odometer_reading = 23 my_new_car.read_odometer()
咱們使用句點表示法來直接訪問並設置汽車的屬性odometer_reading
。這行代碼讓 Python在實例my_new_car
中找到屬性odometer_reading
,並將該屬性的值設置爲23:
2016 Audi A4 This car has 23 miles on it.
有時候須要像這樣直接訪問屬性,但其餘時候須要編寫對屬性進行更新的方法。
若是有替你更新屬性的方法,將大有裨益。這樣,你就無需直接訪問屬性,而可將值傳遞給一個方法,由它在內部進行更新。下面的示例演示了一個名爲update_odometer()
的方法:
class Car(): #### 前面的代碼省略 def update_odometer(self, mileage): """將里程錶讀數設置爲指定的值""" self.odometer_reading = mileage my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name()) my_new_car.update_odometer(23) my_new_car.read_odometer()
對Car類所作的惟一修改是,添加了方法update_odometer()
。這個方法接受一個里程值,並將其存儲到self.odometer_reading
中。而後咱們調用了update_odometer()
,並向它提供了實參23(該實參對應於方法定義中的形參mileage
)。它將里程錶讀數設置爲23;而方法read_odometer()
打印該讀數:
2016 Audi A4 This car has 23 miles on it.
可對方法update_odometer()
進行擴展,使其在修改里程錶讀數時作些額外的工做。下面來添加一些邏輯,禁止任何人將里程錶讀數往回調:
class Car(): #### 前面的代碼省略 def update_odometer(self, mileage): """將里程錶讀數設置爲指定的值 禁止將里程錶讀數往回調""" if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print("You can't roll back an odometer!")
如今,update_odometer()
在修改屬性前檢查指定的讀數是否合理。若是新指定的里程 (mileage
)大於或等於原來的里程(self.odometer_reading
),就將里程錶讀數改成新指定的里程,不然就發出警告,指出不能將里程錶往回撥。
有時候須要將屬性值遞增特定的量,而不是將其設置爲全新的值。假設咱們購買了一輛二手
車,且從購買到登記期間增長了100英里的里程,下面的方法讓咱們可以傳遞這個增量,並相應地增長里程錶讀數:
class Car(): def increment_odometer(self, miles): """將里程錶讀數增長指定的量""" self.odometer_reading += miles my_used_car = Car('subaru', 'outback', 2013) print(my_used_car.get_descriptive_name()) my_used_car.update_odometer(23500) my_used_car.read_odometer() my_used_car.increment_odometer(100) my_used_car.read_odometer()
新增的方法increment_odometer()
接受一個單位爲英里的數字,並將其加入到self.odometer_reading
中。而後咱們建立了一輛二手車——my_used_car
。咱們調用方法update_odometer()
並傳入23500,將這輛二手車的里程錶讀數設置爲23500。而後咱們調用increment_odometer()
並傳入100,以增長從購買到登記期間行駛的100英里:
2013 Subaru Outback This car has 23500 miles on it. This car has 23600 miles on it.
你能夠輕鬆地修改這個方法,以禁止增量爲負值,從而防止有人利用它來回撥里程錶。
小做業
15-1 用戶:建立一個名爲 User 的類,其中包含屬性 first_name 和 last_name。在類User中定義一個名爲 describe_user()的方 法,它打印用戶信息摘要,建立多個表示不一樣用戶的實例,並對每一個實例都調用上述兩個方法。
15-2 在爲完成練習 15-1 而編寫的User類中,添加一個名爲login_attempts的屬性。編寫一個名爲increment_login_attempts()的方法,它將屬性login_attempts 的值加 1。再編寫一個名爲 reset_login_attempts()的方法,它將屬性login_attempts的值重置爲0。
根據User類建立一個實例,再調用方法increment_login_attempts()屢次。打印屬性login_attempts的值,確認它被正確地遞增;而後,調用方法 reset_login_attempts(),並再次打印屬性 login_attempts 的值,確認它被重置爲0。
想查看做業答案能夠去個人Githu倉庫在文件夾15-1_15-2下