第 9 章 . 类
- 面向对象编程是最有效的软件编写方法之一。
- 在面向对象编程中,你编写表示现实世界中的事务和情景的类,并基于这些类来创建对象。
- 编写类时,你定义一大类对象都有的通用行为。
- 基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。
- 使用面向对象编程可模拟现实情景。
- 根据类创建对象被称为实例化;
1. 创建类和使用
-
使用类几乎可以模拟任何东西;
-
编写一个表示小狗的简单类
Dog
; -
它表示的不是特定的小狗,而是任何小狗;
-
对于大多数狗,通用的都有名字和年龄,而且还会蹲下和打滚;
-
根据
Dog
类创建的每个实例都将存储名字和年龄,并且赋予每条小狗蹲下(sit()
)和打滚(roll_over()
)的能力;class Dog(): """一次模拟小狗的简单尝试""" # init 前后各两个下划线,具体原因暂时未知 def __init__(self, name, age): """初始化属性 name, age""" self.name = name self.age = age def sit(self): """模拟小狗被命令蹲下""" msg = self.name.title() + " is now sitting." print(msg) def roll_over(self): """模拟小狗被命令打滚""" msg = self.name.title() + " rolled over!" print(msg)
-
根据约定,Python 中,首字母大写的名称指的是类;
-
类定义中的括号是空的,因为我们要从空白创建这个类;
1.1 方法 __init__()
- 类中的函数称为方法;
__init__()
是一个特殊的方法,每当你根据Dog
类创建新实例时,Python 都会自动运行它;- 这个方法名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免 Python 默认方法与普通方法法生名称冲突;
__init__()
包含三个形参:self
、name
和age
;self
必不可少,还必须位于其他形参的前面;- 因为 Python 调用这个
__init__()
方法来创建Dog
实例时,将自动传入实参self
; - 每个与类相关联的方法调用都自动传递实参
self
; - 它是一个指向实例本身的引用,让实例能够访问类中的属性和方法;
- 上述实例中,当我们创建
Dog
实例时,Python 将调用Dog
类的方法__init__()
; - 我们将通过实参向
Dog()
传递名字和年龄; self
会自动传递,因此我们不需要传递它,只需要给name
和age
两个形参提供值;- 第 6 行和第 7 行处定义的变量都有前缀
self
; - 以
self
为前缀的变量都可供类中的所有方法使用; - 可以通过类的任何实例来访问这些变量;
self.name = name
获取存储在形参name
中的值,并将其存储到变量name
中,然后该变量被关联到当前创建的实例;- 像这样可以通过实例访问的变量称为属性。
1.2 自定义的方法
Dog
还定义了另外两个方法:sit()
和roll_over()
;- 由于这些方法不需要额外的信息,所以它们的形参只有一个
self
;
1.3 在 Python 2.7 中创建类
-
在 Python 2.7 中创建类时,只需要在括号内包含单词
object
:class ClassName(object): --snip--
1.4 根据类创建实例
-
可以将类视为如何创建实例的说明;
-
Dog
类是一系列说明,让 Python 知道如何创建表示特定小狗的实例; -
下面来创建一个表示特定小狗的实例;
class Dog(): """一次模拟小狗的简单尝试""" def __init__(self, name, age): """初始化属性 name, age""" self.name = name self.age = age def sit(self): """模拟小狗被命令蹲下""" msg = self.name.title() + " is now sitting." print(msg) def roll_over(self): """模拟小狗被命令打滚""" msg = self.name.title() + " rolled over!" print(msg) my_dog = Dog('rric', 9) my_dog.sit() my_dog.roll_over() msg = "My dog name is " + my_dog.name.title() + "." print(msg)
-
在编写好
Dog
类之后,在 19 行,让 Python 创建一条名为rric
、年龄为 9 岁的狗; -
当遇到这行代码时,Python 使用实参
rric
和 9 调用Dog
类中的方法__init__()
; -
方法
__init__()
创建一个表示特定小狗的示例,并使用我们提供的值来设置属性name
和age
; -
方法
__init__()
并未显式地包含return
语句,但Python
自动返回一个表示这条小狗的实例; -
我们将这个实例存储在变量
my_dog
中;- 特殊强调一点:首字母大写的名称指类(
Dog
),小写的名称指的是根据类创建的实例(my_dog
);
- 特殊强调一点:首字母大写的名称指类(
1. 访问属性
- 使用句点表示法,访问实例的属性;
- 在第 22 行中,
my_dog.name
就是用来访问my_dog
属性name
的值; - 关于上述的操作,Python 是先找到实例
my_dog
,然后再查找与这个实例相关联的属性name
; - 在
Dog
类中引用这个属性时,使用的是self.name
’;
2. 调用方法
- 根据
Dog
类创建实例后,就可以使用句点表示法来调用Dog
类中定义的任何方法; - 在第 20 和第 21 行时,分别调用了蹲下和打滚方法;
3. 创建多个实例
-
可以根据需求创建多个实例:
class Dog(): --snip-- # 实例1 dog1 = Dog('rric', 9) dog1.sit() dog1.roll_over() msg = "My dog name is " + dog1.name.title() + ".\n" print(msg) # 实例2 dog2 = Dog('lili', 3) dog2.sit() dog2.roll_over() msg = "My dog name is " + dog2.name.title() + ".\n" print(msg)
1.5 练习
-
餐馆:
- 创建一个名为 Restaurant 的类,其方法
__init__()
设置两个属性:restaurant_name
和cuisine_type
。 - 创建一个名为
describe_restaurant()
的方法和一个名为open_restaurant()
的方法,其中前者打印前述两项信息,后者打印一条消息,指出餐馆正在营业。 - 根据这个类创建一个名为
restaurant
的实例,分别打印其中两个属性,再调用前述的两个方法;
- 创建一个名为 Restaurant 的类,其方法
-
代码:
class Restaurant(): """一次模拟餐馆的简单尝试""" def __init__(self, restaurant_name, cuisine_type): self.restaurant_name = restaurant_name self.cuisine_type = cuisine_type def describe_restaurant(self): """描述餐馆的信息""" msg = "The name of the restaurant is " + self.restaurant_name.title() + "." print(msg) msg = "The restaurant is a " + self.cuisine_type.title() + " restaurant." print(msg) def open_restaurant(self): msg = "The restaurant is open." print(msg) # 创建实例 restaurant = Restaurant('restaurant_A', 'chinese') # 打印属性 name = restaurant.restaurant_name.title() cuisine = restaurant.cuisine_type.title() msg = name + " is a " + cuisine + " restaurant." print(msg) # 调用方法 restaurant.describe_restaurant() restaurant.open_restaurant()
-
结果:
-
三家餐馆:
- 根据餐馆类,创建三个实例,并对每个实例调用方法
describe_restaurant()
:
- 根据餐馆类,创建三个实例,并对每个实例调用方法
-
代码:
class Restaurant(): """一次模拟餐馆的简单尝试""" def __init__(self, restaurant_name, cuisine_type): self.restaurant_name = restaurant_name self.cuisine_type = cuisine_type def describe_restaurant(self): """描述餐馆的信息""" msg = "\nThe name of the restaurant is " + self.restaurant_name.title() + "." print(msg) msg = "The restaurant is a " + self.cuisine_type.title() + " restaurant." print(msg) def open_restaurant(self): msg = "The restaurant is open." print(msg) # 创建实例 restaurant_a = Restaurant('China', 'chinese') restaurant_b = Restaurant('American', 'American') restaurant_c = Restaurant('Japan', 'Japanese') # 调用方法 restaurant_a.describe_restaurant() restaurant_b.describe_restaurant() restaurant_c.describe_restaurant()
-
结果:
-
用户:
- 创建一个名为
User
的类,其中包含属性first_name
和last_name
,还有用户简介通常会存储的其他几个属性; - 在类
User
中定义一个名为describe_user()
的方法,它打印用户信息摘要; - 再定义一个名为
greet_user()
的方法,它向用户发出个性化的问候; - 创建多个表示不同用户的实例,并对每个实例都调用上述两个方法;
- 创建一个名为
-
代码:
class User(): """一个用户类""" def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name def describe_user(self): full_name = self.first_name + " " + self.last_name msg = "\nThis user is called " + full_name.title() print(msg) def greet_user(self): full_name = self.first_name + " " + self.last_name msg = full_name.title() + ", hello, my friend!" print(msg) user1 = User('li', 'ming') user1.describe_user() user1.greet_user() user2 = User('zhao', 'si') user2.describe_user() user2.greet_user() user3 = User('liu', 'qiang') user3.describe_user() user3.greet_user()
-
结果:
2. 使用类和实例
-
可以使用类来模拟现实世界中的很多情景;
-
类编写好后,大部分时间都花在使用类创建的实例上;
-
这个时候需要做的就是修改实例的属性;
- 可以直接修改;
- 也可以编写方法以特定的方式进行修改;
-
编写一个表示汽车的类,存储了有关汽车的信息:
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) msg = my_new_car.get_descriptive_name() print(msg)
-
这与之前的
Dog
类没有什么区别,唯一不同就是使用了return
将数据进行了返回;
2.1 给属性指定默认值
-
类中的属性都必须有初始值,哪怕这个值是 0 或者是空字符串;
-
在有些情况下,如设置默认值时,在方法
__init__()
内指定这种初始值是可行的; -
如果你对某个属性这样做了,就无需包含为它提供初始值的形参;
-
对
Car
类添加一个名为odometer_reading
的属性,并设置初始值为 0; -
对
Car
类添加一个名为read_odomete()
的方法,用于读取汽车的里程表: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): """打印一条关于里程的信息""" msg = "This car has " + str(self.odometer_reading) + " miles on it." print(msg) my_new_car = Car('audi', 'a4', 2016) msg = my_new_car.get_descriptive_name() print(msg) my_new_car.read_odometer()
-
结果:
2.2 修改属性的值
- 可以通过三种不同的方式来修改属性的值:
- 直接通过实例进行修改;
- 通过方法进行设置;
- 通过方法进行递增(增加特定的值);
1. 直接修改属性的值
-
要修改属性的值,最简单的方法就是通过实例直接访问它:
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): """打印一条关于里程的信息""" msg = "This car has " + str(self.odometer_reading) + " miles on it." print(msg) my_new_car = Car('audi', 'a4', 2016) msg = my_new_car.get_descriptive_name() print(msg) my_new_car.odometer_reading = 30 my_new_car.read_odometer()
-
在第 24 行,我们对汽车的里程进行了修改,结果如下:
2. 通过方法修改属性的值
-
如果有方法可以直接代替你更新属性的方法,将会更便捷;
-
这样就无需访问属性,直接将值传递给方法,由它在内部进行修改;
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): """打印一条关于里程的信息""" msg = "This car has " + str(self.odometer_reading) + " miles on it." print(msg) def update_odometer(self, mileage): """将里程表读数设置为指定的值""" self.odometer_reading = mileage my_new_car = Car('audi', 'a4', 2016) msg = my_new_car.get_descriptive_name() print(msg) my_new_car.update_odometer(30) my_new_car.read_odometer()
-
对
Car
类进行了添加里程表读数设置方法,第 21~23 行,并在第 28 行进行调用该方法:
3. 通过方法堆属性的值进行递增
-
有时候需要将属性值递增特定的量,而不是将其设置为全新的值;
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): """打印一条关于里程的信息""" msg = "This car has " + str(self.odometer_reading) + " miles on it." print(msg) def update_odometer(self, mileage): """将里程表读数设置为指定的值""" self.odometer_reading = mileage def increment_odometer(self, miles): """里程表读数增加指定的量""" self.odometer_reading += miles my_new_car = Car('audi', 'a4', 2016) msg = my_new_car.get_descriptive_name() print(msg) my_new_car.update_odometer(30) my_new_car.read_odometer() my_new_car.increment_odometer(50) my_new_car.read_odometer()
-
对
Car
类进行了添加里程表读数增加指定的量方法,第 25~27 行,并在第 36 行进行调用:
2.3 练习
-
就餐人数:
- 在餐馆的代码中,添加一个名为
number_served
的属性,将其值设置为 0; - 根据这个类创建一个名为
restaurant
的实例; - 打印有多少人在这家餐馆就餐过,然后修改这个值并在此打印;
- 添加一个名为
set_number_served()
的方法,它让你能够设置就餐人数,调用这个方法并向他传递一个值,然后再次打印这个值; - 添加一个名为
increment_number_served()
的方法,它让你能够设置就餐人数。调用这个方法并向它传递这样一个值:你认为这家餐馆每天可能接待的就餐人数;
- 在餐馆的代码中,添加一个名为
-
代码:
class Restaurant(): def __init__(self, restaurant_name, cuisine_type): self.restaurant_name = restaurant_name self.cuisine_type = cuisine_type self.number_served = 0 def describe_restaurant(self): msg = "The name of the restaurant is " + self.restaurant_name.title() + "." print(msg) msg = "The restaurant is a " + self.cuisine_type.title() + " restaurant." print(msg) def open_restaurant(self): msg = "The restaurant is open." print(msg) def set_number_served(self, number_served): self.number_served = number_served def increment_number_served(self, increase): self.number_served += increase restaurant = Restaurant('restaurant_A', 'chinese') msg = "The number of people dining at this restaurant is " + str(restaurant.number_served) + "." print(msg) restaurant.set_number_served(20) msg = "The number of people dining at this restaurant is " + str(restaurant.number_served) + "." print(msg) restaurant.increment_number_served(10) msg = "The number of people dining at this restaurant is " + str(restaurant.number_served) + "." print(msg)
-
结果:
-
尝试登录次数:
- 在用户代码中,添加一个名为
login_attempts
的属性; - 编写一个名为
increment_login_attempts()
的方法,它将属性login_attempts
的值加1; - 在编写一个名为
reset_login_attempts()
的方法,它将属性login_attempts
的值重置为0; - 根据
User
类创建一个实例,再调用方法increment_login_attempts()
多次; - 打印属性
login_attempts
的值,确认它被正确地递增; - 然后,调用方法
reset_login_attempts()
,并再次打印属性login_attempts
的值,确认它被重置为0。
- 在用户代码中,添加一个名为
-
代码:
class User(): def __init__(self, first_name, last_name, age, sex): self.first_name = first_name self.last_name = last_name self.age = age self.sex = sex self.login_attempts = 1 def describe_user(self): full_name = self.first_name + " " + self.last_name msg = "\nThis user is called " + full_name.title() + ", " if self.sex == 'boy': msg += "he is a " + self.sex + ", and he is " + str(self.age) + " years old." elif self.sex == 'girl': msg += "she is a " + self.sex + ", and she is " + str(self.age) + " years old." else: msg = "\nInformation are wrong! This user is called " + full_name.title() + "." print(msg) def greet_user(self): full_name = self.first_name + " " + self.last_name msg = "Hello, " + full_name.title() + ", my friend, I miss you so much!" print(msg) def increment_login_attempts(self): self.login_attempts += 1 def reset_login_attempts(self): self.login_attempts = 0 user1 = User('li', 'mo', 20, 'boy') user1.describe_user() user1.greet_user() msg = "Before : This user has been login attempts " + str(user1.login_attempts) print(msg) i = 0 while i < 5: user1.increment_login_attempts() i += 1 msg = "After : This user has been login attempts " + str(user1.login_attempts) print(msg) user1.reset_login_attempts() msg = "Reset this user login attempts " + str(user1.login_attempts) print(msg) user2 = User('li', 'hong', 22, 'girl') user2.describe_user() user2.greet_user() user3 = User('li', 'yun', 24, 'non') user3.describe_user() user3.greet_user()
-
结果:
3. 继承
- 编写类时,并非总要从空白开始;
- 如果需要编写的类是另一个现成类的特殊版本,可以使用继承;
- 一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;
- 原有的类称为父类;
- 新类称为子类;
- 子类继承父类的所有属性和方法,同时还可以定义自己的属性和方法;
3.1 子类的方法 __init__()
-
创建子类的实例时,Python 首先要完成的任务是给父类的所有属性赋值;
-
此时,需要父类的
__init__()
方法施以援手; -
此例中模拟电动汽车;电动汽车是一种特殊的汽车,因此我们可以在前面创建的
Car
类的基础上创建新类ElectricCar
,这样就只需要对电动汽车特殊的属性和方法编写代码就可以;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): """打印一条关于里程的信息""" msg = "This car has " + str(self.odometer_reading) + " miles on it." print(msg) def update_odometer(self, mileage): """将里程表读数设置为指定的值""" self.odometer_reading = mileage 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', 2016) msg = my_tesla.get_descriptive_name() print(msg)
- 首先是
Car
类代码;创建子类时,父类必须包含在当前文件中,且位于子类前面; - 定义子类
ElectricCar
时,必须在括号内指定父类的名称,第 28 行; - 方法
__init__()
接受创建Car
实例所需要的信息,第 31 行; super()
是一个特殊的函数,将父类和子类关联起来,第 32 行;- 这行代码让 Python 调用
ElectricCar
父类的方法__nit__()
,让ElectricCar
包含父类所有的属性; - 父类也成为超类(superclass);
- 首先是
-
结果:
2016 Tesla Model S
3.2 Python 2 中的继承
-
在 Python 2 中,继承语法稍有不同;
-
ElectricCar
定义时,函数super()
需要两个实参:子类名和对象self
;
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)
3.3 给子类定义属性和方法
-
让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法;
-
下面添加一个电动汽车特有的属性(电瓶),以及一个描述该方法的属性;
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): """描述电瓶容量的消息""" msg = "This car has a " + str(self.battery_size) + "-kwh battery." print(msg) my_tesla = ElectricCar('tesla', 'model s', 2016) msg = my_tesla.get_descriptive_name() print(msg) my_tesla.battery.describe_battery()
- 在第 10 行,添加
ElectricCar
类的新属性,并设置初始值,并且Car
类实例都不包含它; - 在第 12 行,添加一个描述电瓶信息的方法;
- 在第 10 行,添加
-
结果:
2016 Tesla Model S This car has a 70-kwh battery.
3.4 重写父类的方法
-
对于父类的方法,只要它不符合子类模拟的事物的行为,都可以对其进行重写;
-
可以在子类中定义一个这样的方法,即它与要重写的父类方法同名;
-
这样,Python 不会考虑这个父类的方法,而只关注你在子类中定义的相应方法;
class Car(): """一次模拟汽车的简单尝试""" def __init__(self, make, model, year): """初始化描述汽车属性""" self.make = make self.model = model self.year = year self.odometer_reading = 0 def read_odometer(self): """打印一条关于里程的信息""" msg = "This car has " + str(self.odometer_reading) + " miles on it." print(msg) class ElectricCar(Car): """电动汽车的独到之处""" def __init__(self, make, model, year): super().__init__(make, model, year) def read_odometer(self): msg = "重写父类方法时,Python 将不会考虑父类的方法,只关注子类中定义的相应方法" print(msg) my_tesla = ElectricCar('tesla', 'model s', 2016) my_tesla.read_odometer()
- 第 22 行,对父类的第 11 行,描述里程方法进行重写;
- 经过打印后,之后出现子类的方法;
-
结果:
3.5 将实例用作属性
-
使用代码模拟实物时,给类添加的细节只会越来越多;
-
属性和方法清单以及文件都越来越长;
-
在这种情况下,可以将类的一部分作为一个独立的类提取出来;
-
即将一个大类可以拆分成多个协同工作的小类;
-
例如,在不断完善
ElectricCar
细节时,会出现专门针对汽车电瓶的属性和方法; -
可以将这些属性和方法提取出来,放到一个名为
Battery
的类中,并将一个Battery
实例用作ElectricCar
类的一个属性: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): """打印一条关于里程的信息""" msg = "This car has " + str(self.odometer_reading) + " miles on it." print(msg) def update_odometer(self, mileage): """将里程表读数设置为指定的值""" self.odometer_reading = mileage def increment_odometer(self, miles): self.odometer_reading += miles class Battery(): def __init__(self, battery_size=70): """初始化电瓶属性""" self.battery_size = battery_size def describe_battery(self): """描述电瓶容量的消息""" msg = "This car has a " + str(self.battery_size) + "-kwh battery." print(msg) def get_range(self): """打印一条信息,指出电瓶的续航里程""" if self.battery_size == 70: range = 240 elif self.battery_size == 85: range = 270 msg = "This car can go approximately " + str(range) + " miles on a full charge." print(msg) class ElectricCar(Car): """电动汽车的独到之处""" def __init__(self, make, model, year): super().__init__(make, model, year) self.battery = Battery() my_tesla = ElectricCar('tesla', 'model s', 2016) msg = my_tesla.get_descriptive_name() print(msg) my_tesla.battery.describe_battery() my_tesla.battery.get_range()
- 在第 29 行,定义了一个名为
Battery
的新类,它没有继承任何类; - 在第 32 行,方法
__init__()
除self
外,还有另一个形参battery_size
;这个形参是可选的,如果没有给他提供值,电瓶容量将会被设置为 70; - 方法
describe_battery()
也被移动到这个类中; - 在
ElectricCar
类中,添加了一个self.battery
属性,第 53 行; - 这行代码让 Python 创建一个新的
Battery
实例,并将该实例存储在属性self.battery
中; - 每当 51 行的方法
__init__()
被调用时,都将执行该操作,因此,每创建一个ElectricCar
实例,都会自动创建一个Battery
实例; - 第 58 行中,会让 Python 在实例
my_tesla
中查找属性battery
,并对存储在该属性中的Battery
实例调用方法describe_battery()
;
- 在第 29 行,定义了一个名为
-
结果:
2016 Tesla Model S This car has a 70-kwh battery. This car can go approximately 240 miles on a full charge.
3.6 练习
1. 冰淇淋小店:
-
创建一个名为
Restaurant
的类,其方法__init__()
设置两个属性:restaurant_name
和cuisine_type
; -
创建一个名为
describe_restaurant()
的方法和一个名为open_restaurant()
的方法,其中前者打印前述两项信息,后者打印一条消息,指出餐馆正在营业; -
冰淇淋小店是一种特殊的餐馆,编写一个名为
IceCreamStand
的类,让它继承餐馆Restaurant
类; -
添加一个
flavors
的属性,用于存储一个由各种口味的冰淇淋组成的列表; -
编写一个显示这些冰淇淋的方法;
-
创建一个
IceCreamStand
实例,并调用这个方法;
-
代码:
class Restaurant(): """定义一个餐馆类""" def __init__(self, restaurant_name, cuisine_type): self.restaurant_name = restaurant_name self.cuisine_type = cuisine_type def describe_restaurant(self): msg = "The restaurant is called " + self.restaurant_name + "." print(msg) msg = "The restaurant cuisine type are " + self.cuisine_type + "." print(msg) def open_restaurant(self): msg = "The restaurant is opening!" print(msg) class IceCreamStand(Restaurant): def __init__(self, restaurant_name, cuisine_type): super().__init__(restaurant_name, cuisine_type) self.flavors = ['strawberry', 'vanilla', 'chocolate'] def describe_icecream(self): msg = "The ice creams are as follows: " print(msg) for flavor in self.flavors: msg = "- " + flavor print(msg) my_icecream = IceCreamStand('IceCreamStand_A', 'Chinese') my_icecream.describe_icecream()
-
结果:
The ice creams are as follows: - strawberry - vanilla - chocolate
2. 管理员
-
创建一个名为
User
的类,其中包含属性first_name
和last_name
,还有用户简介通常会存储的其他几个属性; -
在类
User
中定义一个名为describe_user()
的方法,它打印用户信息摘要; -
再定义一个名为
greet_user()
的方法,它向用户发出个性化的问候; -
管理员是一种特殊的用户,编写一个名为
Admin
的类,让它继承User
类; -
添加一个名为
privileges
的属性,用于存储一个由字符串(如"can add post"、"can delete post"、"can ban user"
等)组成的列表; -
编写一个名为
show_privileges()
的方法,它显示管理员的权限; -
创建一个
Admin
实例,并调用这个方法;
-
代码:
class User(): """这是一个用户类""" def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age def describe_user(self): full_name = self.first_name + " " + self.last_name msg = "This user is named " + full_name.title() + ", and " + str(self.age ) + " years old." print(msg) def greet_user(self): full_name = self.first_name + " " + self.last_name msg = "Hi, " + full_name.title() + "! My friend, How are you? I miss you so much!" print(msg) class Admin(User): def __init__(self, first_name, last_name, age): super().__init__( first_name, last_name, age) self.privileges = ["can add post", "can delete post", "can ban user"] def show_privileges(self): msg = "The administrator has the following permissions: " print(msg) for permission in self.privileges: msg = "- " + permission print(msg) admin = Admin('li', 'si', 28) admin.show_privileges()
-
结果:
The administrator has the following permissions: - can add post - can delete post - can ban user
3. 权限
-
编写一个名为
Privileges
的类,它只有一个属性 –privileges
,其中存储了管理员所说的字符串列表; -
将方法
show_privileges()
移动到这个类中; -
并在
Admin
类中,将一个Privileges
实例用作其属性; -
创建一个
Admin
实例,并使用方法show_privileges()
来显示其权限;
-
代码:
class User(): """这是一个用户类""" def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age def describe_user(self): full_name = self.first_name + " " + self.last_name msg = "This user is named " + full_name.title() + ", and " + str(self.age ) + " years old." print(msg) def greet_user(self): full_name = self.first_name + " " + self.last_name msg = "Hi, " + full_name.title() + "! My friend, How are you? I miss you so much!" print(msg) class Privileges(): def __init__(self, privileges=["can add post", "can delete post", "can ban user"]): self.privileges = privileges def show_privileges(self): msg = "The administrator has the following permissions: " print(msg) for permission in self.privileges: msg = "- " + permission.title() print(msg) class Admin(User): def __init__(self, first_name, last_name, age): super().__init__( first_name, last_name, age) self.privileges = Privileges() user_admin = Admin('li', 'si', 20) user_admin.privileges.show_privileges()
-
结果:
The administrator has the following permissions: - Can Add Post - Can Delete Post - Can Ban User
4. 电瓶升级
-
在本节最后一个
electric_car.py
版本中,给Battery
类添加一个名为upgrade_battery()
的方法; -
这个方法检查电瓶容量,如果它不是 85,就将它设置为 85。
-
创建一辆电瓶容量为默认值的电动汽车,调用方法
get_range()
。你会看到这辆汽车的续航里程增加了;
-
代码:
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): """打印一条指出汽车里程的消息""" msg = "This car has " + str(self.odometer_reading) + " miles on it." print(msg) def update_odometer(self, mileage): """ 将里程表毒蛇设置为指定的值 禁止将里程表读数往回调 """ if mileage >= self.odometer_reading: self.odometer_reading = mileage else: msg = "You can't roll back an odometer!" print(msg) def increment_odometer(self, miles): """将里程表读数增加指定的量""" self.odometer_reading += miles class Battery(): """一次模拟电动汽车电瓶的简单尝试""" def __init__(self, battery_size=70): """初始化电瓶属性""" self.battery_size = battery_size def describe_battery(self): """打印一条描述电瓶容量的消息""" msg = "This car has a " + str(self.battery_size) + "-kWh battery." print(msg) def get_range(self): """打印一条消息,指出电瓶的续航里程""" if self.battery_size == 70: range = 240 elif self.battery_size == 85: range = 270 msg = "This car can go approximately " + str(range) + " miles on a full charge." print(msg) def upgrade_battery(self): """对电瓶进行升级""" if self.battery_size != 85: self.battery_size = 85 class ElectricCar(Car): """电动汽车的独特之处""" def __init__(self, make, model, year): """初始化父类的属性,再初始化电动汽车特有的属性""" super().__init__(make, model, year) self.battery = Battery() my_tesla = ElectricCar('tesla', 'model s', 2016) my_tesla.battery.get_range() my_tesla.battery.upgrade_battery() my_tesla.battery.get_range()
-
结果:
This car can go approximately 240 miles on a full charge. This car can go approximately 270 miles on a full charge.
4. 导入类
- 随着不断地给类添加功能,文件可能变得很长。即使妥善的使用继承也是如此;
- 为了遵循 Python 的总体理念,应让文件尽可能整洁;
- Python 允许你将类存储在模块中,然后在主程序中导入所需的模块;
4.1 导入单个类
-
下面创建一个只包含
Car
类的模块;car.py
"""一个可用于表示汽车的类""" 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): """打印一条关于里程的信息""" msg = "This car has " + str(self.odometer_reading) + " miles on it." print(msg) def update_odometer(self, mileage): """将里程表读数设置为指定的值""" self.odometer_reading = mileage def increment_odometer(self, miles): self.odometer_reading += miles
- 在第 1 行,包含了一个模块级文档字符串,对该模块的内容做了简要的描述;
- 一般对自己所创建的每个模块都编写文档字符串;
-
下面创建另一个文件
my_car.py
,并在其中导入Car
类并创建其实例:my_car.py
from car import Car my_new_car = Car('audi', 'a4', 2016) msg = my_new_car.get_descriptive_name() print(msg)
- 第 1 行的
import
语句是让 Python 打开模块car
,并导入其中的Car
类; - 这样就可以使用
Car
类了,就像在这个文件中定义一样;
- 第 1 行的
-
结果:
2016 Audi A4
-
导入类是一种有效的编程方式;
-
通过将这个类移动到另一个模块中,并导入该模块,依然可以使用其所有的功能,但主程序文件变得整洁而易于阅读了;
4.2 在同一模块中存储多个类
-
虽然同一模块中的类之间应存在某种相关性,但是可以根据需要在一个模块中存储任意数量的类;
-
可以在
Car
类中,存储关于电动汽车的Battery
和ElectricCar
类;car.py
"""一组用于表示汽车的类""" 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): """打印一条关于里程的信息""" msg = "This car has " + str(self.odometer_reading) + " miles on it." print(msg) def update_odometer(self, mileage): """将里程表读数设置为指定的值""" self.odometer_reading = mileage def increment_odometer(self, miles): self.odometer_reading += miles class Battery(): """一次模拟电动汽车电瓶的简单尝试""" def __init__(self, battery_size=70): """初始化电瓶属性""" self.battery_size = battery_size def describe_battery(self): """打印一条描述电瓶容量的消息""" msg = "This car has a " + str(self.battery_size) + "-kWh battery." print(msg) def get_range(self): """打印一条消息,指出电瓶的续航里程""" if self.battery_size == 70: range = 240 elif self.battery_size == 85: range = 270 msg = "This car can go approximately " + str(range) + " miles on a full charge." print(msg) def upgrade_battery(self): """对电瓶进行升级""" if self.battery_size != 85: self.battery_size = 85 class ElectricCar(Car): """电动汽车的独特之处""" def __init__(self, make, model, year): """初始化父类的属性,再初始化电动汽车特有的属性""" super().__init__(make, model, year) self.battery = Battery()
-
创建一个名为
my_electric_car.py
的文件,导入ElectricCar
类,并创建一辆电动汽车了:my_electric_car.py
from car import ElectricCar my_tesla = ElectricCar('tesla', 'model s', 2016) msg = my_tesla.get_descriptive_name() print(msg) 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.
4.3 从一个模块中导入多个类
-
可根据需要在程序文件中导入任意数量的类;
-
如果在同一个程序中要创建普通汽车和电动汽车,就需要将
Car
和ElectricCar
类同时导入;from car import Car, ElectricCar my_beetle = Car('volkswagen', 'beetle', 2016) msg = my_beetle.get_descriptive_name() print(msg) my_tesla = ElectricCar('tesla', 'roadster', 2016) msg = my_tesla.get_descriptive_name() print(msg)
-
从一个模块中导入多个类时,用豆好分隔了各个类;
-
导入必要的类后,就可以根据需要创建每个类的任意数量的实例;
-
结果:
2016 Volkswagen Beetle 2016 Tesla Roadster
4.4 导入整个模块
-
还可以导入整个模块,在使用句点表示法访问需要的类;
-
这种导入方法很简单,代码也容易阅读;
import car my_beetle = Car('volkswagen', 'beetle', 2016) msg = my_beetle.get_descriptive_name() print(msg) my_tesla = ElectricCar('tesla', 'roadster', 2016) msg = my_tesla.get_descriptive_name() print(msg)
4.5 导入模块中的所有类
-
要导入模块中的每个类,可以使用下面的语法:
from module_name import *
-
但是不推荐使用这种导入方式;
- 这种导入方式可能引发名称方面的困惑,如果一不小心导入一个与程序文件中其他东西同名的类,将引发难以诊断的错误;
- 需要从一个模块中导入很多类时,最好导入整个模块,并使用
module_name.class_name
语法来进行访问;
4.6 从一个模块中导入另一个模块
-
有时候,需要将类分散到多个模块中,以免模块太大,或在同一个模块中存储不相关的类;
-
将类存储在多个模块中时,会出现一个模块中的类依赖于另一个模块中的类,在这种情况下,可以在前一个模块中导入必要的类;
-
例如,将
Car
类存储在一个模块中; -
将
ElectricCar
和Battery
类存储在另一个模块中;car.py
"""一个用于表示汽车的类""" class Car(): --snip--
electric_car.py
"""一组可以用于表示电动汽车的类""" from car import Car class Battery(): --snip class ElectricCar(Car): --snip
ElectricCar
类需要访问Car
类,所以在第 2 行,直接将Car
类导入该模块中;
-
从每个模块中导入类,可以根据需求创建任何类型的汽车:
from car import Car from electric_car import ElectricCar my_beetle = Car('volkswagen', 'beetle', 2016) msg = my_beetle.get_descriptive_name() print(msg) my_tesla = ElectricCar('tesla', 'roadster', 2016) msg = my_tesla.get_descriptive_name() print(msg)
-
结果:
2016 Volkswagen Beetle 2016 Tesla Roadster
4.7 自定义工作流程
- 在组织大型项目的代码方面,
- 一开始应该让代码结构尽可能简单;
- 先在一个文件中完成所有的工作,确定一切都能正确运行后,再将类移到独立的模块中;
4.8 练习
1. 导入 Restaurant 类
-
将最新的
Restaurant
类存储在一个模板中; -
在另一个文件中,导入
Restaurant
类,创建一个Restaurant
实例,并调用Restaurant
的一个方法,以确认import
语句正确无误; -
代码:
restaurant.py
"""定义一个餐馆类""" class Restaurant(): def __init__(self, restaurant_name, cuisine_type): self.restaurant_name = restaurant_name self.cuisine_type = cuisine_type def describe_restaurant(self): msg = "The restaurant is called " + self.restaurant_name + "." print(msg) msg = "The restaurant cuisine type are " + self.cuisine_type + "." print(msg) def open_restaurant(self): msg = "The restaurant is opening!" print(msg) class IceCreamStand(Restaurant): def __init__(self, restaurant_name, cuisine_type): super().__init__(restaurant_name, cuisine_type) self.flavors = ['strawberry', 'vanilla', 'chocolate'] def describe_icecream(self): msg = "The ice creams are as follows: " print(msg) for flavor in self.flavors: msg = "- " + flavor print(msg)
import_restaurant.py
from restaurant import Restaurant my_restaurant = Restaurant('name_A', 'chinese') my_restaurant.describe_restaurant()
-
结果:
The restaurant is called name_A. The restaurant cuisine type are chinese.
2. 导入 Admin 类
-
以为完成练习权限而做的工作为基础,将
User
、Privileges
和Admin
类存储在一个模块中; -
再创建一个文件,在其中创建一个
Admin
实例并对其调用方法show_privileges()
,以确认一切都能正确运行。 -
代码:
admin.py
"""这是一个用户类""" class User(): def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age def describe_user(self): full_name = self.first_name + " " + self.last_name msg = "This user is named " + full_name.title() + ", and " + str(self.age ) + " years old." print(msg) def greet_user(self): full_name = self.first_name + " " + self.last_name msg = "Hi, " + full_name.title() + "! My friend, How are you? I miss you so much!" print(msg) class Privileges(): def __init__(self, privileges=["can add post", "can delete post", "can ban user"]): self.privileges = privileges def show_privileges(self): msg = "The administrator has the following permissions: " print(msg) for permission in self.privileges: msg = "- " + permission.title() print(msg) class Admin(User): def __init__(self, first_name, last_name, age): super().__init__( first_name, last_name, age) self.privileges = Privileges()
user_admin.py
from admin import Admin my_admin = Admin('liu', 'neng', 20) my_admin.privileges.show_privileges()
-
结果:
The administrator has the following permissions: - Can Add Post - Can Delete Post - Can Ban User
3. 多个模块
-
将
User
类存储在一个模块中,并将Privileges
和Admin
类存储在另一个模块中; -
再创建一个文件,在其中创建一个
Admin
实例,并对其调用方法show_privileges()
,以确认一切都能正确运行; -
代码:
user.py
"""这是一个用户类""" class User(): def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age def describe_user(self): full_name = self.first_name + " " + self.last_name msg = "This user is named " + full_name.title() + ", and " + str(self.age ) + " years old." print(msg) def greet_user(self): full_name = self.first_name + " " + self.last_name msg = "Hi, " + full_name.title() + "! My friend, How are you? I miss you so much!" print(msg)
admin.py
"""这是一个管理员类""" from user import User class Privileges(): def __init__(self, privileges=["can add post", "can delete post", "can ban user"]): self.privileges = privileges def show_privileges(self): msg = "The administrator has the following permissions: " print(msg) for permission in self.privileges: msg = "- " + permission.title() print(msg) class Admin(User): def __init__(self, first_name, last_name, age): super().__init__( first_name, last_name, age) self.privileges = Privileges()
user_admin.py
from admin import Admin my_admin = Admin('li', 'li', 15) my_admin.privileges.show_privileges()
-
结果:
The administrator has the following permissions: - Can Add Post - Can Delete Post - Can Ban User
5. Python 标准库
-
Python 标准库是一组模块,安装的 Python 都包含它;
-
模块
collections
中的OrderedDict
类是一种有序的字典;favorite_language.py
from collections import OrderedDict favorite_language = OrderedDict() favorite_language['jen'] = 'python' favorite_language['sarah'] = 'c' favorite_language['edward'] = 'ruby' favorite_language['phil'] = 'python' for name, language in favorite_language.items(): msg = name.title() + "'s favorite language is " + language.title() + "." print(msg)
-
结果:
Jen's favorite language is Python. Sarah's favorite language is C. Edward's favorite language is Ruby. Phil's favorite language is Python.
-
要了解 Python 标准库,访问网站 Python Module of the Week
6. 类编码风格
- 类名采用 驼峰命名法 ,将类名中的每个单词首字母都大写,而不使用下划线;
- 实例名和模块名都采用小写格式,并在单词之间加上下划线;
- 对于每个类,都应紧跟在类定义后面包含啊一个文档字符串;用来描述类的功能;
- 在类中,可以使用一个空行来分隔方法;
- 在模块中,可以使用两个空行来分隔类;
- 需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的
import
语句,再添加一个空行,然后编写导入你自己编写的模块的import
语句。