成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Python學(xué)習(xí)之路8.1-類

hss01248 / 3678人閱讀

摘要:被繼承的類稱為父類基類或超類,新的類稱為子類或派生類。但要注意的是,繼承關(guān)系應(yīng)只發(fā)生在有較強(qiáng)相互關(guān)系的類之間,比如從車類派生出電動(dòng)車類,沒有從車類派生出哈士奇這種騷操作。

《Python編程:從入門到實(shí)踐》筆記。
本章主要介紹一種重要的編程思想:面向?qū)ο缶幊?,包括了類與對(duì)象等概念及操作。
1. 概述

面向?qū)ο缶幊?Object-oriented programming, OOP)是最有效的軟件編寫方法之一。面向?qū)ο蟮乃枷胍彩侨祟愖怨耪J(rèn)識(shí)世界的方法,即“分門別類”。而在以往的經(jīng)驗(yàn)里,筆者印象最深刻的面向?qū)ο笏枷刖褪侵袑W(xué)生物課本上對(duì)自然界的分類:界門綱目科屬種。這里要明白兩個(gè)概念:類與對(duì)象。類是一個(gè)總的抽象概念,是一群相似事物的總括,是一個(gè)虛的概念,而這些“事物”便是對(duì)象,例如:“狗”這一概念,這就是一個(gè)“類”,哪怕是具體到某一個(gè)特定的種類,比如哈士奇,這也是個(gè)類,只有當(dāng)真正具體到某一條狗時(shí),比如“你家的哈士奇A”,這才到達(dá)了“對(duì)象”這一概念,綜上:類是抽象的,對(duì)象是實(shí)際的。而從類到對(duì)象的過程,就叫做類的實(shí)例化。

2. 創(chuàng)建和使用類 2.1 創(chuàng)建一個(gè)Car類

在Python中類名一般采用駝峰命名法,即每個(gè)單詞的首字母大寫,而不使用下劃線,實(shí)例名和模塊名都采用小寫,用下劃線拼接。并且,不論是在寫函數(shù),類,還是代碼文件,最好都加上一個(gè)文檔字符串,比如下面的三引號(hào)字符串。

class Car:
    """一次模擬汽車的簡(jiǎn)單嘗試"""

    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.")

    def update_odometer(self, mileage):
        """將里程表讀書設(shè)置為指定的值,且禁止讀數(shù)回調(diào)"""
        if mileage <= 0:
            print("Mileage must be bigger than 0!")
        elif mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can"t roll back an odometer!")

    def increment_odometer(self, miles):
        """將里程表讀數(shù)增加指定的量,且該量必須為正數(shù)"""
        if miles > 0:
            self.odometer_reading += miles
        else:
            print("Mile must be bigger than 0!")

    def fill_gas_tank(self):
        """將油箱裝滿"""
        print("The gas tank has been filled!")

以下有幾點(diǎn)需要注意:

①類中的函數(shù)稱為方法,比如上述定義的三個(gè)函數(shù);類中與self相綁定的變量稱為屬性,比如make,model,year(不是指那三個(gè)形參,而是與self綁定的變量)。

②每一個(gè)類必有一個(gè)__init()__方法,這個(gè)方法被稱為構(gòu)造方法(在C++中被稱為構(gòu)造函數(shù),不過不用太糾結(jié)到底是“方法”還是“函數(shù)”,一個(gè)東西放在了不同地方有了不同的名字而已)。當(dāng)然它也有默認(rèn)的版本,即只有一個(gè)self參數(shù),并且該函數(shù)什么也不做,這也表明,你甚至都不用定義這個(gè)方法,到時(shí)候Python會(huì)自動(dòng)生成并調(diào)用默認(rèn)構(gòu)造方法,不過“不定義構(gòu)造方法”這種情況估計(jì)也就只有像筆者這樣初學(xué)的時(shí)候才能遇到 ^_^。

