python基礎===繼承


編寫類時,並非總是要從空白開始。如果你要編寫的類是另一個現成類的特殊版本,可使用繼承。一個類繼承另一個類時,它將自動獲得另一個類的所有屬性和方法;原有的類稱為父類,而新類稱為子類。子類繼承了其父類的所有屬性和方法,同時還可以定義自己的屬性和方法。

1.子類的方法__init__()

創建子類的實例時,Python首先需要完成的任務是給父類的所有屬性賦值。為此,子類的方法__init__()需要父類施以援手。
例如,下面來模擬電動汽車。電動汽車是一種特殊的汽車,因此我們可以在前面創建的Car類的基礎上創建新類ElectricCar,這樣我們就只需為電動汽車特有的屬性和行為編寫代碼。下面來創建一個簡單的ElectricCar類版本,它具備Car類的所有功能:

class car():
    """docstring for car"""
    def __init__(self, make, modle, year):
        self.make = make
        self.modle = modle
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        long_name = str(self.year) + " " +self.make + " " + self.modle
        return long_name.title()

    def read_odometer(self):
        """打印一條指出汽車歷程額消息"""
        print("This car has " + str(self.odometer_reading) + "miles on it.")

    def update_odometer(self,mileage):
        """將里程表讀數設置為指定的值"""
        self.odometer_reading = mileage

    def reset_odometer_reading(self):
        self.odometer_reading = 0

    def add_odometer_reading(self):
        '''里程加1'''
        self.odometer_reading += 1




class ElectricCar(car):
    """docstring for ElectricCar"""
    def __init__(self, make, modle, year):
        '''初始化父類的屬性'''
        super().__init__(make, modle, year)

my_tesla = ElectricCar("tesla", "modles", 2017)
print(my_tesla.get_descriptive_name())
my_tesla.read_odometer()

>>>2017 Tesla Modles
This car has 0miles on it.

首先是Car類的代碼。創建子類時,父類必須包含在當前文件中,且位於子類前面。我們定義了子類ElectricCar。定義子類時,必須在括號內指定父類的名稱。方法__init__()接受創建Car實例所需的信息。

super()是一個特殊函數,幫助Python將父類和子類關聯起來。這行代碼讓Python調用ElectricCar的父類的方法__init__(),讓ElectricCar實例包含父類的所有屬性。父類也稱為超類(superclass),名稱super因此而得名。
為測試繼承是否能夠正確地發揮作用,我們嘗試創建一輛電動汽車,但提供的信息與創建普通汽車時相同。我們創建ElectricCar類的一個實例,並將其存儲在變量my_tesla中。這行代碼調用ElectricCar類中定義的方法__init__(),后者讓Python調用父類Car中定義的方法__init__()。我們提供了實參'tesla'、'model s'和2017。

除方法__init__()外,電動汽車沒有其他特有的屬性和方法。

2.Python 2.7 中的繼承

在Python 2.7中,繼承語法稍有不同,ElectricCar類的定義類似於下面這樣:

class Car(object):
  def __init__(self, make, model, year):
    --snip--


class ElectricCar(Car):   def __init__(self, make, model, year):     super(ElectricCar, self).__init__(make, model, year)     --snip--

函數super()需要兩個實參:子類名和對象self。為幫助Python將父類和子類關聯起來,這些實參必不可少。另外,在Python 2.7中使用繼承時,務必在定義父類時在括號內指定object。

3.給子類定義屬性和方法

讓一個類繼承另一個類后,可添加區分子類和父類所需的新屬性和方法。
下面來添加一個電動汽車特有的屬性(電瓶),以及一個描述該屬性的方法。我們將存儲電瓶容量,並編寫一個打印電瓶描述的方法:

class car():
    --snip--



class ElectricCar(car):
    """docstring for ElectricCar"""
    def __init__(self, make, modle, year):
        '''初始化父類的屬性'''
        super().__init__(make, modle, year)
        self.battery_size = 70

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

my_tesla = ElectricCar("tesla", "modles", 2017)
print(my_tesla.get_descriptive_name())
my_tesla.read_odometer()
my_tesla.describe_battery()

>>>2017 Tesla Modles
This car has 0miles on it.
This car has a 70-kwh battery.

我們添加了新屬性self.battery_size,並設置其初始值(如70)。根據ElectricCar 類創建的所有實例都將包含這個屬性,但所有Car實例都不包含它。我們還添加了一個名為describe_battery()的方法,它打印有關電瓶的信息。我們調用這個方法時,將看到一條電動汽車特有的描述。

對於ElectricCar類的特殊化程度沒有任何限制。模擬電動汽車時,你可以根據所需的准確程度添加任意數量的屬性和方法。如果一個屬性或方法是任何汽車都有的,而不是電動汽車特有的,就應將其加入到Car類而不是ElectricCar類中。這樣,使用Car類的人將獲得相應的功能,而ElectricCar類只包含處理電動汽車特有屬性和行為的代碼。

4.重寫父類的方法

對於父類的方法,只要它不符合子類模擬的實物的行為,都可對其進行重寫。為此,可在子類中定義一個這樣的方法,即它與要重寫的父類方法同名。這樣,Python將不會考慮這個父類方法,而只關注你在子類中定義的相應方法。
假設Car類有一個名為fill_gas_tank()的方法,它對全電動汽車來說毫無意義,因此你可能想重寫它。下面演示了一種重寫方式:

