前面的学习内容没有写在CSDN里,学习内容从9.3的习题课程开始
第九章 类
9.3 继承
9.3动手试一试
9-8权限
权限:编写一个名为 Privileges 的类,它只有一个属性——privileges,其中
存储了练习 9-7 所说的字符串列表。将方法 show_privileges()移到这个类中。在 Admin
类中,将一个 Privileges 实例用作其属性。创建一个 Admin 实例,并使用方法
show_privileges()来显示其权限。
class User():
"""用户信息类,用于显示用户信息"""
def __init__(self,first_name,last_name,user_info):
"""初始化属性"""
self.first_name=first_name
self.last_name=last_name
self.user_info=user_info
def describe_user(self):
"""打印用户信息"""
print("姓名:"+self.first_name+self.last_name+"\n用户年龄:"+str(self.user_info))
def greet_user(self):
"打印给用户的问候语"
print("欢迎您来到我们餐厅,您的信息是")
class Privileges():
def __init__(self,privileges=["can add post","can delete post","can ban user"]):
self.priviliges=privileges
def show_privileges(self):
print("\nPrivileges")
for privilege in self.priviliges:
print("The privilege is"+" "+privilege)
class Admin(User):
"""添加管理员权限"""
def __init__(self, first_name, last_name, user_info):
super().__init__(first_name, last_name, user_info)
self.privileges=Privileges()
admin=Admin("li","zeyu",36)
admin.greet_user()
admin.describe_user()
admin.privileges.show_privileges()
习题9-9
电瓶升级:在本节最后一个 electric_car.py 版本中,给 Battery 类添加一个名为
upgrade_battery()的方法。这个方法检查电瓶容量,如果它不是 85,就将它设置为 85。
创建一辆电瓶容量为默认值的电动汽车,调用方法 get_range(),然后对电瓶进行升级,
并再次调用 get_range()。你会看到这辆汽车的续航里程增加了。
class Car():
def __init__(self,make,year,model):
self.make=make
self.year=year
self.model=model
def describle_info(self):
long_name=str(self.make)+" "+str(self.year)+" "+str(self.model)
print(long_name.title())
def get_descriptive(self):
"""获取里程数"""
print(self.odometer)
def update_odometer(self,miliage):
"""指定里程数"""
if miliage >= self.odometer:
self.odometer=miliage
else:
print("里程数不能回拨")
def increment(self,add_miles):
self.odometer+=add_miles
def fill_gas_tank(self):
"""有油箱"""
print("\nThis car need a gas tank!")
class Battery():
def __init__(self,battery_size=70):
self.battery_size=battery_size
def get_range(self):
if self.battery_size==70:
range=240
elif self.battery_size==85:
range=270
message = "\nThis car can go approximately " + str(range)
message += " miles on a full charge."
print(message)
def upgrade_battery(self):
if self.battery_size!=85:
self.battery_size=85
else:
print("The battery is already upgraded.")
def describe_battery(self):
"""打印一条描述电瓶容量的消息"""
print("This car has a " + str(self.battery_size) + "-kWh battery.")
class Electric_car(Car):
def __init__(self, make, year, model):
super().__init__(make, year, model)
self.battery= Battery()
def fill_gas_tank(self):
"""电动车没有油箱"""
print("This car doesn't need a gas tank!")
my_tesla=Electric_car("tesla",2016,"model s")
my_tesla.describle_info()
my_tesla.fill_gas_tank()
my_tesla.battery.get_range()
my_tesla.battery.upgrade_battery()
my_tesla.battery.get_range()
一直报错显示:AttributeError: 'Battery' object has no attribute 'battery_size',错误原因主要在于这一行代码
def _init_(self,battery_size=70):
修正后的代码如下:
def __init__(self,battery_size=70):
两根下划线呀,这个错误对于新手来说很隐蔽
9.4 导入类
Python允许你将类存储在模块中,然后在主程序中导入所需的模块。
9.4.1 导入单个类
应为自己创建的每个模块都编写文档字符串,用来说明模块文档
"""一个可用于表示汽车的类"""
把所有和车相关的方法写在car类里面,单个的实例出来
创建另一个文件——my_car.py,在其中导入Car类并创建其实例
from car import Car
my_new_car=("aodi","model s",2013)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading=23
my_new_car.read_odometer()
运行后发现报错,显示
Traceback (most recent call last):
File "c:\workspace\pywork\my_car", line 4, in <module>
print(my_new_car.get_descriptive_name())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'tuple' object has no attribute 'get_descriptive_name'
查阅纠正后发现,下面这行代码有错误
my_new_car=("aodi","model s",2013)
在实例化的过程中,没有正确创建实例,实例化的格式应该为
实例名称=类名(属性)
所以修正的代码为
my_new_car=Car("aodi","model s",2013)
输出结果为:
通过将这个类移到一个模块中,并导入该模块,你依然可以使用其所有功能,但主程序文件变得整洁而易于阅读了。
9.4.2 在一个模块中存储多个类
虽然同一个模块中的类之间应存在某种相关性,但可根据需要在一个模块中存储任意数量的类。
类Battery和ElectricCar都可帮助模拟汽车,因此下面将它们都加入模块car.py中。
class Car():
-snip-
class Battery():
"""一次模拟电动汽车电瓶的简单尝试"""
def __init__(self, battery_size=60):
"""初始化电瓶的属性"""
self.battery_size = battery_size
def describe_battery(self):
"""打印一条描述电瓶容量的消息"""
print("This car has a " + str(self.battery_size) + "-kWh battery.")
def get_range(self):
"""打印一条描述电瓶续航里程的消息"""
if self.battery_size == 70:
range = 240
elif self.battery_size == 85:
range = 270
message = " This car can go approximately " + str(range)
message += " miles on a full charge."
print(message)
class ElectricCar(Car):
"""模拟电动汽车的独特之处"""
def __init__(self, make, model, year):
"""
初始化父类的属性,再初始化电动汽车特有的属性
"""
super().__init__(make, model, year)
self.battery = Battery()
我按照要求加进去之后,一直报错,报错内容为
找了很久,终于找到了报错原因,range的值是根据battery_size的变量给的,只有当battery_size=70或者battery_size=85的时候,才会给range变量赋值。
if self.battery_size == 70:
range = 240
elif self.battery_size == 85:
range = 270
可是我根据教材上写的内容复制过去,发现battery_size的值=60,不满足条件语句的条件
class Battery():
"""一次模拟电动汽车电瓶的简单尝试"""
def __init__(self, battery_size=60):
"""初始化电瓶的属性"""
self.battery_size = battery_size
因此也当然不会给range赋值了,将battery_size的初始值改为70就可以正常运行了。
9.4.3 从一个模块中导入多个类
从一个模块中导入多个类时,用逗号分隔了各个类。导入必要的类后,就可根据需要
创建每个类的任意数量的实例。
from car import Car,ElectricCar
my_beetle=ElectricCar("volkswagen","beetle",2016)
print(my_beetle.get_descriptive_name())
my_new_car=Car("aodi","model s",2013)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading=23
my_new_car.read_odometer()
输出结果
9.4.4 导入整个模块
还可以导入整个模块,再使用句点表示法访问需要的类。这种导入方法很简单,代码也易
于阅读。
由于创建类实例的代码都包含模块名,因此不会与当前文件使用的任何名称发生冲突
import car
my_beetle=car.ElectricCar("volkswagen","beetle",2016)
print(my_beetle.get_descriptive_name())
my_new_car=car.Car("aodi","model s",2013)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading=23
my_new_car.read_odometer()
9.4.5 导入模块中的所有类
要导入模块中的每个类,可使用下面的语法:
from module_name import *
不推荐使用这种导入方式,这种导入方式没有明确地指出你使用了模块中的哪些类。这种导入方式还可能引发名称方面的困惑。如果你不小心导入了一个与程序文件中其他东西同名的类,将引发难以诊断的错误。
需要从一个模块中导入很多类时,最好导入整个模块,并使用module_name.class_name语法
来访问类。这样做时,虽然文件开头并没有列出用到的所有类,但你清楚地知道在程序的哪些地
方使用了导入的模块;你还避免了导入模块中的每个类可能引发的名称冲突。
9.4.6 在一个模块中导入另一个模块
有时候,需要将类分散到多个模块中,以免模块太大,或在同一个模块中存储不相关的类。
将类存储在多个模块中时,你可能会发现一个模块中的类依赖于另一个模块中的类。在这种情况
下,可在前一个模块中导入必要的类。
将Car类存储在一个模块中,并将ElectricCar和Battery类存储在另一个模块中。
将第二个模块命名为electric_car.py(这将覆盖前面创建的文件electric_car.py),并将Battery和ElectricCar类复制到这个模块中:
现在可以分别从每个模块中导入类,以根据需要创建任何类型的汽车了:
输出结果
9.4.7 自定义工作流程
如果你喜欢模块和文件的交互方式,可在项目开始时就尝试将类存储到模块中。先找出让你能够编写出可行代码的方式,再尝试让代码更为组织有序。
9.4动手试一试
9-10导入 Restaurant 类
将最新的 Restaurant 类存储在一个模块中。在另一个文件中,导入 Restaurant 类,创建一个 Restaurant 实例,并调用 Restaurant 的一个方法,以确认 import 语句正确无误。
import restaurant
my_first_restaurant=restaurant.Resturant("李泽宇的小店","中国餐厅")
my_first_restaurant.open_restaurant()
运行结果如下:
9-11 导入 Admin 类
以为完成练习 9-8 而做的工作为基础,将 User、 Privileges 和Admin 类存储在一个模块中,再创建一个文件,在其中创建一个 Admin 实例并对其调用方法 show_privileges(),以确认一切都能正确地运行。
user.py
"""一个可用于表示汽车的类"""
class User():
"""用户信息类,用于显示用户信息"""
def __init__(self,first_name,last_name,user_info):
"""初始化属性"""
self.first_name=first_name
self.last_name=last_name
self.user_info=user_info
def describe_user(self):
"""打印用户信息"""
print("姓名:"+self.first_name+self.last_name+"\n用户年龄:"+str(self.user_info))
def greet_user(self):
"打印给用户的问候语"
print("欢迎您来到我们餐厅,您的信息是")
class Privileges():
def __init__(self,privileges=["can add post","can delete post","can ban user"]):
self.priviliges=privileges
def show_privileges(self):
print("\nPrivileges")
for privilege in self.priviliges:
print("The privilege is"+" "+privilege)
class Admin(User):
"""添加管理员权限"""
def __init__(self, first_name, last_name, user_info):
super().__init__(first_name, last_name, user_info)
self.privileges=Privileges()
my_user.py
import user
admin=user.Admin("李","泽宇",18)
admin.privileges.show_privileges()
运行结果如下:
9-12 多个模块
将 User 类存储在一个模块中,并将 Privileges 和 Admin 类存储在另一个模块中。再创建一个文件,在其中创建一个 Admin 实例,并对其调用方法show_privileges(),以确认一切都依然能够正确地运行。
user.py
"""一个可用于表示汽车的类"""
class User():
"""用户信息类,用于显示用户信息"""
def __init__(self,first_name,last_name,user_info):
"""初始化属性"""
self.first_name=first_name
self.last_name=last_name
self.user_info=user_info
def describe_user(self):
"""打印用户信息"""
print("姓名:"+self.first_name+self.last_name+"\n用户年龄:"+str(self.user_info))
def greet_user(self):
"打印给用户的问候语"
print("欢迎您来到我们餐厅,您的信息是")
privileges.py
from user import User
class Privileges():
def __init__(self,privileges=["can add post","can delete post","can ban user"]):
self.priviliges=privileges
def show_privileges(self):
print("\nPrivileges")
for privilege in self.priviliges:
print("The privilege is"+" "+privilege)
class Admin(User):
"""添加管理员权限"""
def __init__(self, first_name, last_name, user_info):
super().__init__(first_name, last_name, user_info)
self.privileges=Privileges()
my_user.py
import privileges
admin=privileges.Admin("李","泽宇",18)
admin.privileges.show_privileges()
输出结果
9.5 Python 标准库
字典让你能够将信息关联起来,但它们不记录你添加键—值对的顺序。要创建字典并记录其
中的键—值对的添加顺序,可使用模块collections中的OrderedDict类。
from collections import OrderedDict
favorite_languages = OrderedDict()
favorite_languages['jen']='python'
favorite_languages['zeyu']='C#'
favorite_languages['yanqing']='C++'
favorite_languages['Ann']='Java'
for name,language in favorite_languages.items():
print(name.title()+"'s favorite language is "+language.title()+'.')
这里是根据类创建了一个实例(python创建实例的方式:实例名称=类名(属性))
输出结果
9.5动手试一试
9-13 使用 OrderedDict
在练习 6-4 中,你使用了一个标准字典来表示词汇表。请使用 OrderedDict 类来重写这个程序,并确认输出的顺序与你在字典中添加键—值对的顺序一致。
同上述的示例代码
9-14 骰子
模块 random 包含以各种方式生成随机数的函数,其中的 randint()返回一个位于指定范围内的整数,例如,下面的代码返回一个 1~6 内的整数:
from random import randint
x = randint(1, 6)
请创建一个 Die 类,它包含一个名为 sides 的属性,该属性的默认值为 6。编写一个名为 roll_die()的方法,它打印位于 1 和骰子面数之间的随机数。创建一个 6 面的骰子,再掷 10 次。
创建一个 10 面的骰子和一个 20 面的骰子,并将它们都掷 10 次。
代码如下,但是它一直报错,不知道为什么
from random import randint
class Die():
"""投掷骰子"""
def __init__(self,sides=6):
self.sides=sides
def roll_die(self):
for i in range(1,11):
x = randint(1,self.sides)
print(x)
#实例化类
die_first = Die(6)
die_second = Die(10)
die_third = Die(20)
#调用实例
die_first.roll_die()
die_second.roll_die()
die_third.roll_die()
报错内容如下:
后面终于发现了问题,是因为实例化写在类外面,我写在了类里面,所以一定要注意格式呀~
正确代码如下:
from random import randint
class Die():
"""投掷骰子"""
def __init__(self, sides=6):
self.sides = sides
def roll_die(self):
for i in range(1, 11):
x = randint(1, self.sides)
print(x)
# 实例化类
die_first = Die(6)
die_second = Die(10)
die_third = Die(20)
# 调用实例方法
die_first.roll_die()
die_second.roll_die()
die_third.roll_die()
9-14补充range函数
补充:在Python中,range()
是一个内置函数,用于创建一个代表一个数字序列的范围对象。它通常用于循环,例如在 for
循环中,来生成一系列连续的整数值。
range()
函数有三种不同的用法:
-
range(stop)
:生成从0开始到stop - 1
结束的整数序列。 -
range(start, stop)
:生成从start
开始到stop - 1
结束的整数序列。 -
range(start, stop, step)
:生成从start
开始到stop - 1
结束的整数序列,每个数之间的步长为step
。
这些参数可以是整数,也可以是可以转换为整数的其他数据类型。range()
返回的对象实际上是一个生成器(generator),它在需要时按需生成值,而不是一次性将所有值都存储在内存中。
9-15 Python Module of the Week
Python 3 Module of the Week — PyMOTW 3
9.6 类编码风格
类名应采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名
和模块名都采用小写格式,并在单词之间加上下划线。
对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的
功能,并遵循编写函数的文档字符串时采用的格式约定。每个模块也都应包含一个文档字符串,
对其中的类可用于做什么进行描述。
可使用空行来组织代码,但不要滥用。在类中,可使用一个空行来分隔方法;而在模块中,
可使用两个空行来分隔类。
需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的import语句,再
添加一个空行,然后编写导入你自己编写的模块的import语句。在包含多条import语句的程序中,
这种做法让人更容易明白程序使用的各个模块都来自何方。
9.7 小结
这一章学习了类的创建,修改值、类的继承、类的实例化、类的导入以及导入Python标准库的类