③Python中self參數(shù)是類中每個(gè)非靜態(tài)方法必須要有的形參,且必須放在第一個(gè),它是一個(gè)指向?qū)嵗旧恚ú皇穷惐旧恚。┑囊粋€(gè)引用,讓實(shí)例能夠訪問類中的屬性和方法,我們?cè)谡{(diào)用類的方法時(shí)不用手動(dòng)傳入該參數(shù),它會(huì)自動(dòng)被傳入。類中的屬性在類中所有的方法里都能被訪問,這便是通過self參數(shù)實(shí)現(xiàn)的。如果站在C++的角度理解,self就相當(dāng)于C++類里的this指針,指向?qū)ο笞陨怼?/p>

④類中的每個(gè)屬性都必須有初始值,哪怕這個(gè)值是0,空字符串或者None。比如本例中的四個(gè)屬性,前三個(gè)屬性的值由用戶傳入,odometer_reading的值被設(shè)為了0。

⑤在上述代碼的第一行類名Car后面可帶可不帶小括號(hào),即class Car:這種寫法可行,class Car():這種寫法也可以。

2.2 使用該Car類

以下代碼創(chuàng)建了一個(gè)Car類的對(duì)象,并對(duì)該對(duì)象進(jìn)行了簡(jiǎn)單的操作。

# 代碼:
class Car:
    -- snip --     # 這不是一個(gè)Python語法!這里只是表示省略。

my_new_car = Car("audi", "a4", 2016)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()

# 直接修改屬性
my_new_car.odometer_reading = -100
my_new_car.read_odometer()
my_new_car.odometer_reading += -1
my_new_car.read_odometer()

# 通過方法修改屬性
my_new_car.update_odometer(-100)
my_new_car.read_odometer()
my_new_car.increment_odometer(-1)
my_new_car.read_odometer()

my_new_car.update_odometer(100)
my_new_car.read_odometer()
my_new_car.increment_odometer(1)
my_new_car.read_odometer()

# 結(jié)果:
2016 Audi A4
This car has 0 miles on it.
This car has -100 miles on it.
This car has -101 miles on it.
Mileage must be bigger than 0!
This car has -101 miles on it.
Mile must be bigger than 0!
This car has -101 miles on it.
This car has 100 miles on it.
This car has 101 miles on it.

從上述代碼可以看出,Python和C++,Java一樣,也是使用句點(diǎn)表示法來訪問屬性以及調(diào)用方法。從上述代碼及結(jié)果可以看出,實(shí)例的屬性可以直接也可以通過方法進(jìn)行訪問和修改。

直接訪問對(duì)象的屬性可以使操作變得簡(jiǎn)單,但這違反了封閉性原則,并且直接修改屬性也不利于規(guī)范對(duì)屬性的操作。比如代碼中將里程設(shè)置為一個(gè)負(fù)值,且在增加里程時(shí)增量也是一個(gè)負(fù)值,這顯然不符合常理(雖然有時(shí)也可以這么做)。而如果將對(duì)屬性的操作放入方法中,則可以規(guī)范這些操作,如上述的read_odometer()update_odometer(),increment_odometer()等方法。并且這也是面向?qū)ο缶幊趟岢淖龇?,盡量不要將屬性直接對(duì)外暴露。但可惜的是,Python中任何種類的屬性都能被直接操作。

3. 繼承

編寫類時(shí)并非總是從零開始,如果要編寫的類是現(xiàn)有類的特殊版本,即有相同或相似的屬性和方法,則可以從現(xiàn)有類繼承(派生)出新的類。被繼承的類稱為“父類”、“基類”“超類(superclass)”,新的類稱為“子類“”派生類“

但要注意的是,繼承關(guān)系應(yīng)只發(fā)生在有較強(qiáng)相互關(guān)系的類之間,比如從車類派生出電動(dòng)車類,沒有從車類派生出哈士奇這種騷操作。

