9.1 创建和使用类
根据类来创建对象称为实例化。
9.1.1 创建Dog类
# dog.py
# 在Python中,首字母大写的名称指的是类
class Dog:
"""一次模拟小狗的简单尝试"""
def _init_(self, name, age):
"""初始化属性name和age"""
self.name = name
self.age = age
def sit(self):
"""模拟小狗收到命令时做下"""
print(f"{self.name} is now sitting.")
def roll_over(self):
"""模拟小狗收到命令时打滚"""
print(f"{self.name} rolled over!")
方法_init()_
(1)类中的函数称为方法
(2)当你根据Dog类创建新实例时,Python都会自动运行它。
(3)在这个方法名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。
(4)在这个方法的定义中,形参self必不可少,而且必须位于其他形参的前面。Python调用这个方法来创建Dog实例时,将自动传入实参self。每个实例相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。
(5)以self为前缀的变量可供类中的所有方法使用,可以通过类的任何实例来访问,称为属性。
9.1.2 根据类创建实例
class Dog:
"""一次模拟小狗的简单尝试"""
def _init_(self, name, age):
"""初始化属性name和age"""
self.name = name
self.age = age
def sit(self):
"""模拟小狗收到命令时做下"""
print(f"{self.name} is now sitting.")
def roll_over(self):
"""模拟小狗收到命令时打滚"""
print(f"{self.name} rolled over!")
my_dog = Dog('Willie', 6)
# 访问属性——句点表示法
print(f"My dog's name is {my_dog.name}.")
print(f"My dog is {my_dog.age} years old.")
# 调用方法
my_dog.sit()
my_dog.roll_over()
# 创建多个实例
your_dog = Dog('Lucy', 3)
print(f"Your dog's name is {your_dog.name}.")
print(f"Your dog is {your_dog.age} years old.")
your_dog.sit()
your_dog.roll_over()
动手试一试
9.2 使用类和实例
9.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 = f"{self.year} {self.make} {self.model}"
return long_name.title()
my_new_car = Car('audi', 'a4', '2009')
print(my_new_car.get_descriptive_name())
9.2.2 给属性指定默认值
创建实例时,有些属性无需通过形参来定义,可在方法_init_()中为其指定默认值。
# 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 = 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.")
my_new_car = Car('audi', 'a4', '2009')
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()
9.2.3 修改属性的值
1、直接修改属性的值
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
2、通过方法修改属性的值
# 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 = 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_readig:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
my_new_car = Car('audi', 'a4', '2009')
print(my_new_car.get_descriptive_name())
my_new_car.update_odometer(23)
my_new_car.read_odometer()
3、通过方法对属性的值进行递增
# 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 = 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_readig:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
def increment_odometer(self, miles):
"""将里程表读数增加指定的量"""
self.odometer_reading += miles
my_new_car = Car('audi', 'a4', '2009')
print(my_new_car.get_descriptive_name())
my_new_car.update_odometer(23)
my_new_car.read_odometer()
my_new_car.increment_odometer(100)
my_new_car.read_odometer()
动手试一试
9.3 继承
编写类时,并非总是要从空白开始。如果要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,将自动获得另一个类的所有属性和方法。原有的类称为父类,而新类称为子类。子类继承了父类的所有属性和方法,同时还可以定义自己的属性和方法。
9.3.1 子类的方法_init_()
在既有类的基础上编写新类时,通常要调用父类的方法_init_()。这将初始化在父类_init_()方法中定义的所有属性,从而让子类包含这些属性。
# eletric_car.py
# 定义子类时,必须在圆括号内指定父类的名称
class EletricCar(Car):
"""电动汽车的独特之处"""
def _init_(self, make, model, year):
"""初始化父类的属性"""
# 让Python调用Car类的方法_init_(),父类也称为超类
super()._init_(make, model, year)
my_tesla = EletricCar('tesla', 'model s', 2019)
print(my_tesla.get_description_name())
创建子类时,父类必须包含在当前文件中,且位于子类前面。
9.3.2 给子类定义属性和方法
class EletricCar(Car):
"""电动汽车的独特之处"""
def _init_(self, make, model, year):
"""
初始化父类的属性
再初始化电动汽车特有的属性
"""
# 让Python调用Car类的方法_init_(),父类也称为超类
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 = EletricCar('tesla', 'model s', 2019)
print(my_tesla.get_description_name())
my_tesla.describe_battery()
9.3.3 重写父类的方法
可在子类中定义一个与要重写的父类方法同名的方法。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。
9.3.4 将实例用作属性
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.")
def get_range(self):
"""打印一条消息,指出电瓶的续航里程"""
if self.battery_size == 75:
range = 260
elif self.battery_size == 100:
range = 315
print(f"This car can go about {range} miles on a full charge.")
class EletricCar(Car):
"""电动汽车的独特之处"""
def _init_(self, make, model, year):
"""
初始化父类的属性
再初始化电动汽车特有的属性
"""
# 让Python调用Car类的方法_init_(),父类也称为超类
super()._init_(make, model, year)
# 让Python创建一个新的Battery实例。每当方法_init_()被调用时,都将执行该操作,因此现在每个EletricCar实例都包含一个自动创建的Battery实例
self.battery = Battery()
def describe_battery(self):
"""打印一条描述电瓶容量的消息"""
print(f"This car has a {self.battery_size}-kWh battery.")
my_tesla = EletricCar('tesla', 'model s', 2019)
print(my_tesla.get_description_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()
9.3.5 模拟实物
动手试一试
9.4 导入类
允许将类存储在模块中,然后在主程序中导入所需的模块。
9.4.1 导入单个类
# 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 = 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_readig:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
def increment_odometer(self, miles):
"""将里程表读数增加指定的量"""
self.odometer_reading += miles
# my_car.py
from car import Car
my_new_car = Car('audi', 'a4', 2019)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
9.4.2 在一个模块中存储多个类
# my_electric_car.py
# 将Car、Battery、ElectricCar类均放在car.py文件中
from car import EletricCar
my_tesla = EletricCar('tesla', 'model s', 2019)
print(my_tesla.get_description_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()
9.4.3 从一个模块中导入多个类
# my_cars.py
from car import Car, ElectricCar
9.4.4 导入整改模块
# my_cars.py
import car
my_beetle = car.Car('volkswagen', 'beetle', 2019)
print(my_beetle.get_descriptive_name())
my_tesla = car.ElectricCar('tesla', 'roadster', 2019)
print(my_tesla.get_descriptive_name())
9.4.5 导入模块中的所有类
from module_name import *
不推荐使用这种导入方式,原因:
(1)如果只看文件开头的import语句,就能清楚地知道程序使用了哪些类,将大有裨益。然而这种导入方式没有明确地指出使用了模块中的哪些类
(2)这种方式还可能引发名称方面的迷惑。如果不小心导入了一个与程序文件中其他东西同名的类,将引发难以诊断的错误
9.4.6 在一个模块中导入另一个模块
有时,将类分散到多个模块中,可以在一个模块中导入其必要的类
9.4.7 使用别名
from electric_car import ElectricCar as EC
my_tesla = EC('tesla', 'roadster', 2019)
9.4.8 自定义工作流程
动手试一试
9.5 Python标准库
Python标准库是一组模块,我们安装的Python都包含它。
>>> from random import randint
# 将两个整数作为参数,并随机返回一个位于这两个整数之间(包含)的整数
>>> randint(1, 6)
>>> from random import choice
# 将一个列表或元组作为参数,并随机返回一个元素
>>> players = ['charles', 'martina', 'michael', 'florence', 'eli']
>>> first_up = choice(players)
>>> first_up
'florence'
动手试一试
9.6 类编码风格
1、类名采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不是使用下划线。
2、实例名和模块名都采用小写格式,并在单词之间加上下划线。
3、对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式规定。每个模块也都应包含一个文档字符串,对其中的类可用于做什么进行描述。
4、在类中,可使用一个空行来分隔方法;而在模块中,可使用两个空行来分隔类。
5、需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的import语句,再添加一个空行,然后编写导入你自己编写的模块的import语句。