第9章 符合Python风格的对象

时间:2021-10-29 20:23:12
 #《流畅的Python》读书笔记
# 第9章 符合Python风格的对象
# 本章包含以下话题:
# 支持用于生成对象其他表示形式的内置函数(如repr()、bytes(),等等)
# 使用一个类方法实现备选构造方法
# 扩展内置的 format() 函数和 str.format() 方法使用的格式微语言
# 实现只读属性
# 把对象变为可散列的,以便在集合中及作为 dict 的键使用
# 利用 __slots__ 节省内存
# 我们将开发一个简单的二维欧几里得向量类型,在这个过程中涵盖上述全部话题。在实现这个类型的中间阶段,我们会讨论两个概念:
# 如何以及何时使用 @classmethod 和 @staticmethod 装饰器
# Python 的私有属性和受保护属性的用法、约定和局限 # 9.1 对象表示形式
# 每门面向对象的语言至少都有一种获取对象的字符串表示形式的标准方式。Python 提供了两种方式。
# repr()
#   以便于开发者理解的方式返回对象的字符串表示形式。
# str()
#   以便于用户理解的方式返回对象的字符串表示形式。 # 9.2 再谈向量类
# 为了说明用于生成对象表示形式的众多方法,我们将使用一个Vector2d 类,它与第 1 章中的类似。
# 示例 9-1 Vector2d 实例有多种表示形式
from array import array
import math
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__
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 math.hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self)) # 示例 9-2 vector2d_v0.py:目前定义的都是特殊方法
v1=Vector2d(3,4)
print(v1.x,v1.y)
x,y=v1
print(x,y) # (3.0, 4.0)
print(v1) # Vector2d(3.0, 4.0)
v1_clone = eval(repr(v1))
print(v1 == v1_clone) # True
print(v1) # (3.0, 4.0)
octets=bytes(v1)
print(octets) # b'd\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@'
print(abs(v1)) # 5.0
print(bool(v1),bool(Vector2d(0,0))) # 9.3 备选构造方法
# 我们可以把 Vector2d 实例转换成字节序列了;同理,也应该能从字节序列转换成 Vector2d 实例。 # 示例 9-3 vector2d_v1.py 的一部分:这段代码只列出了frombytes 类方法,要添加到 vector2d_v0.py(见示例 9-2)中定义的Vector2d 类中 # 9.4 classmethod与staticmethod
# staticmethod 装饰器也会改变方法的调用方式,但是第一个参数不是特殊的值。
# 其实,静态方法就是普通的函数,只是碰巧在类的定义体中,而不是在模块层定义。 # 示例 9-4 比较 classmethod 和 staticmethod 的行为
# >>> class Demo:
# @classmethod
# def klassmeth(*args):
# ❶ klassmeth 返回全部位置参数。
# return args
# @staticmethod
# def statmeth(*args):
# ❷ statmeth 也是。
# return args
# ❸ 不管怎样调用 Demo.klassmeth,它的第一个参数始终是 Demo 类。
# >>> Demo.klassmeth()
# (<class '__main__.Demo'>,)
# >>> Demo.klassmeth('spam')
# (<class '__main__.Demo'>, 'spam')
# ❹ Demo.statmeth 的行为与普通的函数相似。
# >>> Demo.statmeth()
# ()
# >>> Demo.statmeth('spam')
# ('spam',) # 9.5 格式化显示
# 内置的 format() 函数和 str.format() 方法把各个类型的格式化方式委托给相应的 .__format__(format_spec) 方法。format_spec 是格式说明符,它是:
# format(my_obj, format_spec) 的第二个参数,或者
# str.format() 方法的格式字符串,{} 里代换字段中冒号后面的部分
# >>> br1=1/2.43
# >>> br1
# 0.4115226337448559
# >>> format(br1,'0.4f')
# '0.4115'
# >>> '1 BRL={rate:0.2f}USD'.format(rate=br1)
# '1 BRL=0.41USD' # 9.6 可散列的Vector2d # 9.7 Python的私有属性和“受保护的”属性
# Python 不能像 Java 那样使用 private 修饰符创建私有属性,但是Python 有个简单的机制,能避免子类意外覆盖“私有”属性。 # 9.8 使用 __slots__ 类属性节省空间
# 在类中定义 __slots__ 属性之后,实例不能再有__slots__ 中所列名称之外的其他属性。 # 9.9 覆盖类属性 # 9.10 本章小结
# 回看表 1-1,你会发现本章的几个代码清单说明了下述特殊方法。
# 所有用于获取字符串和字节序列表示形式的方法:__repr__、__str__、__format__ 和 __bytes__。
# 把对象转换成数字的几个方法:__abs__、__bool__和__hash__。
# 用于测试字节序列转换和支持散列(连同 __hash__ 方法)的__eq__ 运算符。 # 9.11 延伸阅读