python基础07-面向对象1:类和对象、魔方方法
面向对象1:类和对象、魔方方法
一、面向对象编程
1. 面向过程
- 把编程任务划分成一个一个的步骤,然后按照步骤分别去执行。
- 以 小明起床上学 为例,面向过程则做了如下 4 件事:起床、穿衣、洗漱、上学,这 4 个过程的顺序很重要,须一个一个地实现。
- 对于面向过程的思想: 需要实现一个功能的时候,看重的是开发的步骤和过程,每一个步骤都需要自己亲力亲为。
- 以 吃饭 举例:买菜、洗菜、煮饭、切菜……
- 面向过程编程适合开发中小型项目
2. 面向对象
把构成问题事务分解成各个
对象
,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题步骤中的行为。
- 以 小明起床上学 为例,我们关心的是小明这个对象,而不是 起床、穿衣、洗漱、上学 这 4 个过程,这 4 个过程是小明这个对象的一部分,只是其中的一种行为,而且对于行为的顺序没有强制要求。
对于面向对象的思想:当需要实现一个功能的时候,看重的并不是过程和步骤,而是关心的是对象,对象能做啥
- 以 吃饭 举例:找 饭馆 对象,饭馆提供菜和饭,不关心做菜和煮饭的内部过程
面向对象编程适合开发大型项目
二、类和对象
1. 类
- 很多事物存在 相同的属性和行为(也叫方法),比如人有姓名年龄,能吃饭睡觉等等。
- 描述 具有共同特征的事物的 抽象,称为 类 (class)。
- 类包含两个组成部分:
- 属性:比如姓名,年龄,身高,肤色等
- 方法:比如吃饭,睡觉,飞行,歌唱等
2. 对象
- 对象 是 类 的实例,是具体的实体
3. 类和对象的关系
- 类是对象的模板(不占内存空间),对象是类的实例(占内存空间)。
- 类相当于图纸,对象相当于根据图纸制造的实物。
- 每个对象必须有一个对应的类。
三、定义类
1. 定义类
定义一个类,格式如下:
#class 类名:
#class 类名(): # 前2个旧式写法(经典类),不推荐
class 类名(object):
方法列表(不是真的是列表,只是多个函数的定义)
说明:
- 定义类时有 2 种形式:经典类和新式类,前两行注释部分则为经典类,推荐写新式类
- object 是Python 里所有类的最顶级父类
- 类名 的命名规则按照 驼峰命名法
2. 定义方法
定义方法(方法也叫实例方法)格式为:
class 类名(object):
def 方法名(self):
...
- 方法的格式和函数类似,也可以设置参数和返回值,但是 需要设置第一个参数为
self
- 编辑器一般会自动生成
self
,后面会讲解self
的作用
示例代码:
class Dog(object):
# self自动添加的,后面有讲解
# 定义在类里面的函数,叫方法
def eat(self):
print('吃骨头')
def drink(self):
print('喝水')
四、创建对象和调用方法
1. 创建对象
Python中,可以根据已经定义的类去创建示例对象。
创建对象的格式为:
对象变量名 = 类名()
示例代码:
# 定义类型
class Dog(object):
# 定义在类里面的函数,叫方法
def eat(self):
print('吃骨头')
def drink(self):
print('喝水')
# 创建对象格式:实例对象变量名 = 类名()
dog1 = Dog()
2. 调用方法
调用方法的格式为:
对象变量名.方法名()
- 注意:虽然定义方法时设置第一个参数
self
,但是 调用方法时不要传递对应self
的参数,解释器自动处理
示例代码:
class Dog(object):
# 定义在类里面的函数,叫方法
def eat(self):
print('吃骨头')
def drink(self):
print('喝水')
# 创建对象格式:实例对象变量名 = 类名()
dog1 = Dog()
# 对象变量.方法名字(), self不用处理
dog1.eat()
dog1.drink()
3. 创建多个对象
- 类作为对象的模具,根据类可以创建多个对象
class Dog(object):
# 定义在类里面的函数,叫方法
def eat(self):
print('吃骨头')
def drink(self):
print('喝水')
# 对象1 = 类名()
# 对象2 = 类名()
# 对象1
dog1 = Dog()
dog1.eat()
dog1.drink()
# 对象2
dog2 = Dog()
dog2.eat()
dog2.drink()
五、添加和使用属性
1. 添加和使用属性
- 对象既然有实例方法,也有自己的属性。
- 定义/添加属性格式:
对象变量名.属性名 = 数据
- 属性和变量类似,首次赋值时会定义属性,再次赋值改变属性
示例代码:
class Dog(object):
# 定义在类里面的函数,叫方法
def eat(self):
print('吃骨头')
def drink(self):
print('喝水')
# 1. 创建对象变量
dog1 = Dog()
# 2. 对象变量.属性 = 数值
dog1.age = 3
# 打印属性
print(dog1.age)
# 修改属性
dog1.age = 2
print(dog1.age)
六、self的作用
1. self是什么
在Python类中规定,实现方法的第一个参数是实例对象本身,并且约定俗成,把其名字写为self。
某个对象调用其方法时,Python解释器会
自动把这个对象
作为第一个参数传递给方法
- 通俗理解:哪个对象调用方法,方法中self就是这个对象
示例代码:
# 定义类
class Dog(object):
def print_info(self):
print('测试self', id(self))
# 创建对象1
dog1 = Dog()
# 打印dog1的id
print("调用方法前", id(dog1))
# dog1调用print_info, print_info的self就是dog1
# 底层调用:print_info(dog1), 解释器自动把dog1传给方法中的self
dog1.print_info()
print("调用方法后", id(dog1))
print('='*30)
# 创建对象2
dog2 = Dog()
print("调用方法前", id(dog2))
dog2.print_info()
print("调用方法后", id(dog2))
运行结果:
调用方法前 4400762352
测试self 4400762352
调用方法后 4400762352
==============================
调用方法前 4400762544
测试self 4400762544
调用方法后 4400762544
2. self的作用
- 在方法中使用
self
,可以获取到调用当前方法的对象,进而获取到该对象的属性和方法 - self作用:为了区分不同对象的属性和方法
# 定义类
class Dog(object):
def print_info(self):
print(self.type)
# 创建对象,实例化对象
dog1 = Dog()
# 添加属性
dog1.type = '大黄狗'
# 直接调用方法
dog1.print_info()
print('='*30)
dog2 = Dog()
dog2.type = '旺财'
dog2.print_info()
七、魔法方法:init()
1. 魔法方法
- 在Python中,所有以
__
双下划线包起来的方法,都统称为 Magic Method ,中文称 魔法方法 - 魔法方法是系统提供好的方法名字,用户需重新实现它
- 魔法方法一般情况下无需手动调用,在合适时候自动会调用
2. __init__()
基本使用
__init__()
方法叫做 对象的初始化方法,在 创建一个对象后会被自动调用,不需要手动调用__init__()
方法的作用:添加属性
示例代码:
"""
__init__方法:
1. 作用:添加属性
2. 特点:创建对象的时候,实例化对象,自动调用__init__方法
"""
class Dog(object):
def __init__(self):
"""初始化函数,添加属性"""
self.type = '大黄狗'
print('init方法调用了')
def print_info(self):
"""打印实例属性"""
print(self.type)
# 1. 创建对象,实例化对象,自动调用__init__方法
# 2. 尽管是自动调用方法,也是dog1自动调用方法,所以,self就是dog1
dog1 = Dog()
# 手动调用实例方法
dog1.print_info()
运行结果:
init方法调用了
大黄狗
3. __init__()
自定义参数
__init__(self)
除了默认参数self
,还可以设置任意个数的自定义参数,例如:__init__(self,x,y,z)
- init方法 设置的自定义参数必须和创建对象时传递的参数保持一致,例如:
对象变量名 = 类名(x,y,z)
- 开发者可以 设置自定义参数,为对象的默认属性提供 不同的初始值
示例代码:
class Dog(object):
def __init__(self, _type):
"""初始化函数,添加属性"""
self.type = _type
print('init方法调用了')
def print_info(self):
"""打印实例属性"""
print(self.type)
# 创建对象,实例化对象,自动调用__init__方法
# '旺财'传递给 _type 形参
dog1 = Dog('旺财')
# 直接调用方法
dog1.print_info()
print('='*30)
dog2 = Dog('来福')
dog2.print_info()
运行结果:
init方法调用了
旺财
==============================
init方法调用了
来福
八、魔法方法:str()
1. str()方法的使用
- 如果直接 print 打印对象,会看到创建出来的对象在内存中的地址
- 当使用
print(对象变量名)
输出对象的时候,只要类中定义了__str__()
方法,就会打印__str__()
方法返回值 __str__()
方法作用主要返回对象属性信息,print(对象变量名)
输出对象时直接输出__str__()
方法返回的描述信息__str__()
方法的返回值必须是 字符串类型
示例代码:
"""
__str__方法:
1. 返回值必须是字符串类型
2. print(对象变量名) 对象变量名的位置替换为__str__()方法返回值的内容
"""
class Dog(object):
# 添加属性,type, age
def __init__(self, _type, _age):
self.type = _type
self.age = _age
# 测试有定义 __str__ 方法,和没有定义,print(对象)的区别
def __str__(self):
return f'类型{self.type},年龄{self.age}'
# return "类型:%s, 年龄:%d" % (self.type, self.age)
# 创建对象
dog1 = Dog('大白狗', 3)
# 如果有__str__ 方法,dog1的位置替换为__str__()方法返回值的内容
print(dog1)
print('对象描述信息为:', dog1)
运行结果:
类型:大白狗, 年龄:3
对象描述信息为: 类型:大白狗, 年龄:3
九、魔法方法:__del__()
1. del()方法的使用
- 在对象的生命周期结束(对象销毁)时,
__del__()
方法会自动被调用,做一些清理工作
class Dog(object):
def __del__(self):
print('我悄悄地离开了')
# 设计一个函数,在函数内容创建对象
# 函数调用完毕,里面创建的对象,生命周期结束,自动调用__del__方法
def func():
dog1 = Dog()
print('函数调用前')
# 调用函数
func()
print('函数调用后')
运行结果:
函数调用前
我悄悄地离开了
函数调用后
应用:烤地瓜
1. 需求说明
烤地瓜规则:
- 地瓜有自己的状态,默认是生的,地瓜可以进行烧烤
- 地瓜有自己烧烤的总时间,由每次烧烤的时间累加得出
- 地瓜烧烤时,需要提供本次烧烤的时间
- 地瓜烧烤时,地瓜状态随着烧烤总时间的变化而改变:[0, 3) 生的、[3, 6) 半生不熟、[6, 8) 熟了、>=8 烤糊了
- 输出地瓜信息时,可以显示地瓜的状态和烧烤的总时间
2. 面向对象设计
2.1 地瓜类
- 使用
SweetPotato
类可以创建 地瓜对象 - 地瓜有两个属性:
- 状态 state:字符串
- 烧烤总时间 cooked_time:整数
- 定义cook方法, 提供参数time设置本次烧烤的时间
- 使用 本次烧烤时间 对 烧烤总时间 进行 累加
- 根据烧烤总时间, 设置地瓜的状态:
- [0, 3) -> 生的
- [3, 6) -> 半生不熟
- [6, 8) -> 熟了
- 大于等于8 -> 烤糊了
2.2 主程序逻辑
- 创建 地瓜对象
- 分多次 烧烤地瓜
- 每烧烤一次,输出地瓜信息
2.3 示例代码
2.3.1 步骤流程
"""
# SweetPotato 类的设计
地瓜有两个属性:
状态 state:字符串
烧烤总时间 cooked_time:整数
# 1. 定义__init__方法,添加2个属性
# 1.1 默认状态state是生的
# 1.2 默认时间cooked_time是0
# 2. 定义__str__方法
# 2.1 返回地瓜状态,烧烤总时间
# 3. 定义 cook 方法, 提供参数 time 设置 本次烧烤的时间
# 3.1 使用 本次烧烤时间 对 烧烤总时间 进行 累加
# 3.2 根据 烧烤总时间, 设置地瓜的状态:
[0, 3) -> 生的
[3, 6) -> 半生不熟
[6, 8) -> 熟了
大于等于8 -> 烤糊了
# 4. 主逻辑程序
# 4.1 创建 地瓜对象
# 4.2 分多次烧烤地瓜
# 4.3 每烧烤一次,输出地瓜信息
"""
2.3.2 定义地瓜类、通过__init__()方法
添加属性
class SweetPotato(object):
"""地瓜类"""
# 1. 定义__init__方法,添加2个属性
def __init__(self):
# 1.1 默认状态state是生的
self.state = '生的'
# 1.2 默认时间cooked_time是0
self.cooked_time = 0
2.3.3 定义__str__()
方法
# 2. 定义__str__方法
def __str__(self):
# 2.1 返回地瓜状态,烧烤总时间
return f'地瓜状态为:{self.state}, 烧烤总时间为:{self.cooked_time} 分钟'
2.3.4 定义”烤地瓜”方法
# 3. 定义 cook 方法, 提供参数 time 设置 本次烧烤的时间
def cook(self, time):
# 3.1 使用 本次烧烤时间 对 烧烤总时间 进行 累加
self.cooked_time += time
# 3.2 根据 烧烤总时间, 设置地瓜的状态:
if 0 <= self.cooked_time < 3:
self.state = '生的'
elif 3 <= self.cooked_time < 6:
self.state = '半生不熟'
elif 6 <= self.cooked_time < 8:
self.state = '熟了'
else:
self.state = '烤糊了'
2.3.5 测试代码
把上面 3 块代码合并为一个程序后,在代码的下面添加以下代码进行测试:
# 4.1 创建 地瓜对象
sp = SweetPotato()
print(sp) # 输出地瓜信息
# 4.2 分多次烧烤地瓜
sp.cook(2)
print(sp) # 4.3 每烧烤一次,输出地瓜信息
sp.cook(5)
print(sp)
sp.cook(2)
print(sp)
运行结果:
地瓜状态为:生的, 烧烤总时间为:0 分钟
地瓜状态为:生的, 烧烤总时间为:2 分钟
地瓜状态为:熟了, 烧烤总时间为:7 分钟
地瓜状态为:烤糊了, 烧烤总时间为:9 分钟
3. 拓展功能
3.1 烤地瓜需求拓展
- 地瓜可以添加佐料,如 盐、孜然、辣酱等
- 输出地瓜信息时,可以显示地瓜的状态、烧烤总时间、以及添加过的所有佐料
3.2 需求分析
- 每个地瓜记录自己的佐料,定义属性来记录
- 地瓜可以添加多个佐料,应该定义 容器类型属性 来记录添加的佐料
- 地瓜类 应该 定义方法来实现添加佐料的功能
3.3 示例代码
3.3.1 步骤流程
# 5. 拓展功能
# 5.1 添加属性 condiments, 列表类型,默认为空列表
# 5.2 修改 __str__ 返回信息,返回增加已添加的佐料信息
# 5.3 定义 add_condiments(self, temp), temp为添加什么佐料的参数
# 5.3.1 佐料列表追加元素
# 5.4 再次测试代码,添加佐料,重新打印信息
3.3.2 添加 condiments
属性
# 1. 定义__init__方法,添加3个属性
def __init__(self):
# 1.1 默认状态state是生的
self.state = '生的'
# 1.2 默认时间cooked_time是0
self.cooked_time = 0
# 5.1 添加属性 condiments, 列表类型,默认为空列表
self.condiments = []
3.3.3 修改__str__()
方法,在方法中使用condiments
属性显示已添加的佐料
# 2. 定义__str__方法
def __str__(self):
# 2.1 返回地瓜状态,烧烤总时间
# 5.2 修改 __str__ 返回信息,返回增加已添加的佐料信息
return f'地瓜状态为:{self.state}, 烧烤总时间为:{self.cooked_time} 分钟,包括的佐料为:{self.condiments}'
3.3.4 定义add_condiments()
方法
# 5.3 定义 add_condiments(self, temp), temp为添加什么佐料的参数
def add_condiment(self, temp):
# 5.3.1 佐料列表追加元素
self.condiments.append(temp)
3.3.5 再次测试
# 4.1 创建 地瓜对象
sp = SweetPotato()
print(sp) # 输出地瓜信息
# 4.2 分多次烧烤地瓜
sp.cook(2)
# 添加佐料
sp.add_condiment('番茄酱')
print(sp) # 4.3 每烧烤一次,输出地瓜信息
sp.cook(5)
sp.add_condiment('孜然')
print(sp)
sp.cook(2)
sp.add_condiment('烤肉')
print(sp)
运行结果:
地瓜状态为:生的, 烧烤总时间为:0 分钟,包括的佐料为:[]
地瓜状态为:生的, 烧烤总时间为:2 分钟,包括的佐料为:['番茄酱']
地瓜状态为:熟了, 烧烤总时间为:7 分钟,包括的佐料为:['番茄酱', '孜然']
地瓜状态为:烤糊了, 烧烤总时间为:9 分钟,包括的佐料为:['番茄酱', '孜然', '烤肉']
应用: 搬家具
1. 需求说明
搬家具规则:
- 家具分不同的类型,并占用不同的面积
- 输出家具信息时,显示家具的类型和家具占用的面积
- 房子有自己的地址和占用的面积
- 房子可以添加家具,如果房子的剩余面积可以容纳家具,则提示家具添加成功;否则提示添加失败
- 输出房子信息时,可以显示房子的地址、占地面积、剩余面积
2. 面向对象设计
2.1 家具类
使用
Item
类可以创建 家具对象家具有两个属性:
- 家具类型 type:字符串
- 家具面积 area:整数
实现
__str__
方法
- 显示家具的 type 和 area 属性
2.2 房子类
使用
Home
类可以创建 房子对象房子有三个属性:
- 地址 address:字符串
- 房子面积 area:整数
- 房子剩余面积 free_area:整数,默认为房子的面积
实现
__str__
方法
- 显示房子的 address 、area、free_area 属性
实现
add_item
方法,提供
item
参数来添加家具
- 如果
可以容纳家具:
- 打印添加家具的类型和面积
- **剩余面积** 减少
- 如果 不能容纳家具: 提示家具添加失败
2.3 主程序逻辑
- 创建 家具对象, 输出 家具信息
- 创建 房子对象, 输出 房子信息
- 房子添加家具, 输出 房子信息
2.4. 示例代码
"""
家具类 Item
# 1. 定义__init__方法,添加2个属性,需要2个形参 _type, _area
# 1.1 家具类型 type
# 1.2 家具面积 area
# 2. 实现__str__方法
# 2.1 返回家具类型和家具面积
房子类 Home
# 1. 定义__init__方法,添加3个属性,需要3个形参
# 1.1 地址 address
# 1.2 房子面积 area
# 1.3 房子剩余面积 free_area,默认为房子的面积
# 2. 实现__str__方法
# 2.1 返回房子地址、面积、剩余面积信息
# 3. 实现add_item方法,提供item参数来添加家具,item是对象
# 3.1 如果 房间的剩余面积 >= 家具的面积,可以容纳家具:
# 3.1.1 打印添加家具的类型和面积
# 3.1.2 剩余面积 减少
# 3.2 否则 不能容纳家具:提示家具添加失败
主程序逻辑:
# 1. 创建 家具对象, 输出 家具信息
# 2. 创建 房子对象, 输出 房子信息
# 3. 房子添加家具, 输出 房子信息
"""
class Item(object):
"""家具类"""
# 1. 定义__init__方法,添加2个属性,需要2个形参 _type, _area
def __init__(self, _type, _area):
# 1.1 地址 address
self.type = _type
# 1.2 家具面积 area
self.area = _area
# 2. 实现__str__方法
def __str__(self):
# 2.1 返回房子地址、面积、剩余面积信息
return f'家具类型为:{self.type}, 家具面积为:{self.area}'
class House(object):
"""房子类"""
# 1. 定义__init__方法,添加3个属性,需要3个形参
def __init__(self, _addr, _area, _free_area):
# 1.1 地址 address
self.address = _addr
# 1.2 房子面积 area
self.area = _area
# 1.3 房子剩余面积 free_area,默认为房子的面积
self.free_area = _free_area
# 2. 实现__str__方法
def __str__(self):
# 2.1 返回房子地址、面积、剩余面积信息
return f'房子地址:{self.address}, 房子面积:{self.area}, 房子剩余面积:{self.free_area}'
# 3. 实现add_item方法,提供item参数来添加家具,item是家具对象
def add_item(self, item):
# 3.1 如果 房间的剩余面积 >= 家具的面积,可以容纳家具:
if self.free_area > item.area:
# 3.1.1 打印添加家具的类型和面积
print(f'添加{item.type}成功了,家具占用面积为:{item.area}')
# 3.1.2 剩余面积 减少
self.free_area -= item.area
# 3.2 否则 不能容纳家具: 提示家具添加失败
else:
print('家具添加失败')
# 1. 创建 家具对象, 输出 家具信息
# 2. 创建 房子对象, 输出 房子信息
# 3. 房子添加家具, 输出 房子信息
h = House('北京', 70, 70) # 创建房间对象h
print(h) # 打印房子对象,获取House中__str__返回的信息
tv = Item('超大电视', 10) # 创建家具1
print(tv) # 打印家具对象,输出Item中__str__返回的信息
h.add_item(tv) # 房子添加家具
print(h) # 打印房子对象,获取House中__str__返回的信息
bed = Item('超大床', 20) # 创建家具2
print(bed) # 打印家具对象,输出Item中__str__返回的信息
h.add_item(bed) # 房子添加家具
print(h)
运行结果:
房子地址:北京, 房子面积:70, 房子剩余面积:70
家具类型为:超大电视, 家具面积为:10
添加超大电视成功了,家具占用面积为:10
房子地址:北京, 房子面积:70, 房子剩余面积:60
家具类型为:超大床, 家具面积为:20
添加超大床成功了,家具占用面积为:20
房子地址:北京, 房子面积:70, 房子剩余面积:40
版权声明:本博客所有文章除特殊声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明出处 caijinbo的博客!