蟒蛇书学习笔记——Chapter 09 Section 03 继承【第九章完结 3/3】

9.3 继承

  编写类时,并非总是要从空白开始。如果要编写的类是另一个现成类的特殊版本,可使用继承
  一个类继承另一个类时,将自动获得另一个类的所有属性和方法
  原有的类称为父类,而新类称为子类。子类继承了父类的所有属性和方法,同时还可以定义自己的属性和方法

9.3.1 子类的方法__init__():

  在既有类的基础上编写新类时,通常要调用父类的方法__init__()。这将初始化在父类__init__()方法中定义的所有属性,从而让子类包含这些属性

  例如,下面来模拟电动汽车。

  电动汽车是一种特殊的汽车,因此可在前面创建的Car类的基础上创建新类ElectricCar。这样就只需为电动汽车特有的属性和行为编写代码。
  下面来创建ElectricCar 类的一个简单版本,它具备Car类的所有功能:

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 = f"{self.year} {self.make} {self.model}"
        return long_name.title()
    
    def read_odometer(self):
        print(f"This car has {self.odometer_reading} miles on it.")
        
    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")
            
    def increment_odometer(self, miles):
        self.odometer_reading += miles
        
class ElectricCar(Car):
    """电动车的特殊之处"""
    
    def __init__(self, make, model, year):
        """初始化父类属性"""
        super().__init__(make, model, year)
        
my_tesla = ElectricCar('tesla','model s', 2019)
print(my_tesla.get_descriptive_name())

output:

2019 Tesla Model S

  创建子类时,父类必须包含在当前文件中,且位于子类前面。在26行,定义了子类ElectricCar。
  定义子类时,必须在圆括号内指定父类的名称。方法__init__()接受创建Car实例所需的信息。
  第31行的super()是一个特殊函数,让你能够调用父类的方法。

  这行代码让Python调用Car类的方法__init__(),让ElectricCar实例包含这个方法中定义的所有属性

  父类也称为超类(superclass),名称super由此而来。

  第33行,创建ElectricCar类的一个实例,并将其赋给变量my_tesla。

  这行代码调用ElectricCar类中定义的方法__init__(),后者让Python调用父类Car 中定义的方法__init__()。
  我们提供了实参'tesla'、'model s'和2019。
  ElectricCar实例的行为与Car实例一样,现在可以开始定义电动汽车特有的属性和方法了。

9.3.2 子类的方法__init__():

  让一个类继承另一个类后,就可以添加区分子类和父类所需的新属性和新方法了
  下面来添加一个电动汽车特有的属性(电瓶),以及一个描述该属性的方法。

  我们将存储电瓶容量,并编写一个打印电瓶描述的方法:

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 = f"{self.year} {self.make} {self.model}"
        return long_name.title()
    
    def read_odometer(self):
        print(f"This car has {self.odometer_reading} miles on it.")
        
    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")
            
    def increment_odometer(self, miles):
        self.odometer_reading += miles
        
class ElectricCar(Car):
    """电动车的特殊之处"""
    
    def __init__(self, make, model, year):
        """
        初始化父类属性。
        再初始化电动汽车特有的属性。
        """
        super().__init__(make, model, year)
        self.battery_size = 75
        
    def describe_battery(self):
        """打印一条描述电瓶容量的消息"""
        print(f"This car has a {self.battery_size}-kWh battery.")
        
my_tesla = ElectricCar('tesla','model s', 2019)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()

output:

2019 Tesla Model S
This car has a 75-kWh battery.

  第35行,添加了新属性self.battery_size,并设置其初始值(75)。
  根据ElectricCar类创建的所有实例都将包含该属性,但所有Car实例都不包含它
  还添加了一个名为describe_battery()的方法,打印有关电瓶的信息。调用这个方法时,将看到一条电动汽车特有的描述。
思考:
如果一个属性或方法是任何汽车都有的,而不是电动汽车特有的。那么我们在编写ElectricCar子类时就希望在继承Car类的同时,不继承那个属性。

9.3.3 重写父类的方法:

  对于父类的方法,只要它不符合子类模拟的实物的行为,都可以进行重写。为此,可在子类中定义一个与要重写的父类方法同名的方法。
  这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。

  假设Car类有一个名为fill_gas_tank()的方法,它对全电动汽车来说毫无意义,因此你可能想重写它。下面演示了一种重写方式:

class Car:
    """模拟汽车"""
    
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0
        self.gas = 20
        
    def get_descriptive_name(self):
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()
    
    def read_odometer(self):
        print(f"This car has {self.odometer_reading} miles on it.")
        
    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")
            
    def increment_odometer(self, miles):
        self.odometer_reading += miles
        
    def fill_gas_tank(self, gas):
        self.gas += gas
        
        
class ElectricCar(Car):
    """电动车的特殊之处"""
    
    def __init__(self, make, model, year):
        """
        初始化父类属性。
        再初始化电动汽车特有的属性。
        """
        super().__init__(make, model, year)
        self.battery_size = 75
        
    def describe_battery(self):
        """打印一条描述电瓶容量的消息"""
        print(f"This car has a {self.battery_size}-kWh battery.")
        
    def fill_gas_tank(self):
        """电动车没有油箱"""
        print("This car doesn't need a gas tank!")
        
my_test_elec_car = ElectricCar('BYD','KKX', 2020)
my_test_elec_car.fill_gas_tank()

output:

This car doesn't need a gas tank!

9.3.4 将实例用作属性:

  使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多(属性和方法清单以及文件都越来越长)。
  在这种情况下,可能需要将类的一部分提取出来,作为一个独立的类。可以将大型类拆分成多个协同工作的小类。

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 = f"{self.year} {self.make} {self.model}"
        return long_name.title()
    
    def read_odometer(self):
        print(f"This car has {self.odometer_reading} miles on it.")
        
    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")
            
    def increment_odometer(self, miles):
        self.odometer_reading += miles
        
class Battery:
    """一次模拟电动车电瓶的简单尝试"""
    
    def __init__(self, battery_size=75):
        """初始化电瓶属性"""
        self.battery_size = battery_size
        
    def describe_battery(self):
        """打印一条描述电瓶容量的信息"""
        print(f"This car has a {self.battery_size}-kWh battery.")
        
class ElectricCar(Car):
    """电动车的独特之处"""
    
    def __init__(self, make ,model, year):
        """
        初始化父类的属性。
        再初始化电动汽车特有的属性。
        """
        super().__init__(make, model, year)
        self.battery = Battery()
        
my_telsa = ElectricCar('tesla', 'model s', 2019)

print(my_tesla.get_descriptive_name())
my_telsa.battery.describe_battery()

output:

2019 Tesla Model S
This car has a 75-kWh battery.

  第26行定义一个名为Battery的新类,它没有继承任何类。第29行定义的方
法__init__()除self外,还有另一个形参battery_size。这个形参是可选的:如果没有给它提供值,电瓶容量将被设置为75。方法describe_battery()也移到了这个类中。

  在ElectricCar类中,添加了一个名为self.battery的属性(见46行)。

  这行代码让Python创建一个新的Battery实例(因为没有指定容量,所以为默认值75),并将该实例赋给属性self.battery。每当方法__init__()被调用时,都将执行该操作,因此现在每个ElectricCar实例都包含一个自动创建的Battery实例。

my_telsa.battery.describe_battery()

  这行代码让Python在实例my_tesla中查找属性battery,并对存储在该属性中的Battery实例调用方法describe_battery()。

posted @ 2022-02-19 14:30  姜子牙会更好  阅读(50)  评论(0编辑  收藏  举报