美文网首页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