以下是從Car類派生出ElectricCar類的代碼:

# 代碼:
class Car:
    -- snip --
    
class ElectricCar(Car):
    """電動(dòng)汽車的獨(dú)特之處"""

    def __init__(self, make, model, year):
        """初始化父類的屬性,再初始化電動(dòng)汽車特有的屬性"""
        super().__init__(make, model, year)
        self.battery_size = 70

    def describe_battery(self):
        """打印一條描述電池容量的消息"""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")

    def fill_gas_tank(self):   # 重寫了父類的方法
        """電動(dòng)車沒有油箱"""
        print("This car doesn"t need a gas tank!")


my_audi = Car("audi", "a4", 2018)
print(my_audi.get_descriptive_name())
my_audi.fill_gas_tank()
print()     # 用作空行

my_tesla = ElectricCar("tesla", "model s", 2018)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
my_tesla.fill_gas_tank()

# 結(jié)果:
2018 Audi A4
The gas tank has been filled!

2018 Tesla Model S
This car has a 70-kWh battery.
This car doesn"t need a gas tank!

從以上代碼可以總結(jié)出幾點(diǎn):

①創(chuàng)建子類的實(shí)例時(shí),Python首先需要對(duì)父類進(jìn)行初始化操作,通過super()函數(shù)返回父類的引用,然后再調(diào)用父類的構(gòu)造方法,即super().__init__(參數(shù)列表)。在Python2中,對(duì)父類的初始化需要以如下方式初始化父類:

super(ElectricCar, self).__init__(make, model, year)

在Python3中也可以按上述方式來初始化父類,但也可以在單繼承時(shí)省略super()函數(shù)中的參數(shù)。

②子類可以訪問父類的所有屬性,還可以增加新的屬性:my_tesla對(duì)象訪問了父類的make, model, year等屬性,并且還增加了battery_size屬性。

③子類可以重寫父類的方法:ElectricCar類重寫了Car類的fill_gas_tank()方法。

這里需要區(qū)分兩個(gè)概念:重寫(Override)重載(Overload)

重寫也叫覆蓋,主要是用在繼承上。當(dāng)繼承關(guān)系上的類中有相同的方法,但子類和父類在該方法中的操作不相同時(shí),子類對(duì)該方法進(jìn)行重新編寫,覆蓋掉從父類繼承下來的方法。在調(diào)用時(shí),Python會(huì)自動(dòng)判斷該對(duì)象是否是派生類來調(diào)用該方法相應(yīng)的實(shí)現(xiàn)。正是有了重寫,面向?qū)ο笾?strong>多態(tài)(Polymorphism)這一特性才得以實(shí)現(xiàn)。

重載主要用于函數(shù)(方法)。在像C/C++,Java這樣的語言中,可以有多個(gè)同名的函數(shù),但參數(shù)列表必須不相同,比如參數(shù)個(gè)數(shù),參數(shù)類型不相同。這些語言則根據(jù)參數(shù)列表來區(qū)分到底調(diào)用的是同名函數(shù)中的哪一個(gè)函數(shù)。但重載并不屬于多態(tài)性!這些語言在編譯源文件的時(shí)候,會(huì)根據(jù)參數(shù)列表來對(duì)同名函數(shù)生成不同的函數(shù)名(具體方法就是添加前綴或后綴),然后將源代碼中的這些同名函數(shù)都替換成新函數(shù)名,所以重載并不屬于多態(tài)。但是Python中并沒有函數(shù)重載這種說法!因?yàn)镻ython有關(guān)鍵字參數(shù)和可變參數(shù)這種神器(當(dāng)然C++也有變長(zhǎng)參數(shù),它用三個(gè)點(diǎn)表示,不知道Python可變參數(shù)的底層實(shí)現(xiàn)是不是就和C++的變長(zhǎng)參數(shù)有關(guān))。

