【Python 1-15】Python手把手教程之——詳解類Class以及類的使用

做者 | 弗拉德
來源 | 弗拉德(公衆號:fulade_me)python

建立和使用類

使用類幾乎能夠模擬任何東西。下面來編寫一個表示小狗的簡單類Dog——它表示的不是特定的小狗,而是任何小狗。對於大多數寵物狗,咱們都知道些什麼呢?它們都有名字和年齡,咱們還知道,大多數小狗還會蹲下和打滾。因爲大多數小狗都具有上述兩項信息(名字和年齡)和兩種行爲(蹲下和打滾),咱們的Dog類將包含它們。這個類讓Python知道如何建立表示小狗的對象。編寫這個類後,咱們將使用它來建立表示特定小狗的實例。git

建立Dog類

根據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()

類中的函數稱爲方法,咱們來第一個,方法__init__()是一個特殊的方法,每當你根據Dog類建立新實例時,Python都會自動運行它。在這個方法的名稱中,開頭和末尾各有兩個下劃線,這是一種約定,旨在避免Python默認方法與普通方法發生名稱衝突。 ide

咱們將方法__init__()定義成了包含三個形參:selfnameage。在這個方法的定義中,形參self必不可少,還必須位於其餘形參的前面。由於 Python調用這個__init__()方法來建立Dog實例時,將自動傳入實參self。每一個與類相關聯的方法調用都自動傳遞實參self,它是一個指向實例自己的引用,讓實例可以訪問類中的屬性和方法。咱們建立Dog實例時,Python將調用Dog類的方法__init__()。咱們將經過實參向Dog()傳遞名字和年齡。self會自動傳遞,所以咱們不須要傳遞它。每當咱們根據Dog類建立實例時,都只需給最後兩個形參(nameage)提供值。函數

__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__()建立一個表示特定小狗的示例,並使用咱們提供的值來設置屬性nameage。方法__init__()並未顯式地包含return語句,但Python自動返回一個表示這條小狗的實例。咱們將這個實例存儲在變量my_dog中。在這裏,命名約定頗有用,咱們一般能夠認爲首字母大寫的名稱(如 Dog)指的是類,而小寫的名稱(如my_dog)指的是根據類建立的實例。遊戲

1. 訪問屬性

要訪問實例的屬性,可以使用句點表示法。咱們編寫了以下代碼來訪問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.
2. 調用方法

根據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()
這種語法頗有用。若是給屬性和方法指定了合適的描述性名稱,如nameagesit()roll_over(),即使是從未見過的代碼塊,咱們也可以輕鬆地推斷出它是作什麼的。

3. 建立多個實例

可按需求根據類建立任意數量的實例。下面再建立一個名爲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類建立另外一個實例。 你可按需求根據一個類建立任意數量的實例,條件是將每一個實例都存儲在不一樣的變量中,或佔用 列表或字典的不一樣位置。

使用類和實例

你可使用類來模擬現實世界中的不少情景。類編寫好後,你的大部分時間都將花在使用根據類建立的實例上。你須要執行的一個重要任務是修改實例的屬性。你能夠直接修改實例的屬性,也能夠編寫方法以特定的方式進行修改。

Car類

下面來編寫一個表示汽車的類,它存儲了有關汽車的信息,還有一個彙總這些信息的方法:

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,咱們還在這個方法中包含了另外三個形參:makemodelyear。方法__init__()接受這些形參的值,並將它們存儲在根據這個類建立的實例的屬性中。建立新的Car實例時,咱們須要指定其製造商、型號和生產年份。

而後咱們又定義了一個名爲get_descriptive_name()的方法,它使用屬性yearmakemodel建立一個對汽車進行描述的字符串,讓咱們無需分別打印每一個屬性的值。爲在這個方法中訪問屬性的值,咱們使用了self.makeself.modelself.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.

有時候須要像這樣直接訪問屬性,但其餘時候須要編寫對屬性進行更新的方法。

2. 經過方法修改屬性的值

若是有替你更新屬性的方法,將大有裨益。這樣,你就無需直接訪問屬性,而可將值傳遞給一個方法,由它在內部進行更新。下面的示例演示了一個名爲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),就將里程錶讀數改成新指定的里程,不然就發出警告,指出不能將里程錶往回撥。

3. 經過方法對屬性的值進行遞增

有時候須要將屬性值遞增特定的量,而不是將其設置爲全新的值。假設咱們購買了一輛二手
車,且從購買到登記期間增長了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


公衆號

相關文章
相關標籤/搜索