目录
8.1 改变对象的字符串显示
__str__()
__repr__()
Python 魔术(特殊)方法 https://blog.csdn.net/qq_42105144/article/details/103274416
8.2 自定义字符串的格式化
__format__()
8.3 让对象支持上下文管理协议
给类添加 __enter__() 和 __exit__() 方法
上下文管理器的行为由两种魔术方法确定:
object.__enter__(self) 是在with语句创建的代码块的开头应执行的操作。 with 语句将会绑定这个方法的返回值到 as 子句中指定的目标。
object.__exit__(self, exc_type, exc_value, traceback) 是在代码块已执行(或终止)之后应执行的操作。它可用于处理异常,执行清理或在执行块中的操作后立即执行总是执行的操作。
编写上下文管理器的主要原理是你的代码会放到 with
语句块中执行。 当出现 with
语句的时候,对象的 __enter__()
方法被触发, 它返回的值(如果有的话)会被赋值给 as
声明的变量。然后,with
语句块里面的代码开始执行。 最后,__exit__()
方法被触发进行清理工作。
不管 with
代码块中发生什么,上面的控制流都会执行完,就算代码块中发生了异常也是一样的(发生异常就直接转去执行__exit__ )。 事实上,__exit__()
方法的第三个参数包含了异常类型、异常值和追溯信息(如果有的话)。 __exit__()
方法能自己决定怎样利用这个异常信息,或者忽略它并返回一个None值。 如果 __exit__()
返回 True
,那么异常会被清空,就好像什么都没发生一样, with
语句后面的程序继续在正常执行。
8.4 创建大量对象时节省内存方法
对于主要是用来作为简单的数据结构的类而言,可以通过给类添加 __slots__
属性来极大的减少实例所占的内存,如下一个作为日期的数据结构的类:
class Date:
__slots__ = ['year', 'month', 'day']
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
不使用slots直接存储一个Date实例, 在64位的Python上面要占用428字节,而如果使用了slots,内存占用下降到156字节。 如果程序中需要同时创建大量的日期实例,那么这个就能极大的减小内存使用量了。
当定义 __slots__
后,Python就会为实例使用一种更加紧凑的内部表示。 实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。 在 __slots__
中列出的属性名在内部被映射到这个数组的指定小标上。 使用slots一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在 __slots__
中定义的那些属性名。
8.5 在类中封装属性名
两种不同的编码约定(单下划线和双下划线)来命名私有属性,限制访问
8.6 创建可管理的属性
怎么对类实例的属性增加除访问与修改之外的其他处理逻辑,比如类型检查或合法性验证?
@property
https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p06_create_managed_attributes.html
https://www.liaoxuefeng.com/wiki/1016959663602400/1017502538658208
8.7 调用父类方法
super()
非常全面:https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p07_calling_method_on_parent_class.html
8.8 子类中扩展property
@property
https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p08_extending_property_in_subclass.html
8.9 创建新的类或实例属性
一个描述器就是一个实现了三个核心的属性访问操作(get, set, delete)的类, 分别为 __get__()
、__set__()
和 __delete__()
这三个特殊的方法。
8.10 使用延迟计算属性
@property
8.11 简化数据结构的初始化
当需要使用大量很小的数据结构类的时候, 相比手工一个个定义 __init__()
方法而已,使用在基类中定义公用的 __init__() 函数这种方式可以大大简化代码,示例:
'''在基类结构中定义公用的 __init__() 函数
'''
>>> class Structure:
#属性列表
_fields = []
def __init__(self,*args):
if len(args) != len(self._fields):
raise TypeErroe(f'Expected {len(self._fields)} attrs!')
for f,v in zip(self._fields,args):
#设置属性值
setattr(self,f,v)
'''子类点'''
>>> class Point(Structure):
_fields = ['x','y']
>>> a = Point(10,15)
>>> a = Point(10,2,3)
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
a = Point(10,2,3)
File "<pyshell#1>", line 5, in __init__
raise TypeErroe(f'Expected {len(self._fields)} attrs!')
NameError: name 'TypeErroe' is not defined
'''子类圆'''
>>> class Circle(Structure):
_fields = ['radius','x','y']
>>> c = Circle(3,10,15)
>>> c.radius
3
8.12 定义接口或者抽象基类
抽象基类的一个主要用途是在代码中检查某些类是否为特定类型,实现了特定接口
8.13 实现数据模型的类型约束
本节使用了很多高级技术,包括描述器、混入类、super()
的使用、类装饰器和元类。