然而這都不重要!明白重寫和重載的概念,會(huì)用就行了,至于這倆和多態(tài)究竟有沒有關(guān)系并不重要,至今網(wǎng)上對(duì)這倆與多態(tài)的關(guān)系都沒有一個(gè)準(zhǔn)確的說法。筆者以前看C++的書的時(shí)候記得專門把重載的底層實(shí)現(xiàn)給提了出來(哪本書忘了),但筆者才疏學(xué)淺,暫不清楚重寫在編譯時(shí)是個(gè)什么情況,說不定也是靠生成新函數(shù)名并替換,如果這樣的話,那重載也可以算多態(tài)了,不過這只是筆者的猜測(cè)!感興趣的小伙伴可自行研究這倆在編譯時(shí)的情況。

之所以把這倆多帶帶提出來,主要是好多人在考研復(fù)試或者找工作面試的時(shí)候載到了這個(gè)概念上。尤其是考研,考研復(fù)試似乎更傾向于重寫屬于多態(tài),重載不屬于多態(tài)。

3.1 將實(shí)例用作屬性

使用代碼模擬實(shí)物時(shí),隨著開發(fā)的進(jìn)展,勢(shì)必一個(gè)類的屬性和方法將會(huì)越來越多,單單一個(gè)類的代碼就會(huì)越來越長(zhǎng)。這時(shí)可以考慮是否能將其中一部分代碼多帶帶提取出來作為一個(gè)新的類。比如前面的ElectricCar類里的電池就可以多帶帶提出來作為一個(gè)類。

# 代碼:
class Car:
    -- snip --

class Battery:
    """一次模擬電動(dòng)汽車電池的簡(jiǎn)單嘗試"""

    def __init__(self, battery_size=70):
        """初始化電池的屬性"""
        self.battery_size = battery_size

    def describe_battery(self):
        """打印一條描述電池容量的信息"""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")

    def get_range(self):
        """輸出電池的續(xù)航里程"""
        if self.battery_size == 70:
            miles = 240
        elif self.battery_size == 85:
            miles = 270

        message = "This car can go approximately " + str(miles) + " miles on a full charge."
        print(message)

class ElectricCar(Car):
    def __init__(self, make, model, year):
        super().__init__(make, model, year)
        self.battery = Battery()

my_tesla = ElectricCar("tesla", "model s", 2018)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()

# 結(jié)果:
2018 Tesla Model S
This car has a 70-kWh battery.
This car can go approximately 240 miles on a full charge.

模擬復(fù)雜的實(shí)物時(shí),需要解決一些有趣的問題,比如續(xù)航里程是電池的屬性還是汽車的屬性呢?如果只描述一輛車,那將get_range()方法放入Battery()中并無不妥,但如果要描述整個(gè)汽車產(chǎn)品線呢?比如這一款車型能跑多遠(yuǎn),那也許將該方法放入ElectricCar類則比較合適。但不管怎樣,這里強(qiáng)調(diào)的是應(yīng)該站在一個(gè)更高的邏輯層面考慮問題。

4. 從模塊導(dǎo)入類

與上一篇寫關(guān)于函數(shù)的文章相似,類也可以多帶帶形成模塊??梢砸粋€(gè)類就是一個(gè)模塊,也可以多個(gè)類(一般是相關(guān)聯(lián)的類)放入一個(gè)模塊。比如將上述的Car類多帶帶放在一個(gè)文件中,除去此類的代碼,其他代碼均刪除,最后將該文件命名為car.py(注意這里的文件名是小寫的)。然后再在程序中帶入該類:

from car import Car
# 如果命名有沖突,也可以給Car類起個(gè)別名
# from car import Car as C

my_new_car = Car("audi", "a4", 2018)
print(my_new_car.get_descriptive_name())

my_new_car.odometer_reading = 23
my_new_car.read_odometer()

