第九章(二)
2、使用类和实例
修改类的属性,可以直接修改实例的属性,也可以编写方法以特定的方式进行修改。
(1)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())
# 输出:
2016 Audi A4
方法init()接受形参的值,并将它们存储在根据这个类创建的实例的属性中。创建新的Car实例时,我们需要指定其制造商、型号和生产年份。为了在方法get_descriptive_name()中访问属性的值,需要使用(self.属性名)。
(2)给属性指定默认值
类中的每个属性都必须有初始值,哪怕这个值是0或空字符串。在有些情况下,如设置默认值时,在方法init()内指定这种初始值是可行的;如果你对某个属性这样做了,就无需包含为它提供初始值的形参。
class Car():
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0 # 1
def get_descriptive_name(self):
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title()
def read_odometer(self): # 2
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() # 3
上述代码中的#号处是新增上去的,在第一个#号处Python将创建一个名为odometer_reading的属性,并将其初始值设置为0。在第二个#号处定义一个方法,用于打印汽车的里程数,并在第三个#号处调用该方法输出打印结果。
(3)修改属性的值
可以以三种不同的方式修改属性的值:直接通过实例进行修改;通过方法进行设置;通过方法进行递增(增加特定的值)。
- 直接修改属性的值
要修改属性的值,最简单的方式就是通过实例直接访问它。
my_new_car.odometer_reading = 23
# 输出:
2016 Audi A4
This car has 23 miles on it.
在调用read_odometer()方法前添加上面的语句,此语句通过句点表示法来直接访问并设置汽车的属性,这让该属性的值重新设置为23。
- 通过方法修改属性的值
若有一个修改属性的方法,就无需直接访问属性,而可将值传递给一个方法,有它在内部进行更新。
def update_odometer(self, mileage):
self.odometer_reading = mileage
my_new_car.update_odometer(23)
# 输出:
2016 Audi A4
This car has 23 miles on it.
在类中添加上述的方法,并在调用read_odometer()方法之前调用该方法对属性进行修改。最后的输出结果与上面直接访问属性的结果是一样的。
还可以对update_odometer()进行扩展,比如禁止任何人将里程数往回调:
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()方法进行修改。如果新指定的里程大于或等于原来的里程,就将里程表读数改为新指定的里程;否则就发出警告,指出不能将里程数往回调。
- 通过方法对属性的值进行递增
有时候需要将属性值递增特定的量,而不是将其设置为全新的值。
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()
# 输出:
2013 Subaru Outback
This car has 23500 miles on it.
This car has 23600 miles on it.
在类中新增一个方法increment_odometer(),用于将里程数递增一定的量。并将使用类和实例部分全部修改为第二部分的代码。从最后的输出结果可以看出,确实实现了增加100的效果。
3、继承
编写类时,类后的括号并非总是空的。当你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。
(1)子类的方法init()
创建子类的实例时,Python首先需要完成的任务是给父类的所有属性赋值。
下面模拟电动汽车,电动汽车是一中特殊的汽车,所以在Car类的基础上创建新类ElectricCar,这时只需要给电动汽车编写它独有的属性和方法。
class ElectriCar(Car):
def __init__(self, make, model, year):
super().__init__(make, model, year)
my_tesla = ElectriCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
# 输出:
2016 Tesla Model S
创建子类时,父类必须包含在当前文件中,且位于子类前面。定义子类时,必须在括号内指定父类的名称。方法init()接受创建Car实例所需的信息。super()是一个特殊的函数,帮助Python将父类和子类关联起来,让Python调用父类的方法,并让其包含父类的所有属性。父类也称为超类(superclass),名称super因此而得名。在这里,我们让子类调用父类的方法,由输出结果可以知道这样的继承是可行的。
(2)Python 2.7中的继承
在Python 2.7中,创建一个类时,括号内要写入object。函数super()需要两个实参:子类名和对象self,为帮助Python将父类与子类关联起来,这些实参必不可少。
(3)给子类定义属性和方法
让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法。下面添加一个电动汽车特有的属性(电瓶),以及一个描述该属性的方法。
self.battery_size = 70
def describe_battery(self):
print("This car has a " + str(self.battery_size) + "-kWh battery.")
my_tesla.describe_battery()
# 输出:
2016 Tesla Model S
This car has a 70-kWh battery.
在init()方法中添加了新属性并设置初始值为70。根据ElectricCar类创建的所有实例都将包含这个属性,而所有Car实例都不包含它。在这里还添加了一个方法用于打印有关电瓶的信息。
可根据需要的准确程度在子类中添加任意数量的属性和方法。如果一个属性或方法是任何汽车都有的,而不是电动汽车特有的,就应该将其加入父类而不是子类中。这样,使用父类的人将获得相应的功能,而子类只包含特有的属性和方法。
(4)重写父类的方法
对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。在子类中重写的方法必须与父类的方法同名。这样Python会忽略父类的方法,而执行子类中的方法。假设父类中有一个油箱的方法,但对电动汽车来说没有意义,因此可以对它进行重写。
def fill_gas_tank():
print("This car doesn't need a gas tank!")
使用继承时,可让子类保留从父类那里继承而来的精华,并剔除不需要的糟粕。
(5)将实例用作属性
当给类添加的细节越来越多,属性和方法清单以及文件越来越长,就需要将类的一部分作为一个独立的类提取出来,可以将大型类拆分成多个协同工作的小类。如将关于电瓶的属性和方法提取出来放到一个名为Battery的类中,并将一个Battery实例用作ElectricCar类的一个属性:
class Battery():
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.")
self.battery = Battery()
my_tesla.battery.describe_battery()
# 输出:
2016 Tesla Model S
This car has a 70-kWh battery.
第一部分为定义的一个Battery新类,它没有继承任何类,在init()方法中给电瓶容量设置了初始值为70,这个形参是可选的。原来在电动汽车类中的方法也移到了这个类中。在ElectricCar类中添加了一个self.battery属性,每当方法init()被调用时,都将创建一个Battery实例,因此每个ElectricCar实例都包含一个自动创建的Battery实例。最后调用Battery类中的方法时,让Python在实例my_tesla中查找属性battery,并对存储在该属性中的Battery实例调用方法describe_battery()。
(6)模拟实物
当编写的程序能够很好的模拟实物时,就可以帮组你解决很多生活中的问题,可以收到意想不到的效果。