1. 创建和使用类
1.1 创建Dog类
下面将创建一个表示小狗的Dog类,根据Dog类创建的每个示例都将存储名字和年龄,并且赋予每条小狗蹲下(sit()
)和打滚(roll_over()
)的能力:
dog.py
class Dog():
""" 创建一个表示小狗的类 """
def __init__(self, name, age):
""" 初始化属性name和age """
self.name = name
self.age = age
def sit(self):
""" 模拟小狗被命令时蹲下 """
print(self.name.title() + " is now sitting.")
def roll_over(self):
""" 模拟小狗被命令时打滚 """
print(self.name.title() + " rolled over!")
在上面的代码中,定义了一个名为Dog的类。根据约定,在Python中,首字母大写的名称指的是类。这个类定义中的括号是空的,说明该类并没有需要继承的父类。
1.1.1 方法__init__()
类中的函数称为方法,而__init__()
是一个特殊的方法,每当你根据Dog类创建新示例时,Python都会自动运行它,类似于C++类中的构造函数,在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。
我们将方法__init__()
定义成了包含三个实参:self、name和age。在这个方法的定义中,形参self是必不可少的,还必须位于其他形参的前面,每个与类相关联的方法调用都自动传递实参self,它是一个指向示例本身的引用,让实例能够访问类中的属性和方法。我们将通过实参向Dog()传递名字和年龄,self
会自动传递,因此我们不需要传递它,只需要给最后两个形参(name
和age
)提供值。
在类中,以self
为前缀的变量都可供类中的所有方法使用,我们还可以通过类的实例来访问这些变量。self.name = name
获取存储在形参name
中的值,并将其存储到变量name
中,然后该变量被关联到当前创建的实例。self.age = age
的作用相同。像这样在类中可通过实例访问的变量称为属性。
1.2 根据类创建实例
可将类视为有关如何创建实例的说明,Dog
类是一系列说明,让Python知道如何创建表示特定小狗的实例。下面来创建一个表示特定小狗的实例:
class Dog():
--snip--
my_dog = Dog('willie', 6)
print("My dog's name is " + my_dog.name.title() + ".")
print("My dog is " + str(my_dog.age) + " years old.")
在上面这段代码中,创建了一个名字为’willie’、年龄为6的小狗。遇到这行代码时,Python将实参'willie'
和6
传入Dog类中的方法__init__()
。方法__init__()
创建一个便是特定小狗的实例,并使用实参的值来设置属性name
和age
。方法__init__()
并未显式地包含return语句,但Python自动返回一个表示这条小狗的实例,并将这个实例存储在变量my_dog
中。在Python中,我们通常可以认为首字母大写的名称(如Dog)指的是类,而小写的名称(如my_dog)指的是根据类创建的实例。
1.2.1 访问属性
要访问实例的属性,可使用句点表示法:
my_dog.name
在这里,Python先找到实例my_dog
,再查找与这个实例相关联的属性name
。在Dog
类中引用这个属性时,使用的是self.name
,因此,在上面的实例化代码中,打印出的结果如下所示:
My dog's name is Willie.
My dog is 6 years old.
1.2.2 调用方法
根据Dog
类创建实例后,就可以使用句点表示法来调用Dog
类中定义的任何方法:
class Dog():
--snip--
my_dog = Dog('willie', 6)
my_dog.sit()
my_dog.roll_over()
要调用方法,可指定实例的名称和要调用的方法,并用句点分隔它们。上面的代码中运行的输出结果如下所示:
Willie is now sitting.
Willie rolled over!
2. 使用类和实例
在类编写好后,你的大部分时间都将花在使用根据类创建的实例上,你需要执行的一个重要任务是修改实例的属性。你可以直接修改实例的属性,也可以编写方法以特定的方式进行修改。
2.1 Car类
下面是一个表示汽车的类,它存储了有关汽车的信息,还有一个汇总这些信息的方法:
car.py
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
2.2 给属性指定默认值
类中的每个属性都必须有初始值,哪怕这个值是0或是空字符。在有些情况下,如设置默认值时,在方法__init__()
内指定这种初始值是可行的,如如果你对某个属性这样做了,就无需包含为它提供初始值的形参。
下面来添加一个名为odometer_reading
的属性,其初始值总是为0。除此之外,还添加了一个名为read_odometer()
的方法,用于读取汽车的里程表:
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 = 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.")
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()
上面这段代码打印的结果如下,由于未对汽车里程进行修改,因此汽车的里程还是0:
2016 Audi A4
This car has 0 miles on it.
2.3 修改属性的值
在Python中,有如下两种不同的方式可以修改属性的值:直接通过实例进行修改;通过方法进行设置。下面依次介绍这些方法。
2.3.1 直接修改属性的值
要修改属性的值,最简单的方式是通过实例直接访问它。下面的代码直接将里程表度数设置为23:
class Car():
--snip--
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
此时,my_new_car
这个实例中的odometer_reading
属性就被修改为23:
2016 Audi A4
This car has 23 miles on it.
2.3.2 通过方法修改属性的值
下面的例子演示了一个名为update_odometer()
的方法:
class Car():
--snip--
def update_odometer(self, mileage):
""" 将里程表读书设置为指定的值 """
self.odometer_reading = mileage
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.update_odometer(23)
my_new_car.read_odometer()
上面的代码中添加了一个新的方法update_odometer()
。这个方法接受一个里程值,并将其存储到self.odometer_reading
中。上面的代码打印结果如下:
2016 Audi A4
This car has 23 miles on it.
3. 继承
编写类时,并非总是要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法。原有的类称为父类,而新类称为它的子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。
3.1 子类的方法__init__()
创建子类时的示例时,Python首先需要先给父类的所有属性赋值,为此,子类的方法__init__()
需要依靠父类。
下面的代码创建一个简单的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 = 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):
""" 将里程表读书设置为指定的值 """
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
class ElectricCar(Car):
"""创建一个表示电动汽车的类 """
def __init__(self, make, model, year):
""" 初始化父类的属性 """
super().__init__(make, model, year)
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
创建子类时,父类必须包含在当前文件中,且位于子类前面,在上面的代码中,我们定义了子类ElectricCar
。定义子类时,必须在括号内指定父类的名称。方法__init__()
接受创建Car
示例所需的信息。super()
是一个特殊函数,帮助Python将父类和子类关联起来,这行代码让Python调用ElectricCar
的父类的方法__init__()
,让ElectricCar
实例包含父类的所有属性。父类也称为超类,名称super()
因此而得名。上面这段代码的输出结果为:
2016 Tesla Model S
可以看到继承自Car
类的ElectricCar
类确实具有和其父类Car相同的功能。
3.2 给子类定义属性和方法
让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法,下面的代码添加一个电动车特有的属性(电瓶),以及一个描述该属性的方法:
class Car():
--snip--
class ElectricCar(Car):
""" 创建一个继承自汽车类的电动车类 """
def __init__(self, make, model, year):
""" 电动车的特点,初始化父类的属性,再初始化电动车特有的属性 """
super().__init__(make, model, year)
self.battery_size = 70
def describe_battery(self):
""" 打印一条描述电瓶容量的消息 """
print("This car has a " + str(self.battery_size) + "-kWh battery.")
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
在上面这行代码中,添加了新属性self.battery_size
,并设置初始值为70。根据ElectricCar
类创建的所有实例都将包含这个属性,但所有Car
实例都不包含它。除此之外,还添加了一个describe_battery()
方法,它打印有关电瓶的信息。我们调用这个方法时,将看到一条电动车特有的描述:
2016 Tesla Model S
This car has a 70-kWh battery.
3.3 重写父类方法
对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注你在子类中重新定义的该方法。
假设Car
类有一个名为fill_gas_tank()
的方法,它对全电动车来说毫无意义,下面演示了一种重写方式:
class ElectricCar(Car):
--snip--
def fill_gas_tank():
""" 电动汽车没有邮箱 """
print("This car doesn't need a gas tank!")
现在,如果有人对电动车调用方法fill_gas_tank()
,Python将忽略Car
类中的方法fill_gas_tank()
。转而运行上述代码。使用继承时,可让子类保留从父类哪里继承而来的精华,并剔除不需要的糟粕。
3.4 将实例用作属性
在Python中,可以将大型类拆分成多个协同工作的小类,例如在下面的代码中,将针对汽车电瓶的属性和方法提取出来,放到另一个名为Battery
的类中,并将一个Battery
实例用作ElectricCar
类的一个属性:
class Car():
--snip--
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.")
class ElectricCar(Car):
""" 创建一个关于电动车的类 """
def __init__(self, make, model, year):
""" 初始化父类的属性,再初始化电动车特有的属性 """
super().__init__(make, model, year)
self.battery = Battery()
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
在上面的代码中,定义了一个名为Battery
的新类,在ElectricCar
这个类中,创建了一个名为self.battery
的Battery实例。现在每个ElectricCar实例都包含一个自动创建的Battery实例。输出结果如下所示:
2016 Tesla Model S
This car has a 70-kWh battery.