1. 程式人生 > >03python面向對象編程2

03python面向對象編程2

parent nbsp __init__ spl bsp 消息 roc format opera

3.繼承

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

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

In [1]:
"""A class that can be used to represent a car."""

class Car():
    """A simple attempt to represent a car."""

    def __init__(self, manufacturer, model, year):
        """Initialize attributes to describe a car."""
        self.manufacturer = manufacturer
self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): """Return a neatly formatted descriptive name.""" long_name = str(self.year) + ‘ ‘ + self.manufacturer + ‘ ‘ + self.model return long_name
.title() def read_odometer(self): """Print a statement showing the car‘s mileage.""" print("This car has " + str(self.odometer_reading) + " miles on it.") def update_odometer(self, mileage): """ Set the odometer reading to the given value. Reject the change if it attempts to roll the odometer back. """ if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print("You can‘t roll back an odometer!") def increment_odometer(self, miles): """Add the given amount to the odometer reading.""" self.odometer_reading += miles
In [2]:
class ElectricCar(Car):
    """Models aspects of a car, specific to electric vehicles."""

    def __init__(self, manufacturer, model, year):
        """
        Initialize attributes of the parent class.
        Then initialize attributes specific to an electric car.
        """
        super().__init__(manufacturer, model, year)

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

super() 是一個特殊函數,幫助Python將父類和子類關聯起來。這行代碼讓Python調用 ElectricCar 的父類的方法 init() ,讓 ElectricCar 實例包含父類的所有屬性。父類也稱為超 類(superclass),名稱super因此而得名。

為測試繼承是否能夠正確地發揮作用,我們嘗試創建一輛電動汽車,但提供的信息與創建普通汽車時相同。在?處,我們創建 ElectricCar 類的一個實例,並將其存儲在變量 my_tesla 中。這行代碼調用 ElectricCar 類中定義的方法 init() ,後者讓Python調用父類 Car 中定義的方法 init() 。我們提供了實參 ‘tesla‘ 、 ‘model s‘ 和 2016 。 除方法 init() 外,電動汽車沒有其他特有的屬性和方法。當前,我們只想確認電動汽車具備普通汽車的行為:

In [3]:
my_tesla = ElectricCar(‘tesla‘, ‘model s‘, 2016)
print(my_tesla.get_descriptive_name())
2016 Tesla Model S

3.2 Python2.7中的繼承。
在Python 2.7中,繼承語法稍有不同, ElectricCar 類的定義類似於下面這樣:
函數 super() 需要兩個實參:子類名和對象 self 。為幫助Python將父類和子類關聯起來,這些實參必不可少。另外,在Python 2.7中使用繼承時,務必在定義父類時在括號內指定 object 。

In [4]:
# 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 --

3.3 給子類定義屬性和方法

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

In [5]:
class ElectricCar(Car):
    """Models aspects of a car, specific to electric vehicles."""

    def __init__(self, manufacturer, model, year):
        """
        Initialize attributes of the parent class.
        Then initialize attributes specific to an electric car.
        """
        super().__init__(manufacturer, model, year)
        self.battery_size = 70
        
    def describe_battery(self):
        """打印一條描述電瓶容量的消息"""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")
In [6]:
my_tesla = ElectricCar(‘tesla‘, ‘model s‘, 2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
2016 Tesla Model S
This car has a 70-kWh battery.

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

In [7]:
# class ElectricCar(Car):
#     -- snip --
#     def fill_gas_tank(self):
#         """電動汽車沒有油箱"""
#         print("This car doesn‘t need a gas tank!")

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

3.5 將實例用作屬性
使用代碼模擬實物時,你可能會發現自己給類添加的細節越來越多:屬性和方法清單以及文 件都越來越長。在這種情況下,可能需要將類的一部分作為一個獨立的類提取出來。你可以將大 型類拆分成多個協同工作的小類。

例如,不斷給 ElectricCar 類添加細節時,我們可能會發現其中包含很多專門針對汽車電瓶 的屬性和方法。在這種情況下,我們可將這些屬性和方法提取出來,放到另一個名為 Battery 的 類中,並將一個 Battery 實例用作 ElectricCar 類的一個屬性:

In [10]:
# class Car():
#     -- snip --
class Battery():
    """A simple attempt to model a battery for an electric car."""

    def __init__(self, battery_size=60):
        """Initialize the batteery‘s attributes."""
        self.battery_size = battery_size

    def describe_battery(self):
        """Print a statement describing the battery size."""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")  
In [11]:
class ElectricCar(Car):
    """Models aspects of a car, specific to electric vehicles."""

    def __init__(self, manufacturer, model, year):
        """
        Initialize attributes of the parent class.
        Then initialize attributes specific to an electric car.
        """
        super().__init__(manufacturer, model, year)
        self.battery = Battery()

我們定義了一個名為 Battery 的新類,它沒有繼承任何類。方法 init() 除 self 外,還有另一個形參 battery_size 。這個形參是可選的:如果沒有給它提供值,電瓶容量將 被設置為60。方法 describe_battery() 也移到了這個類中。

在 ElectricCar 類中,我們添加了一個名為 self.battery 的屬性。這行代碼讓Python 創建一個新的 Battery 實例(由於沒有指定尺寸,因此為默認值 60 ),並將該實例存儲在屬性 self.battery 中。每當方法 init() 被調用時,都將執行該操作;因此現在每個 ElectricCar 實 例都包含一個自動創建的 Battery 實例。

In [13]:
my_tesla = ElectricCar(‘tesla‘, ‘model s‘, 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
2016 Tesla Model S
This car has a 60-kWh battery.
In [14]:
class Battery():
    """A simple attempt to model a battery for an electric car."""

    def __init__(self, battery_size=60):
        """Initialize the batteery‘s attributes."""
        self.battery_size = battery_size

    def describe_battery(self):
        """Print a statement describing the battery size."""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")  
        
    def get_range(self):
        """Print a statement about the range this battery provides."""
        if self.battery_size == 60:
            range = 140
        elif self.battery_size == 85:
            range = 185
        else:
            range = 200
        message = "This car can go approximately " + str(range)
        message += " miles on a full charge."
        print(message)
    
In [15]:
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 60-kWh battery.
This car can go approximately 140 miles on a full charge.

新增的方法 get_range()。

3.6 模擬實物
模擬較復雜的物件(如電動汽車)時,需要解決一些有趣的問題。續航裏程是電瓶的屬性還 是汽車的屬性呢?如果我們只需描述一輛汽車,那麽將方法 get_range() 放在 Battery 類中也許是合 適的;但如果要描述一家汽車制造商的整個產品線,也許應該將方法 get_range() 移到 ElectricCar 類中。在這種情況下, get_range() 依然根據電瓶容量來確定續航裏程,但報告的是一款汽車的續 航裏程。我們也可以這樣做:將方法 get_range() 還留在 Battery 類中,但向它傳遞一個參數,如 car_model ;在這種情況下,方法 get_range() 將根據電瓶容量和汽車型號報告續航裏程。

這讓你進入了程序員的另一個境界:解決上述問題時,你從較高的邏輯層面(而不是語法層 面)考慮;你考慮的不是Python,而是如何使用代碼來表示實物。到達這種境界後,你經常會發 現,現實世界的建模方法並沒有對錯之分。有些方法的效率更高,但要找出效率最高的表示法,需要經過一定的實踐。只要代碼像你希望的那樣運行,就說明你做得很好!即便你發現自己不得 不多次嘗試使用不同的方法來重寫類,也不必氣餒;要編寫出高效、準確的代碼,都得經過這樣的過程。

03python面向對象編程2