类
- 在Python中,首字母大写的名称指的是类
- 这个类定义中的括号是空的,因为我们要从空白创建这个类
- 面向对象编程是最有效的软件编写方式之一
- 在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象
- 编写类时,你定义一大类对象都有通用行为
- 基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个形
- 使用面向对象编程可模拟显示情景,其逼真程度令人惊讶
- 根据类来创建对象被称为实例化,这让你能够使用类的实例
- 编写一些类并创建其实例
- 指定可在实例中存储什么信息,定义可对这些实例执行哪些操作
- 编写一些类来扩展既有类的功能,让相似的类能够高效地共享代码
- 把自己编写的类存储在模块中,并在自己的程序文件中导入其他程序员编写的类
一:创建和使用类
- 使用类几乎可以模拟任何东西
1、创建Dog类
- 它表示的不是特定的小狗,而是任何小狗
- 根据Dog类创建的每个实例都将存储名字和年龄
- 赋予了每条小狗蹲下(sit())和打滚(roll_over())的能力
(1) 方法__init__()
- 类中的函数称为方法
- 有关函数的一切都适用于方法,唯一的差别是调用方法的方式
- 方法__init__()是一个特殊的方法,开头和结尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突
- 形参self必不可少,还必须位于其他形参的前面
- Python调用这个_int_()方法创建Dog实例时,将自动传入实参self
- 每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法
- 可通过实例访问的变量称为属性
# 创建Dog类
class Dog():
# 文档字符,对类的功能作描述
"""一次模拟小狗的尝试"""
# Dog类创建新实例时,Python会自动运行它
def __init__(self, name, age):
"""初始化属性name和age"""
# 以self为前缀的变量都可供类中的所有方法调用
# 通过类的任何实例来访问这些变量
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!")
my_dog = Dog('willie', 6)
2、根据类创建实例
- 可将类视为有关如何创建的说明
- Dog类是一系列说明,让Python知道如何创建表示特定小狗的实例
(1)访问属性
- 要访问属性,可使用句点表示法
- 演示了Python如何获悉属性的值
- my_dog.name.title()
- Python先找到实例my_dog,再查找与这个实例相关联的属性name
- 在Dog类中引用这个属性时,使用的是self.name
- 将my_dog的属性name的值’willie’改为首字母大写
- my_dog.age
- Python先找到实例my_dog,再查找与这个实例相关联的属性age
- 在Dog类中引用这个属性时,使用的是self.age
- 将my_dog的属性age的值6转换为字符串
# 访问属性
'''class Dog():
--snip--'''
my_dog = Dog('willie', 6)
print("My dog name is " + \
my_dog.name.title() + \
".")
print("My dog is " + \
str(my_dog.age) + \
" years old.")
My dog name is Willie.
My dog is 6 years old.
(2) 调用方法
- 根据Dog类创建实例后,就可以使用句点表示法来调用Dog类中定义的任何方法
- 调用方法
- 可指定实例的名称(my_dog)和调用的方法,并用句点分隔它们
- my_dog.sit()
- Python在类Dog中查找方法sit()并运行其代码
- my_dog.roll_over()
- Python在类Dog中查找方法roll_over()并运行其代码
# 调用方法
"""class Dog():
--snip--"""
"""my_dog = Dog('willie', 6)"""
my_dog.sit()
my_dog.roll_over()
Willie is now sitting.
Willie rolled over!
(3) 创建多个实例
- 可按需求根据类创建任意数量的实例,条件是将每个实例都存储在不同的变量中,或占用列表或字典的不同的位置
- 就算给第二条小狗指定同样的名字和年龄,Python依然会根据Dog类创建另一个实例
my_dog = Dog('willie', 6)
print("My dog name is " + \
my_dog.name.title() + \
".")
print("My dog is " + \
str(my_dog.age) + \
" years old.")
my_dog.sit()
your_dog = Dog('lucy', 3)
print("\nMy dog name is " + \
your_dog.name.title() + \
".")
print("My dog is " + \
str(your_dog.age) + \
" years old.")
your_dog.sit()
My dog name is Willie.
My dog is 6 years old.
Willie is now sitting.
My dog name is Lucy.
My dog is 3 years old.
Lucy is now sitting.
二:使用类和实例
- 可以使用类来模拟现实世界中的很多情景
- 类编写好后,大部分时间都花在使用根据类创建的实例
- 修改实例的属性
- 直接修改实例的属性
- 编写方法以特定的方式进行修改
1、Car类
- 编写一个表示汽车的类,它存储了有关汽车的信息
- 一个汇总这些信息的方法
class Car():
"""一次模拟汽车的简单尝试"""
# 方法__init__()接受这些形参的值
# 并将它们存储在根据这个类创建的实例的属性中
def __init__(self, model, make, year):
"""初始化描述汽车的属性"""
self.model = model
self.make = make
self.year = year
return None
def get_descriptive_name(self):
"""返回整洁d额描述性信息"""
long_name = str(self.year) + ' ' + self.make + " " + self.model
return long_name.title()
new_car = Car('a4', 'audi', 2016)
print(new_car.get_descriptive_name())
2016 Audi A4
2、给属性指定默认值
- 类中的每个属性都必须有初始值,哪怕这个值是0或空字符串
- 在有些情况下,如设置默认值时,在方法__init__()内指定这种初始值是可行的
- 对某个属性指定了默认值,就无需包含为它提供初始值的形参
class Car():
"""一次模拟汽车的简单尝试"""
def __init__(self, model, make, year):
"""初始化描述汽车的属性"""
self.model = model
self.make = make
self.year = year
# 添加属性,其初始值总是为0
self.odometer_reading = 0
return None
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.")
new_car = Car('a4', 'audi', 2016)
print(new_car.get_descriptive_name())
new_car.read_odometer()
2016 Audi A4
This car has 0 miles on it.
3、修改属性的值
- 三种不同的方式修改属性值
- 直接通过实例进行修改
- 通过方法设置进行修改
- 通过方法进行递增(增加特定的值)
(1)直接修改属性的值
- 要修改属性的值,最简单的方式是通过实例直接访问它
- 使用句点表示法来直接访问设置汽车的属性 odometer_reading
- 这行代码让 Python 在实例 my_car 中找到属性 odometer_reading,并将该属性设置为10
class Car():
"""一次模拟汽车的简单尝试"""
def __init__(self, model, make, year):
"""初始化描述汽车的属性"""
self.model = model
self.make = make
self.year = year
self.odometer_reading = 0
return None
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.")
return None
new_car = Car('a4', 'audi', 2016)
print(new_car.get_descriptive_name())
# 1、直接访问属性并修改属性的值
new_car.odometer_reading = 10
new_car.read_odometer()
2016 Audi A4
This car has 10 miles on it.
(2)通过方法修改属性的值
- 无需直接访问属性
- 可将值传递给一个方法,由它在内部进行更新
- 这个方法接受一个里程值,并将其存储到self.odometer_reading中
- 调用了方法update_odometer(),并向它提供了实参(该实参对应于方法定义中的实参mileage)
class Car():
"""一次模拟汽车的简单尝试"""
def __init__(self, model, make, year):
"""初始化描述汽车的属性"""
self.model = model
self.make = make
self.year = year
self.odometer_reading = 10
return None
def get_descriptive_name(self):
"""返回整洁的描述性信息"""
long_name = str(self.year) + ' ' + self.make + " " + self.model
return long_name.title()
# 2、通过方法修改属性的值
def update_odometer(self, mileage):
"""将里程表读数设置为指定的值
禁止将里程表读书往回调
"""
# 禁止任何人将里程表往回调
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
print("This car has " + str(self.odometer_reading) + " miles on it.")
else:
print("You can't roll back an odometer.")
new_car = Car('a4', 'audi', 2016)
print(new_car.get_descriptive_name())
new_car.update_odometer(25)
new_car.update_odometer(5)
2016 Audi A4
This car has 25 miles on it.
You can’t roll back an odometer.
(3)通过方法对属性的值进行递增
- 有时候需要将属性值递增特定的量,而不是将其设置为全新的值
class Car():
"""一次模拟汽车的简单尝试"""
def __init__(self, model, make, year):
"""初始化描述汽车的属性"""
self.model = model
self.make = make
self.year = year
self.odometer_reading = 2500
return None
def get_descriptive_name(self):
"""返回整洁的描述性信息"""
long_name = str(self.year) + ' ' + self.make + " " + self.model
return long_name.title()
# 3、通过方法对属性的值进行递增
def increment_odometer(self, miles):
"""将里程表读数增加指定的量"""
# 禁止任何人将里程表往回调
if miles > 0:
self.odometer_reading += miles
print("This car has " + str(self.odometer_reading) + " miles on it.")
else:
print("You can't roll back an odometer.")
return None
new_car = Car('a4', 'audi', 2016)
print(new_car.get_descriptive_name())
new_car.increment_odometer(100)
new_car.increment_odometer(-100)
2016 Audi A4
This car has 2600 miles on it.
You can’t roll back an odometer.
三:继承
- 编写类时,并非总是要从空白开始
- 如果你要编写的类是另一个现有类的特殊版本,可使用继承
- 一个类继承另一个类时,它将自动获得另一个类的所有属性和方法
- 原有的类称为父类,而新类称为子类
- 子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法
1、子类的方法 init()
- 创建子类的实例时,Python首先完成的任务是给父类的所有值赋值
- 子类的方法 init()需要父类施以援手
# 父类也称超类(superclass)
class Car():
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0
return None
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.")
return None
def update_odometer(self, mileage):
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer.")
return None
def increment_odometer(self, miles):
if miles > 0:
self.odometer += miles
else:
print("You can't roll back an odometer.")
return None
# 定义子类ElectricCar
class ElectricCar(Car):
"""电动汽车的独特之处"""
# 接受Car实例所需的信息
def __init__(self, make, model, year):
"""初始化父类的属性"""
# 将父类和子类关联起来
# 调用父类的方法__init__(),让子类实例包含父类的所有属性
super().__init__(make, model, year)
return None
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
2016 Tesla Model S
2、在Python 2.7 中的继承
- 在 Python 2.7 中使用继承时
- 务必在定义父类时在括号内指定object
- 函数 super() 需要两个实参
- 子类名和对象self
- 为了帮助Python将父类和子类关联起来,这些实参必不可少
3、给子类定义属性和方法
- 让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法
class Car():
- - snip - -
class ElectricCar(Car):
"""电动汽车的独特之处"""
def __init__(self, make, model, year):
"""
初始化父类的属性
初始化电动汽车特有的属性
"""
super().__init__(make, model, year)
# 添加电动汽车特有的属性电瓶容量
self.battery_size = 70
return None
# 添加子类的新方法
def describe_battery(self):
"""打印一条描述电瓶容量的信息"""
print("The car has a " + str(self.battery_size) + "-kWh battery.")
return None
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
# 调用子类的新方法
my_tesla.describe_battery()
2016 Tesla Model S
The car has a 70-kWh battery.
4、重写父类的方法
- 对于父类的方法
- 只要它不符合子类模拟的实物的行为,都可对其进行重写
- 可在子类定义一个这样的方法
- 即它与要重写的父类方法同名
- Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法
- 假设Car类有一个名为 fill_gas_tank() 的方法,它对电动汽车毫无意义,因此你可能想重写它
class Car():
- - snip - -
class ElectricCar(Car):
- - snip - -
# 重写父类的方法
def fill_gas_tank(self):
"""电动汽车没有油箱"""
print("The car doesn't need a gas tank.")
return None
5、将实例用作属性
- 使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多
- 属性和方法清单以及文件都越来越长
- 需要将类的一部分作为一个独立的类提取出来
- 将大型类拆分成多个协同工作的小类
class Car():
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0
return None
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.")
return None
def update_odometer(self, mileage):
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer.")
return None
def increment_odometer(self, miles):
if miles > 0:
self.odometer += miles
else:
print("You can't roll back an odometer.")
return None
class Battery():
"""电动汽车电瓶容量"""
def __init__(self, battery_size=80):
self.battery_size = battery_size
return None
def describe_battery(self):
"""描述电瓶容量"""
print("The car has a " + str(self.battery_size) + "-kWh battery.")
return None
def get_range(self):
"""指出电瓶的续航能力"""
if self.battery_size > 50 and self.battery_size < 80:
range = 250
elif self.battery_size > 80 and self.battery_size < 100:
range = 400
elif self.battery_size > 100:
range = 500
message = "This car can go approximately " + str(range) + " miles on a full charge."
print(message)
class ElectricCar(Car):
"""电动汽车的独特之处"""
def __init__(self, make, model, year):
"""
初始化父类的属性
初始化电动汽车特有的属性
"""
super().__init__(make, model, year)
# 添加电动汽车特有的属性电瓶容量
# 给Battery一个初始量
self.battery_size = Battery()
return None
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
# 实例用作属性的重新赋值
my_tesla.battery_size = Battery(51000)
my_tesla.battery_size.describe_battery()
my_tesla.battery_size.get_range()
2016 Tesla Model S
The car has a 51000-kWh battery.
This car can go approximately 500 miles on a full charge.
四:导入类
- 不断地给类添加功能,文件可能变得很长,即便妥善地使用了继承亦如此
- Python允许将类存储在模块中,然后在主程序中导入所需的模块
1、导入单个类
- 导入类是一种有效的编程方式
- 通过将这个类移到一个模块中,并导入该模块,依然可以使用其所有功能
- 导入单个类,可使用该语法
- from module_name import class_name
2、在一个模块中存储多个类
- 虽然同一个模块中的类之间应存在某种相关性,但可根据需要在一个模块中存储任意数量的类
3、从一个模块中导入多个类
- 可根据需要在程序文件中导入多各类
- 导入多个类,可使用该语法
- from module_name import class_name, class_other_name
4、导入整个模块
- 可以导入整个模块,再使用句点表示法访问需要的类
- 导入整个模块,可使用该语法
- import module_name
5、导入模块中的所有类
- 这种导入方式没有明确地指出使用了模块中的哪些类,还可能引发名称方面的困惑
- 导入模块中的所有类,可使用该语法
- from module_name import *
6、在一个模块中导入另一个模块
- 有时候,需要将类分散到多个模块中,以免模块太大,或者同一个模块中存储不相关的类
- 将类存储在多个模块中时,你可能会发现一个模块中的类依赖于另一个模块中的类
- 可在前一个模块中导入必要的类
7、自定义工作流程
- 先尽可能在一个文件中完成所有工作,确定一切都能正常运行后,再将类移到独立的模块中
- 如果你喜欢模块和文件的交互方式,可在项目开始时就尝试将类存储到模块中
五:Python标准库
- Python 标准库是一组模块,安装 Python 都包含它
- 可使用标准库中的任何函数和类,为此只需在程序开头包含一条简单的 import 语句
六:类编码风格
- 类名应采用驼峰命名法
- 类名中的每个单词的首字母都大写,而不使用下划线
- 实例名和模块都采用小写格式,并在单词之间加上下划线
- 对于每个类,都应紧跟在类定义后面包含一个文档字符串
- 简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式约定
- 对于每个模块,都应紧跟在模块后面包含一个文档字符串
- 对其中的类可用于做什么进行描述
- 可使用空行来组织代码,但不要滥用
- 在类中,可使用一个空行来分隔方法
- 在模块中,可使用两个空行来分隔类
- 需要同时导入标准库中模块和你编写的模块时
- 先编写导入标准库模块的 import 语句
- 再添加一个空行
- 然后导入自己编写的模块 import 语句
- 在包含多条 import 语句的程序中,这种做法让人更容易明白程序使用的各个模块都来自何方
七:小结
- 如何编写类
- 如何使用属性在类中存储信息,以及如何编写方法,以让类具备所需的行为
- 如何编写方法__init__(),以便根据类创建包含所需属性的实例
- 如何修改实例的属性
- 直接修改
- 通过方法修改
- 使用继承可简化相关类的创建工作
- 将一个类的实例用作另一个类的属性可让类更简洁
- 通过将类存储在模块中,并在需要使用这些类的文件中导入它们,可让项目组织有序
- 学习了 Python 标准库
- 学习了编写类时应遵循的 Python 约定