目录
0、复习
二进制文件的读写,不需要指定encoding参数。
基础班考试:单选、多选、代码题。
while True: pass 使用场景:在书写代码的时候,不确定循环一共执行多少次,即不知道循环在什么时候结束(具体结束的时间),在运行代码的过程中才能确定,在代码中进行if判断,如果条件不满足,就使用break终止循环。
my_list = list() # 创建列表类对象
my_dict = dict() # 创建字典类对象
my_tuple = tuple() # 创建元组类对象class 类名(object):
def 函数名(self):
pass
1、类外部添加和获取对象属性
每个对象,会保存自己的属性值,不同对象的属性值之间没有关联。
class Dog(object):
def play(self):
print('小狗快乐的拆家中...')
# 创建对象
dog1 = Dog()
dog1.play() # 小狗快乐的拆家中...
# 给对象添加属性:对象.属性名 = 属性值
dog1.name = '大黄' # 给dog对象添加name属性,属性值是大黄
dog1.age = 2 # 给dog对象添加age属性,属性值是2
# 获取对象的属性值:对象.属性名
print(dog1.name) # 大黄
print(dog1.age) # 2
# 修改属性值和添加一样,存在就是修改;不存在就是添加
dog1.age = 3 # age属性已经存在,所以是修改属性值
print(dog1.age) # 3
dog2 = Dog() # 新创建一个对象dog2
dog2.name = '小白'
print(dog2.name) # 小白
2、类内部操作属性
self 就是一个形参的名字,可以写成其他的形参名,一般不修改这个名字,默认是self。
class Dog(object):
# self作为类中方法的第一个形参,在通过对象调用方法的时候,不需要手动地传递实参值,
# 是python解释器自动将调用该方法的对象传递给self, 所以self这个形参代表的是对象
def play(self):
print(f'self: {id(self)}')
print(f'小狗 {self.name} 在快乐地拆家中...')
# 创建对象
dog1 = Dog()
dog1.name = '大黄'
print(f"dog : {id(dog1)}")
dog1.play()
print('-------------------------')
dog2 = Dog()
dog2.name = '小白'
print(f"dog2: {id(dog2)}")
dog2.play()
3、魔法方法
在 python 的类中有一类方法,这类方法以`两个下划线开头`和`两个下划线结尾`,并且在`满足某个特定条件的情况下,会自动调用`。这类方法,称为魔法方法。
如何学习魔法方法:1.魔法方法在什么情况下会自动调用;2.这个魔法方法有什么作用;3.这个魔法方法有哪些注意事项。
3.1、__init__()
[掌握]
调用时机:在创建对象之后,会立即调用。类似于Java中的构造方法。
作用:
1.用来给对象添加属性,给对象属性一个初始值(构造函数);
2.代码的业务需求,每创建一个对象,都需要执行的代码可以写在`__init__ `中。注意点:如果`__init__`方法中,有除了 self 之外的形参,那么在创建的对象的时候,需要给额外的形参传递实参值`类名(实参)`。
3.1.1、单参__init__()
class Dog(object):
def __init__(self): # self是对象
print('我是__init__方法,我被调用了!')
# 对象.属性名 = 属性值
self.name = '小狗'
# 创建对象
Dog() # 我是__init__方法,我被调用了!
dog1 = Dog() # 我是__init__方法,我被调用了!
print(dog1.name) # 小狗
dog2 = Dog() # 我是__init__方法,我被调用了!
print(dog2.name) # 小狗
3.1.2、多参__init__()
class Dog(object):
def __init__(self, name): # self是对象
print('我是__init__方法,我被调用了!')
# 对象.属性名 = 属性值
self.name = name
def play(self):
print(f"小狗{self.name}快乐地拆家中...")
# 创建对象 类名(实参值)
dog1 = Dog('大黄') # 我是__init__方法,我被调用了!
print(dog1.name) # 大黄
dog1.play() # 小狗大黄快乐地拆家中...
dog2 = Dog('小白') # 我是__init__方法,我被调用了!
print(dog2.name) # 小白
dog2.play() # 小狗小白快乐地拆家中...
3.2、__str__()
[掌握]
调用时机:
1.`print(对象)`,会自动调用`__str__`方法,打印输出的结果是`__str__`方法的返回值;
2.`str(对象)`类型转换,将自定义对象转换为字符串的时候,会自动调用。应用:
1.打印对象的时候,输出一些属性信息;
2.需要将对象转换为字符串类型的时候。注意点:`方法必须返回一个字符串`,只有 self 一个参数。
class Dog(object):
def __init__(self, name, age):
# 添加属性
self.name = name
self.age = age
def __str__(self):
print('我是__str__, 我被调用了...')
# 必须返回一个字符串
return f"小狗的名字是{self.name}、年龄是{self.age}。"
# 创建对象
dog = Dog('大黄', 2)
print(dog) # 没有定义__str__方法,print(对象)默认输出对象的引用地址 # <__main__.Dog object at 0x0000020F33D574C0>
str_dog = str(dog) # 没有定义__str__方法,类型转换,赋值的也是引用地址
print(str_dog) # <__main__.Dog object at 0x0000020F33D574C0>
3.3、__del__()
[理解]
__del__()析构函数。调用时机:对象在内存中被销毁删除的时候(引用计数为 0)会自动调用__del__方法。
1.程序代码运行结束,在程序运行过程中,创建的所有对象和变量都会被删除销毁;
2.使用`del 变量`,将这个对象的引用计数变为 0。会自动调用 __del__ 方法。应用场景:对象被删除销毁的时候,要书写的代码可以写在`__del__`中,一般很少使用。
引用计数:是python内存管理的一种机制,是指一块内存,有多少个变量在引用:
1. 当一个变量,引用一块内存的时候,引用计数加 1;
2. 当删除一个变量,或者这个变量不再引用这块内存,引用计数减 1;
3. 当内存的引用计数变为 0 的时候,这块内存被删除,内存中的数据被销毁。my_list = [1, 2] # 1
my_list1 = my_list # 2
del my_list # 1
del my_list1 # 0
class Dog(object):
def __init__(self, name, age):
# 添加属性
self.name = name
self.age = age
def __str__(self):
# 必须返回一个字符串
return f"小狗的名字是{self.name}, 年龄是{self.age}!"
def __del__(self):
print(f'我是__del__ 方法, 我被调用了, {self.name}被销毁了...')
# 创建一个对象
# dog = Dog('大黄', 2)
# dog1 = Dog('小白', 1)
dog = Dog('小花', 3) # 小花 引用计数为1
dog2 = dog # 小花 引用计数2
print('第一次删除之前。')
del dog # dog变量不能使用了, 小花对象引用计数变为1
print('第一次删除之后。')
print('第二次删除之前。')
del dog2 # dog2变量不能使用, 小花对象的引用计数变为0, 会立即__del__方法
print('第二次删除之后。')
4、案例:烤地瓜
封装的小套路:
1.根据文字的描述信息确定对象,对象有什么,就是属性;
2.根据文字的描述信息,对象能干什么,就是方法;
3.根据文字的描述信息,确定方法怎么书写。
烤地瓜规则:
- 地瓜有自己的状态,默认是生的,地瓜可以进行烧烤。
- 地瓜有自己烧烤的总时间,由每次烧烤的时间累加得出。
- 地瓜烧烤时,需要提供本次烧烤的时间。
- 地瓜烧烤时,地瓜状态随着烧烤总时间的变化而改变:[0,3) 生的、[3,6) 半生不熟、[6,8) 熟了、>=8 烤糊了。
- 输出地瓜信息时,可以显示地瓜的状态和烧烤的总时间。
类名:地瓜类 Potato
属性:
状态 status='生的'
烧烤总时间 total_time = 0
方法:
def cook(self, 烧烤时间):
计算烧烤的总时间
修改地瓜的状态的
pass
输出信息 __str__()
定义属性 __init__()
class Potato(object):
def __init__(self):
self.status = '生的'
self.total_time = 0
def cook(self, time):
# 计算总时间
self.total_time += time
# 修改地瓜的状态
if self.total_time < 3:
self.status = '生的'
elif self.total_time < 6:
self.status = '半生不熟的'
elif self.total_time < 8:
self.status = '熟了'
else:
self.status = '烤糊了'
def __str__(self):
return f"地瓜的状态<<{self.status}>>, 烧烤总时间为<{self.total_time}>"
# 创建对象
potato = Potato()
print(potato) # 地瓜的状态<<生的>>, 烧烤总时间为<0>
potato.cook(4)
print(potato) # 地瓜的状态<<半生不熟的>>, 烧烤总时间为<4>
potato.cook(3)
print(potato) # 地瓜的状态<<熟了>>, 烧烤总时间为<7>
烤地瓜-调料版
属性:调料: name_list = []
方法:添加调料 add()
class Potato(object):
def __init__(self):
self.status = '生的'
self.total_time = 0
self.name_list = [] # 保存调料的列表
def cook(self, time):
# 计算总时间
self.total_time += time
# 修改地瓜的状态
if self.total_time < 3:
self.status = '生的'
elif self.total_time < 6:
self.status = '半生不熟的'
elif self.total_time < 8:
self.status = '熟了'
else:
self.status = '烤糊了'
def __str__(self):
# buf_list = str(self.name_list) # str([]) ===> '[]'
# buf_list = buf_list.replace('[', '')
# 字符串.join(列表):将字符串添加到列表中的每个元素之间,组成新的字符串。
buf = ','.join(self.name_list) # 将列表中的字符串组成一个大的字符串
if self.name_list:
return f"地瓜的状态<<{self.status}>>, 烧烤总时间为<{self.total_time}>, 已添加的调料有: {buf}!"
else:
return f"地瓜的状态<<{self.status}>>, 烧烤总时间为<{self.total_time}>, 还没有添加调料!"
def add(self, name): # 添加调料的方法
self.name_list.append(name)
# 创建对象
potato = Potato()
print(potato) # 地瓜的状态<<生的>>, 烧烤总时间为<0>, 还没有添加调料!
potato.add('油')
potato.cook(4)
potato.add('辣椒面')
print(potato) # 地瓜的状态<<半生不熟的>>, 烧烤总时间为<4>, 已添加的调料有: 油,辣椒面!
potato.cook(3)
potato.add('孜然')
print(potato) # 地瓜的状态<<熟了>>, 烧烤总时间为<7>, 已添加的调料有: 油,辣椒面,孜然!
5、补充:查看对象的引用计数
import sys
class Dog(object):
pass
dog = Dog() # 1
print(sys.getrefcount(dog)) # 2 显示的时候,会比实际的多一个
dog1 = dog # 2
print(sys.getrefcount(dog)) # 3 显示的时候,会比实际的多一个
del dog # 1
print(sys.getrefcount(dog1)) # 2 显示的时候,会比实际的多一个
6、案例:搬家具
搬家具规则:
- 家具分不同的类型,并占用不同的面积。
- 输出家具信息时,显示家具的类型和家具占用的面积。
- 房子有自己的地址和占用的面积。
- 房子可以添加家具,如果房子的剩余面积可以容纳家具,则提示家具添加成功;否则提示添加失败。
- 输出房子信息时,可以显示房子的地址、占地面积、剩余面积。
类名:家具类 Furniture
属性:
类型 name
面积 area
方法:
输出家具信息 __str__
定义属性 __init__
--------------------------------------------
类名:房子类 House
属性:
地址 address
面积 h_area
家具列表 furniture_list = []
方法:
添加家具 add_furniture()
输出房子信息 __str__
定义属性 __init__
# 定义家具类 Furniture 类
class Furniture(object):
def __init__(self, name, area):
# 类型
self.name = name
# 面积
self.area = area
def __str__(self):
return f'家具的类型<{self.name}>, 占地面积<{self.area}>平。'
# 定义房子类
class House(object):
def __init__(self, address, area):
self.address = address
self.h_area = area
self.furniture_list = []
self.free_area = area # 房子的剩余面积
def add_furniture(self, obj_furniture):
""" 添加家具 obj_furniture: 家具类的对象"""
if self.free_area > obj_furniture.area:
self.furniture_list.append(obj_furniture)
# 修改剩余面积
self.free_area -= obj_furniture.area
print(f'家具<{obj_furniture.name}>添加成功。')
else:
print('添加失败,换个大房子吧。')
def __str__(self):
return f"房子的地址为<{self.address}>, 占地面积为<{self.h_area}>, 剩余面积为{self.free_area}。"
# 创建家具对象
bed = Furniture('豪华双人床', 15)
print(bed)
# 创建一个房子类对象
house = House('意大利农场', 100)
print(house)
house.add_furniture(bed)
print(house)
打印家具信息
# 定义家具类 Furniture 类
class Furniture(object):
def __init__(self, name, area):
# 类型
self.name = name
# 面积
self.area = area
def __str__(self):
return f'家具的类型<{self.name}>, 占地面积<{self.area}>平。'
# 定义房子类
class House(object):
def __init__(self, address, area):
self.address = address
self.h_area = area
self.furniture_list = []
self.free_area = area # 房子的剩余面积
def add_furniture(self, obj_furniture):
""" 添加家具 obj_furniture: 家具类的对象"""
if self.free_area > obj_furniture.area:
self.furniture_list.append(obj_furniture)
# 修改剩余面积
self.free_area -= obj_furniture.area
print(f'家具<{obj_furniture.name}>添加成功。')
else:
print('添加失败,换个大房子吧。')
def __str__(self):
# 自定义家具类,将该类的对象添加到列表中(容器), 直接打印列表,显示的是自定义对象的引用地址
# [家具对象, 家具对象, ... ] ---> [家具类型, 家具类型, ...]
if self.furniture_list:
buf_list = [obj.name for obj in self.furniture_list]
return f"房子的地址为<{self.address}>, 占地面积为<{self.h_area}>, 剩余面积为{self.free_area}, " \
f"家具有<{','.join(buf_list)}>。"
else:
return f"房子的地址为<{self.address}>, 占地面积为<{self.h_area}>, 剩余面积为{self.free_area}, " \
f"还没有购买家具。"
# 创建家具对象
bed = Furniture('豪华双人床', 15)
print(bed)
# 创建一个房子类对象
house = House('意大利农场', 100)
print(house)
house.add_furniture(bed)
print(house)
sofa = Furniture('柔软大沙发', 8)
print(sofa)
house.add_furniture(sofa)
print(house)
7、区分__str__()与__repr__()
my_list = ['hello', 'python', 'cpp'] # 列表中存储了三个字符串对象
print(my_list) # ['hello', 'python', 'cpp']
class Dog(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'{self.name}, {self.age}'
def __repr__(self):
"""repr方法和str方法,非常类似,也是必须返回一个字符串!"""
return f"{self.name}"
# 将三个Dog类的对象添加到列表中
my_list1 = [Dog('大黄', 2), Dog('小白', 4), Dog('小花', 6)]
print(my_list1) # [大黄, 小白, 小花]
dog = Dog('大黄', 2)
print(dog) # __str__ 大黄, 2
8、附录:魔方方法(__str__对比__repr__)
python中的魔法方法:`__str__` 和`__repr__`.pdf
8.1、引言
在学习⾯向对象的时候,我们知道在 python 中有⼀类特殊的⽅法,叫做魔法⽅法,这种⽅法的特点如下:
1. ⽅法定义的时候以两个下划线开头和两个下划线结尾:如__init__ 、 __str__ 和__repr__;
2. 这类⽅法⼀般不需要我们⼿动调⽤,在满⾜某个条件的时候会⾃动调⽤,这个满⾜的条件我们可以称为调⽤时机。在Python中有两个魔法⽅法都是⽤来描述对象信息的,__str__和__repr__,那为什么要定义两个这样的⽅法呢,其实是它们设计的⽬的是不⼀样的:
1.__repr__的⽬标是准确性,或者说,__repr__的结果是让解释器⽤的;
2.__str__的⽬标是可读性,或者说,__str__的结果是让⼈看的。
8.2、分析
那下边,我们详细地来看⼀下,他们的⽤法:在不重写__str__和__repr__的情况下,打印对象的 输出结果不太友好,是对象的内存地址,即 id的结果。
class Person(object): # 定义 Person 类
def __init__(self, name):
self.name = name
p = Person("isaac")
# 以下为测试输出的结果:
print(p) # <__main__.Person object at 0x0000016FB4E0E220>
p # <__main__.Person object at 0x0000016FB4E0E220>
p.__str__() # '<__main__.Person object at 0x0000016FB4E0E220>'
p.__repr__() # '<__main__.Person object at 0x0000016FB4E0E220>'
这样的输出结果,并不是我们想要的结果,此时我们重写__str__和__repr__⽅法。
8.2.1、重写__str__()方法
class Person(object): # 定义 Person 类
def __init__(self, name):
self.name = name
def __str__(self):
return "__str__ ⽅法 " + self.name
p = Person("isaac")
# 以下为测试输出的结果:
print(p)
str(p)
f"{p}"
p.__str__()
p
此时我们发现在使⽤ print 打印对象、对象的格式化输出以及调⽤str⽅法,调⽤的都是__str__⽅法。但在交互环境下,直接输出对象的时候,没有调⽤__str__⽅法,输出的结果仍然是id的结果。
8.2.2、重写__repr__()方法
class Person(object): # 定义 Person 类:
def __init__(self, name):
self.name = name
def __str__(self):
return "__str__ ⽅法 " + self.name
def __repr__(self):
return "__repr__ ⽅法 " + self.name
p = Person("isaac")
# 以下为测试输出的结果:
p
p.__repr__()
print(p)
通过简单的对⽐,我们发现,在交互环境下,直接输出对象,调⽤的__repr__⽅法。
另外还需要注意的是,如果将对象放在容器中进⾏输出,调⽤的是__repr__⽅法。
8.3、总结
Python 中的__str__和__repr__⽅法都是⽤来显示的,即描述对象信息的。
1.__str__的⽬标是可读性,或者说,__str__的结果是让⼈看的。主要⽤来打印,即print操作,
2.__repr__的⽬标是准确性,或者说,__repr__的结果是让解释器⽤的。__repr__⽤于交互模式下提示回应,
3.如果没有重写__str__⽅法,但重写了__repr__⽅法时,所有调⽤__str__的时机都会调⽤__repr__⽅法。