也可以將多個(gè)相關(guān)聯(lián)的類放入同一個(gè)文件中,形成一個(gè)模塊,比如上面的Car類,ElectricCar類和Battery類,將該文件命名為cars.py,最后導(dǎo)入該文件:

from cars import Car, ElectricCar

my_beetle = Car("volkswagen", "beetle", 2018)
my_tesla = ElectricCar("tesla", "model s", 2018)
-- snip --     # 后面的代碼和之前的類似,不在贅述

也可以將整個(gè)模塊導(dǎo)入,并使用句點(diǎn)表示法使用模塊中的類:

import cars

my_car = car.Car("volkswagen", "beetle", 2018)
my_tesla = car.ElectricCar("tesla", "model s", 2018)

還可以導(dǎo)入模塊中的所有類(不推薦此法,容易產(chǎn)生命名沖突?。?,此時(shí)便不需要使用句點(diǎn)表示法。

from cars import *

my_beetle = Car("volkswagen", "beetle", 2018)

還可以在模塊中導(dǎo)入另一個(gè)模塊,比如,將Car類多帶帶放在一個(gè)文件中形參一個(gè)模塊,命名為car.py,再新建一個(gè)模塊electric_car.py用于存放Battery類和ElectricCar類,并在該模塊中帶入Car類:

from car import Car

class Battery:
    -- snip --

class ElectricCar(Car):
    -- snip --

最后在執(zhí)行文件的源代碼中根據(jù)需要導(dǎo)入類:

# 這是書中導(dǎo)入兩個(gè)類的代碼
from car import Car
from electric_car import ElectricCar     

my_car = Car("audi", "a4", 2018)
my_tesla = ElectricCar("tesla", "model s", 2018)

之前讀到這的時(shí)候覺得能不能像以下這樣的方式導(dǎo)入Car類:

from electric_car import Car, ElectricCar

my_car = Car("audi", "a4", 2018)
my_tesla = ElectricCar("tesla", "model s", 2018)

后來親測(cè),這樣做也是可以的。那問題就來了,像書中那樣的導(dǎo)入方式是不是發(fā)生了代碼的覆蓋呢?哪種導(dǎo)入的效率更高呢?筆者在這里還有點(diǎn)懵,后續(xù)再更新吧。

模塊導(dǎo)入的方法還有很多,甚至能直接從GitHub導(dǎo)入模塊,上述的導(dǎo)入方式只是皮毛。最后用一個(gè)從標(biāo)準(zhǔn)庫導(dǎo)入OrderedDict類的示例結(jié)束本文。之前版本的Python中普通字典類是不確保鍵值對(duì)之前的順序的,想要確保順序就得使用OrderedDict類。但現(xiàn)在從3.6版本起,Python也確保了普通字典里鍵值對(duì)也是有序的了,但是為了兼容性考慮(有可能你的代碼還要運(yùn)行在3.6之前的版本),目前還是建議使用OrderedDict類。

# 代碼:
from collections import OrderedDict

favorite_languages = OrderedDict()

favorite_languages["jen"] = "python"
favorite_languages["sarah"] = "c"
favorite_languages["edward"] = "ruby"
favorite_languages["phil"] = "python"

for name, language in favorite_languages.items():
    print(name.title() + ""s favorite_language is " + language.title())

# 結(jié)果:
Jen"s favorite_language is Python
Sarah"s favorite_language is C
Edward"s favorite_language is Ruby
Phil"s favorite_language is Python
迎大家關(guān)注我的微信公眾號(hào)"代碼港" & 個(gè)人網(wǎng)站 www.vpointer.net ~

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/41794.html

相關(guān)文章

  • Python學(xué)習(xí)之路21-序列構(gòu)成的數(shù)組

    摘要:第行把具名元組以的形式返回。對(duì)序列使用和通常號(hào)兩側(cè)的序列由相同類型的數(shù)據(jù)所構(gòu)成當(dāng)然不同類型的也可以相加,返回一個(gè)新序列。從上面的結(jié)果可以看出,它雖拋出了異常,但仍完成了操作查看字節(jié)碼并不難,而且它對(duì)我們了解代碼背后的運(yùn)行機(jī)制很有幫助。 《流暢的Python》筆記。接下來的三篇都是關(guān)于Python的數(shù)據(jù)結(jié)構(gòu),本篇主要是Python中的各序列類型 1. 內(nèi)置序列類型概覽 Python標(biāo)準(zhǔn)庫...

    ralap 評(píng)論0 收藏0
  • Python小白學(xué)習(xí)之路

    摘要:在類的成員函數(shù)中訪問實(shí)例屬性需要以為前綴。但提供一種對(duì)私有成員的訪問方式對(duì)象名類名私有成員類中保護(hù)對(duì)象類中系統(tǒng)定義的特殊成員類中私有成員多態(tài)列表項(xiàng)目 Python學(xué)習(xí)第一天 類與對(duì)象 python的成員函數(shù)在,默認(rèn)有一個(gè)self參數(shù),這是類的成員函數(shù)與普通函數(shù)的主要區(qū)別,self,位于參數(shù)列表的開頭,self也代表類的實(shí)例(對(duì)象)自身,可以使用self引用類中的屬性和成員函數(shù)。在...

    Aomine 評(píng)論0 收藏0
  • Python學(xué)習(xí)之路8.2-對(duì)Python的補(bǔ)充

    摘要:本章主要是對(duì)上一章類的補(bǔ)充。對(duì)于多態(tài)的補(bǔ)充子類可以被看成是父類的類型,但父類不能被看成是子類的類型。仍然以類為例,動(dòng)物里有哺乳動(dòng)物,卵生動(dòng)物,有能飛的動(dòng)物和不能飛的動(dòng)物,這是兩種大的分類方式。一般在中,以為結(jié)尾類的都作為接口。 《Python編程:從入門到實(shí)踐》筆記。本章主要是對(duì)上一章Python類的補(bǔ)充。 1. 從一個(gè)類派生出所有類 上一篇文章說道Python類的定義與繼承一般是如下...

    liukai90 評(píng)論0 收藏0
  • Python學(xué)習(xí)之路28-符合Python風(fēng)格的對(duì)象

    摘要:本篇繼續(xù)學(xué)習(xí)之路,實(shí)現(xiàn)更多的特殊方法以讓自定義類的行為跟真正的對(duì)象一樣。之所以要讓向量不可變,是因?yàn)槲覀冊(cè)谟?jì)算向量的哈希值時(shí)需要用到和的哈希值,如果這兩個(gè)值可變,那向量的哈希值就能隨時(shí)變化,這將不是一個(gè)可散列的對(duì)象。 《流暢的Python》筆記。本篇是面向?qū)ο髴T用方法的第二篇。前一篇講的是內(nèi)置對(duì)象的結(jié)構(gòu)和行為,本篇?jiǎng)t是自定義對(duì)象。本篇繼續(xù)Python學(xué)習(xí)之路20,實(shí)現(xiàn)更多的特殊方法以讓...

    Eric 評(píng)論0 收藏0
  • Python學(xué)習(xí)之路31-繼承的利弊

    摘要:使用抽象基類顯示表示接口如果類的作用是定義接口,應(yīng)該將其明確定義為抽象基類。此外,抽象基類可以作為其他類的唯一基類,混入類則決不能作為唯一的基類,除非這個(gè)混入類繼承了另一個(gè)更具體的混入這種做法非常少見。 《流暢的Python》筆記本篇是面向?qū)ο髴T用方法的第五篇,我們將繼續(xù)討論繼承,重點(diǎn)說明兩個(gè)方面:繼承內(nèi)置類型時(shí)的問題以及多重繼承。概念比較多,較為枯燥。 1. 繼承內(nèi)置類型 內(nèi)置類型...

    tinylcy 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<