第 9 章 符合Python风格的对象

本章包含以下话题:

  • 支持用于生成对象其他表示形式的内置函数(如 repr()、bytes(),等等)
  • 使用一个类方法实现备选构造方法
  • 扩展内置的 format() 函数和 str.format() 方法使用的格式微语言
  • 实现只读属性
  • 把对象变为可散列的,以便在集合中及作为 dict 的键使用
  • 利用 slots 节省内存

对象表示形式

  • repr()
      以便于开发者理解的方式返回对象的字符串表示形式。
    -str()
      以便于用户理解的方式返回对象的字符串表示形式。

  为了给对象提供其他的表示形式,还会用到另外两个特殊方法:bytes 和__format__。bytes 方法与 str 方法类似:bytes() 函数调用它获取对象的字节序列表示形式。而 format 方法会被内置的 format() 函数和 str.format() 方法调用,使用特殊的格式代码显示对象的字符串表示形式。

classmethod与staticmethod

  • classmethod
    定义操作类,而不是操作实例的方法。classmethod 改变了调用方法的方式,因此类方法的第一个参数是类本身,而不是实例。classmethod 最常见的用途是定义备选构造方法
  • staticmethod
    staticmethod 装饰器也会改变方法的调用方式,但是第一个参数不是特殊的值。其实,静态方法就是普通的函数,只是碰巧在类的定义体中,而不是在模块层定义。

Python的私有属性和“受保护的”属性

  举个例子。有人编写了一个名为 Dog 的类,这个类的内部用到了 mood 实例属性,但是没有将其开放。现在,你创建了 Dog 子类:Beagle。如果你在毫不知情的情况下又创建了名为 mood 的实例属性,那么在继承的方法中就会把 Dog 类的 mood 属性覆盖掉。
这是个难以调试的问题。避免这种情况,如果以 __mood 的形式(两个前导下划线,尾部没有或最多有一个下划线)命名实例属性,Python 会把属性名存入实例的 dict 属性中,而且会在前面加一个下划线和类名。因此,对 Dog 类来说,__mood 会变成_Dog__mood;对 Beagle类来说,会变成 _Beagle__mood。这个语言特性叫名称改写(name mangling)。

使用 slots 类属性节省空间

  默认情况下,Python 在各个实例中名为 dict 的字典里存储实例属性。为了使用底层的散列表提升访问速度,字典会消耗大量内存。如果要处理数百万个属性不多的实例,通过 slots 类属性,能节省大量内存,方法是让解释器在元组中存储实例属性,而不用字典。

定义 __slots__ 的方式是,创建一个类属性,使用 __slots__ 这个名字,并把它的值设为一个字符串构成的可迭代对象,
其中各个元素表示各个实例属性。
class Vector2d:
	__slots__ = ('__x', '__y')
	typecode = 'd'
 # 下面是各个方法(因排版需要而省略了)

  在类中定义 slots 属性的目的是告诉解释器:“这个类中的所有实例属性都在这儿了!”这样,Python 会在各个实例中使用类似元组的结构存储实例变量,从而避免使用消耗内存的 dict 属性。如果有数百万个实例同时活动,这样做能节省大量内存。
  在类中定义 slots 属性之后,实例不能再有 slots 中所列名称之外的其他属性。这只是一个副作用,不是 slots 存在的真正原因。不要使用__slots__ 属性禁止类的用户新增实例属性。slots 是用于优化的,不是为了约束程序员。
  然而,“节省的内存也可能被再次吃掉”:如果把 ‘dict’ 这个名称添加到__slots__ 中,实例会在元组中保存各个实例的属性,此外还支持动态创建属性,这些属性存储在常规的 dict 中。当然,把 ‘dict’ 添加到 slots 中可能完全违背了初衷,这取决于各个实例的静态属性和动态属性的数量及其用法。粗心的优化甚至比提早优化还糟糕。
  此外,还有一个实例属性可能需要注意,即 weakref 属性,为了让对象支持弱引用,必须有这个属性。用户定义的类中默认就有 weakref 属性。可是,如果类中定义了 slots 属性,而且想把实例作为弱引用的目标,那么要把’weakref’ 添加到 slots 中。
  综上,slots 属性有些需要注意的地方,而且不能滥用,不能使用它限制用户能赋值的属性。处理列表数据时 slots s属性最有用,例如模式固定的数据库记录,以及特大型数据集。然而,如果经常处理大量数据,一定要了解一下

slots 的问题

总之,如果使用得当,slots 能显著节省内存,不过有几点要注意。

  • 每个子类都要定义 slots 属性,因为解释器会忽略继承的 slots 属性。
  • 实例只能拥有 slots 中列出的属性,除非把 ‘dict’ 加入 slots 中(这样做就失去了节省内存的功效)。
  • 如果不把 ‘weakref’ 加入 slots,实例就不能作为弱引用的目标。
       如果你的程序不用处理数百万个实例,或许不值得费劲去创建不寻常的类,那就禁止它创建动态属性或者不支持弱引用。与其他优化措施一样,仅当权衡当下的需求并仔细搜集资料后证明确实有必要时,才应该使用 slots 属性。

覆盖类属性

  Python 有个很独特的特性:类属性可用于为实例属性提供默认值。Vector2d 中有个typecode 类属性,bytes 方法两次用到了它,而且都故意使用 self.typecode 读取它的值。因为 Vector2d 实例本身没有 typecode 属性,所以 self.typecode 默认获取的是 Vector2d.typecode 类属性的值。
  但是,如果为不存在的实例属性赋值,会新建实例属性。假如我们为 typecode 实例属性赋值,那么同名类属性不受影响。然而,自此之后,实例读取的 self.typecode 是实例属性 typecode,也就是把同名类属性遮盖了。借助这一特性,可以为各个实例的typecode 属性定制不同的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值