美文网首页Fluent Python
符合 Python 风格的对象

符合 Python 风格的对象

作者: 一块大番薯 | 来源:发表于2018-01-02 09:03 被阅读12次

对象表现形式

  • 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))
Vector2d

calssmethod 与 staticmethod

classmethod 与 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)
    没有定义,实质调用的是__str__
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_字典中。

__dict__
但是字典为了底层的散列表提高访问速度,会消耗大量内存。
如果要处理数以百万的实例,而这些实例属性不多,则可以考虑 _slots_ 节省内存
  • 定义 _slots_
class Vector2d:
    __slots__ = ('__x', '__y')

把所有的属性都放到类属性 _slots_

  • 一个副作用
    定义类属性 _slots_ 后,实例不能有_slots_ 中所列名称以外的属性。
    不能有 __slots__ 以外的属性
  • 一个要注意的地方
    自定义的类默认有 _weakref_ 属性,为了让实例支持弱引用,需要把给属性放入 _slots_ 中。

覆盖类属性

typecode 设为 f,代表 4 字节单精度浮点数;设为 d,代表 8 字节双精度浮点数。

相关文章

网友评论

    本文标题:符合 Python 风格的对象

    本文链接:https://www.haomeiwen.com/subject/lcpngxtx.html