def ElectricCar(Car):
    --snip--
    def fill_gas_tank():
        """電動汽車沒有油箱"""
        print("This car doesn't need a gas tank!")    

現在,如果有人對電動汽車調用方法fill_gas_tank(), Python將忽略Car類中的方法fill_gas_tank(),轉而運行上述代碼。使用繼承時,可讓子類保留從父類那里繼承而來的精華,並剔除不需要的糟粕。

5.將實例用作屬性

使用代碼模擬實物時,你可能會發現自己給類添加的細節越來越多:屬性和方法清單以及文件都越來越長。在這種情況下,可能需要將類的一部分作為一個獨立的類提取出來。你可以將大型類拆分成多個協同工作的小類。
例如,不斷給ElectricCar類添加細節時,我們可能會發現其中包含很多專門針對汽車電瓶的屬性和方法。在這種情況下,我們可將這些屬性和方法提取出來,放到另一個名為Battery的類中,並將一個Battery實例用作ElectricCar類的一個屬性:

class Car():                   #1
    --snip--

class Battery():                     #2
    """一次模擬電動汽車電瓶的簡單嘗試"""
    def __init__(self, battery_size=70):
        """初始化電瓶的屬性"""
        self.battery_size = battery_size
    def describe_battery(self):                  #3
        """打印一條描述電瓶容量的消息"""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")
class ElectricCar(Car):
    """電動汽車的獨特之處"""
    def __init__(self, make, model, year):
        """
        初始化父類的屬性,再初始化電動汽車特有的屬性
        """
        super().__init__(make, model, year)  
        self.battery = Battery()          #4

my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()

在1處,我們定義了一個名為Battery的新類,它沒有繼承任何類。2處的方法__init__()除self外,還有另一個形參battery_size。這個形參是可選的:如果沒有給它提供值,電瓶容量將被設置為70。方法describe_battery()也移到了這個類中(見3)。
在ElectricCar類中,我們添加了一個名為self.battery的屬性(見4)。這行代碼讓Python創建一個新的Battery實例(由於沒有指定尺寸,因此為默認值70),並將該實例存儲在屬性self.battery中。每當方法__init__()被調用時,都將執行該操作;因此現在每個ElectricCar實例都包含一個自動創建的Battery實例。
我們創建一輛電動汽車,並將其存儲在變量my_tesla中。要描述電瓶時,需要使用電動汽車的屬性battery:

my_tesla.battery.describe_battery()

這行代碼讓Python在實例my_tesla中查找屬性battery,並對存儲在該屬性中的Battery實例調用方法describe_battery().

這看似做了很多額外的工作,但現在我們想多詳細地描述電瓶都可以,且不會導致ElectricCar類混亂不堪。下面再給Battery類添加一個方法,它根據電瓶容量報告汽車的續航里程:

class Car():
    --snip--
class Battery():
    --snip--
    def get_range(self):
    """打印一條消息,指出電瓶的續航里程"""
        if self.battery_size == 70:
            range = 240
        elif self.battery_size == 85:
            range = 270
        message = "This car can go approximately " + str(range)
        message += " miles on a full charge."
        print(message)
class ElectricCar(Car):
    --snip--

my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()


>>>2016 Tesla Model S
This car has a 70-kWh battery.
This car can go approximately 240 miles on a full charge.

處新增的方法get_range()做了一些簡單的分析:如果電瓶的容量為70kWh,它就將續航里程設置為240英里;如果容量為85kWh,就將續航里程設置為270英里,然后報告這個值。為使用
這個方法,我們也通過汽車的屬性battery來調用它.

 

6.模擬實物

模擬較復雜的物件(如電動汽車)時,需要解決一些有趣的問題。續航里程是電瓶的屬性還是汽車的屬性呢?如果我們只需描述一輛汽車,那么將方法get_range()放在Battery類中也許是合適的;但如果要描述一家汽車制造商的整個產品線,也許應該將方法get_range()移到ElectricCar類中。在這種情況下,get_range()依然根據電瓶容量來確定續航里程,但報告的是一款汽車的續航里程。我們也可以這樣做:將方法get_range()還留在Battery類中,但向它傳遞一個參數,如car_model;在這種情況下,方法get_range()將根據電瓶容量和汽車型號報告續航里程。
這讓你進入了程序員的另一個境界:解決上述問題時,你從較高的邏輯層面(而不是語法層面)考慮;你考慮的不是Python,而是如何使用代碼來表示實物。到達這種境界后,你經常會發現,現實世界的建模方法並沒有對錯之分。有些方法的效率更高,但要找出效率最高的表示法,需要經過一定的實踐。只要代碼像你希望的那樣運行,就說明你做得很好!即便你發現自己不得不多次嘗試使用不同的方法來重寫類,也不必氣餒;要編寫出高效、准確的代碼,都得經過這樣的過程。

 

順便提一下


 

如果你也喜歡Python 這里有一群Python愛好者匯集在此。

關注微信公眾號:【軟件測試技術】,回復 888,獲取QQ群號。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM