对象表现形式
- repr()
从开发者的角度,背后是 _repr_() - str()
从用户的角度,背后是 _str_() - bytes()
从计算机的角度,背后是 _bytes_(),需要传入 encoding 参数。 - format()
背后是 _format_(),此外 _format_() 也在 str.format() 中被调用
向量类
from array import array # 实例与字节序列转换时使用
from math import hypot # 计算模
class Vector2d:
typecode = 'd' # 实例与字节序列转换时使用
def __init__(self, x, y):
self.x = float(x) # 尽早捕捉错误,以防构建实例时传入错误参数
self.y = float(y)
def __iter__(self): # 实现拆包
return (i for i in (self.x, self.y))
def __repr__(self):
class_name = type(self).__name__
# {!r} 获取各个分量
return '{}({!r}, {!r})'.format(class_name, *self)
def __str__(self): # 打印实例时调用
return str(tuple(self))
def __bytes__(self):
return bytes([ord(self.typecode)]) + bytes(array(self.typecode, self))
def __eq__(self, other):
return tuple(self) == tuple(other)
def __abs__(self):
return hypot(*self)
def __bool__(self):
return bool(abs(self))
calssmethod 与 staticmethod
- 类方法第一个参数永远是类本身,所以类方法常用作定义备用构造方法。
- 静态方法就是普通函数,只是碰巧出现在类定义体内。
格式化显示
- 格式说明符使用的表示法叫格式规范微语言,是可拓展的,由对象对应的类自行解释 format_spec 参数
- format(my_object, format_spec) 或 format_spec.format(my_object) 背后都是 my_object._format_(format_spec)
- 如果 my_object 对应的类没有定义 _format_ 方法,从 object 继承的方法则会返回 str(my_object)
def angle(self):
return atan2(self.y, self.x) # math.atan2
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('p'):
fmt_spec = fmt_spec[:-1] # 去掉最后的p
coords = (abs(self), self.angle())
outer_fmt = '<{}, {}>'
else:
coords = self
outer_fmt = '({}, {})'
components = (format(c, fmt_spec) for c in coords)
return outer_fmt.format(*components)
可散列的 Vector2d
自定义的类的实例默认都是不可散列的,除非实现了 _hash_ 方法
现在实现 _hash_ 方法:
def __hash__(self):
return hash(self.x) ^ hash(self.y)
下面实现对象不可变:
def __init__(self, x, y):
self.__x = float(x) # 尽早捕捉错误,以防构建实例时传入错误参数
self.__y = float(y)
@property
def x(self):
return self.__x
@property
def y(self):
return self.__y
私有属性
- 属性名称前面加一个下划线。
唯一影响是:from Vector2d import * 不会导入前缀为下划线的属性。 -
属性名称前面加两个下划线,如前面的 __x、__y。
Python 会把这些属性名称改写为 _Vector2d__x、_Vector2d__y,
这一语言特性称为「名称改写 name mangling」。
所以还是可以强行访问到这个属性。
使用 _slots_ 类属性节省空间
实例属性存储在 _dict_字典中。
但是字典为了底层的散列表提高访问速度,会消耗大量内存。
如果要处理数以百万的实例,而这些实例属性不多,则可以考虑 _slots_ 节省内存
- 定义 _slots_
class Vector2d:
__slots__ = ('__x', '__y')
把所有的属性都放到类属性 _slots_ 中
- 一个副作用
定义类属性 _slots_ 后,实例不能有_slots_ 中所列名称以外的属性。
- 一个要注意的地方
自定义的类默认有 _weakref_ 属性,为了让实例支持弱引用,需要把给属性放入 _slots_ 中。
覆盖类属性
typecode 设为 f,代表 4 字节单精度浮点数;设为 d,代表 8 字节双精度浮点数。