对象表示形式

所谓对象表示形式,就是将对象转换为一种可读的形式。

Python提供了两种方式:

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

我们只要实现 __repr____str__ 特殊方法,就可以为repr()str() 提供支持。

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

向量类

我们使用Vector2d类来说明对象表示形式的众多用法。

#vector2d_v0.py
from array import array
import mathclass Vector2d:typecode = 'd' #类属性,在实例和字节序之间转换时使用def __init__(self,x,y):self.x = float(x) #把x和y转换成浮点数,尽早捕获错误,防止传入不当参数self.y = float(y)def __iter__(self):return (i for i in (self.x, self.y)) #把Vector2d实例变成可迭代的对象,这样才能拆包def __repr__(self):class_name = type(self).__name__return '{}({!r}, {!r})'.format(class_name, *self) #因为 Vector2d 实例是可迭代的对象,所以 *self 会#把 x 和 y 分量提供给 format 函数。def __str__(self):return str(tuple(self))def __bytes__(self):return (bytes([ord(self.typecode)]) + #把 typecode 转换成字节序列bytes(array(self.typecode, self))) #迭代 Vector2d 实例,得到一个数组,再把数组转换成字节序列。def __eq__(self, other):return tuple(self) == tuple(other)def __abs__(self):return math.hypot(self.x,self.y)  #模是 x 和 y 分量构成的直角三角形的斜边长def __bool__(self):return bool(abs(self))

在控制台调用如下:

>>> from vector2d_v0 import Vector2d
>>> v1 = Vector2d(3,4)
>>> print(v1.x,v1.y)
3.0 4.0
>>> x,y = v1 #Vecctor2d实例可以拆包成变量元组
>>> x,y
(3.0, 4.0)
>>> v1
Vector2d(3.0, 4.0)
>>> repr(v1)
'Vector2d(3.0, 4.0)'
>>> str(v1)
'(3.0, 4.0)'
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone #支持==比较
True
>>> print(v1)
(3.0, 4.0)
>>> octets = bytes(v1)
>>> octets
b'd\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@'
>>> abs(v1)
5.0
>>> bool(v1),bool(Vector2d(0,0))
(True, False)

备选构造方法

我们可以把 Vector2d实例转换成字节序列了;同理,也应该能从字
节序列转换成 Vector2d 实例。

主要在上面的类中增加如下类方法:

@classmethod #类方法使用classmethod装饰器修饰
def frombytes(cls,octets): #通过cls传入类本身typecode = chr(octets[0]) #从第一个字节中读取typecodememv = memoryview(octets[1:]).cast(typecode) #通过传入的字节序列创建一个memoryview,然后使用typecode转换return cls(*memv) #拆包转换后的memoryview,得到构造方法所需的一对参数

classmethod与staticmethod

classmethod定义操作类的方法,该方法的第一个参数是类本身。其最常见的用途是备选构造方法。

staticmethod装饰器也会改变方法的调用方式,但是第一个参数不是特殊的值。其实,静态方法就是普通的函数。

In [1]: class Demo: ...:     @classmethod ...:     def klassmeth(*args): ...:         return args ...:     @staticmethod ...:     def statmeth(*args): ...:         return args ...:                                                                         In [2]: Demo.klassmeth()#不管怎么调用该方法,第一个参数始终是Demo
Out[2]: (__main__.Demo,)
In [3]: Demo.klassmeth('spam')
Out[3]: (__main__.Demo, 'spam')
In [4]: Demo.statmeth('spam') #它的行为与普通函数类似
Out[4]: ('spam',)

格式化显示

内置的 format() 函数和str.format() 方法把各个类型的格式化方
式委托给相应的 .__format__(format_spec)
法。format_spec是格式说明符,它是:

  • format(my_obj, format_spec)的第二个参数,或者
  • str.format()方法的格式字符串,{}里代换字段中冒号后面的部分
In [5]: brl = 1/2.43
In [6]: brl
Out[6]: 0.4115226337448559
In [7]: format(brl,'0.4f')
Out[7]: '0.4115'
In [8]: '1 BRL = {rate:0.2f} USD'.format(rate=brl)  #rate被brl替换
Out[8]: '1 BRL = 0.41 USD'

{rate:0.2f}'这样的格式字符串其实包含两部分,
冒号左边的 rate 在代换字段句法中是字段名,冒号后面的 0.2f 是格式说明符。格式说明符使用的表示法叫格式规范微语言。

格式规范微语言是可扩展的,因为各个类可以自行决定如何解释
format_spec 参数。例如,datetime 模块中的类,它们的
__format__方法使用的格式代码与strftime() 函数一样。下面是
内置的format() 函数和str.format() 方法的几个示例:

>>> from datetime import datetime
>>> now = datetime.now()
>>> format(now,'%H:%M:%S')
'16:20:38'
>>> "It's now {:%I:%M %p}".format(now)
"It's now 04:20 PM"

如果类没有定义 __format__ 方法,从 object 继承的方法会返回
str(my_object)。我们为Vector2d类定义了 __str__ 方法,因
此可以这样做:

>>> v1 = Vector2d(3,4)
>>> format(v1)
'(3.0, 4.0)'

接下来将实现自己的微语言。假设用户提供的格式说明符是用于格式化向量中各个浮点数分量的。我们想达到这样的效果:

>>> v1 = Vector2d(3, 4)
>>> format(v1)
'(3.0, 4.0)'
>>> format(v1, '.2f')
'(3.00, 4.00)'
>>> format(v1, '.3e')
'(3.000e+00, 4.000e+00)'

增加的方法如下:

def __format__(self, format_spec = ''):components = (format(c,format_spec) for c in self) #使用内置的 format 函数把 fmt_spec 应用到向量的各个分量上return '({}, {})'.format(*components)

可散列的Vector2d

为了让Vector2d实例变成可散列的,必须实现__hash__方法(还有__eq__方法),还需让向量不可变。

#修改了构造方法
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.__ydef __hash__(self):return hash(self.x) ^ hash(self.y)

下面对改造后的向量进行测试:

>>> from vector2d_v2 import Vector2d
>>> v1 = Vector2d(3,4)
>>> v2 = Vector2d(3.1,4.2)
>>> hash(v1),hash(v2)
(7, 384307168202284039)
>>> set([v1,v2])
{Vector2d(3.1, 4.2), Vector2d(3.0, 4.0)}

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

Python 不能像 Java 那样使用 private修饰符创建私有属性,但是
Python 有个简单的机制,能避免子类意外覆盖“私有”属性。

我们上面在属性x名称前加了两个下划线变成了__x,对于该类来说,__x会变成_Vector2d__x,这个语言特性叫名称改写。Python 会把这种属性名存入实例的__dict__属性中,而且会在前面加上一个下划线和类名:

>>> v1.__dict__
{'_Vector2d__x': 3.0, '_Vector2d__y': 4.0}
>>> v1._Vector2d__x #如果知道了如何改写名称,还是可以访问到
3.0

但是有些人不喜欢这种句法,他们约定使用
一个下划线前缀编写“受保护”的属性(如self._x

Python 解释器不会对使用单个下划线的属性名做特殊处理,不过这是很
多 Python 程序员严格遵守的约定,他们不会在类外部访问这种属性。

使用__slots__类属性节省空间

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

定义 __slots__的方式是,创建一个类属性,使用 __slots__这个
名字,并把它的值设为一个字符串构成的可迭代对象,其中各个元素表
示各个实例属性。我喜欢使用元组,因为这样定义的 __slots__ 中所
含的信息不会变化,如示例所示:

class Vector2d:__slots__ = ('__x','__y')

在类中定义 __slots__ 属性的目的是告诉解释器:“这个类中的所有
实例属性都在这儿了!”

覆盖类属性

Python 有个很独特的特性:类属性可用于为实例属性提供默认
值。Vector2d中有个 typecode 类属性,__bytes__ 方法两次用到
了它,而且都故意使用 self.typecode 读取它的值。因为
Vector2d 实例本身没有typecode 属性,所以 self.typecode
认获取的是 Vector2d.typecode 类属性的值。

但是,如果为不存在的实例属性赋值,会新建实例属性。假如我们为
typecode实例属性赋值,那么同名类属性不受影响。然而,自此之
后,实例读取的self.typecode 是实例属性typecode,也就是把
同名类属性遮盖了。借助这一特性,可以为各个实例的typecode
性定制不同的值。

Vector2d.typecode 属性的默认值是 ‘d’,即转换成字节序列时使
用 8 字节双精度浮点数表示向量的各个分量。如果在转换之前把
Vector2d 实例的typecode 属性设为 ‘f’,那么使用 4 字节单精度
浮点数表示各个分量:

>>> v1 = Vector2d(1.1,2.2)
>>> dumpd = bytes(v1)
>>> dumpd
b'd\x9a\x99\x99\x99\x99\x99\xf1?\x9a\x99\x99\x99\x99\x99\x01@'
>>> len(dumpd)
17
>>> v1.typecode = 'f' #修改v1实例的typecode属性
>>> dumpf = bytes(v1)
>>> dumpf
b'f\xcd\xcc\x8c?\xcd\xcc\x0c@'
>>> len(dumpf)
9
>>> Vector2d.typecode # 而类属性不变
'd'

《流畅的Python》读书笔记——符合Python风格的对象相关推荐

  1. 与孩子一起学编程python_与孩子一起学编程(Python读书笔记3)

    第十一章 嵌套与可变循环 Python 3.X里 print()函数默认是自动换行的,所以本章代码会有很多问题,实际上 print()函数里有一个默认参数 end, 默认情况下: end= " ...

  2. 读书笔记——《Python编程从入门到实践》第二章

    读书笔记--<Python编程从入门到实践>第二章 读书笔记--<Python编程从入门到实践>第二章 变量 如何使用变量 如何规范变量命名 字符串 字符串是什么 如何修改字符 ...

  3. Python读书笔记-每日篇-20190222|激活码生成器(redis存储)

    问题描述: 做为 Apple Store App 独立开发者,你要搞限时促销,为你的应用生成激活码(或者优惠券),使用 Python 如何生成 200 个激活码(或者优惠券),并将生成的激活码保存到R ...

  4. Python读书笔记-每日篇-20190221|激活码生成器(mysql存储)

    问题描述: 做为 Apple Store App 独立开发者,你要搞限时促销,为你的应用生成激活码(或者优惠券),使用 Python 如何生成 200 个激活码(或者优惠券),并将生成的激活码保存到M ...

  5. 《Essential C++》读书笔记 之 泛型编程风格

    <Essential C++>读书笔记 之 泛型编程风格 2014-07-07 3.1 指针的算术运算(The Arithmetic of Pointer) 新需求1 新需求2 新需求3 ...

  6. Python基础 笔记(一) Python的介绍

    Python基础 笔记(一) Python的介绍 您好! 欢迎来到木易巷! 接下来,让我们一起来了解Python,走进Python~ 1.编程语言 编程语言(programming language) ...

  7. 【python MySQL 笔记】python和MySQL交互、操作

    [python MySQL 笔记]python和MySQL交互.操作 目录 1. 数据准备 2.  SQL演练 2.1 SQL语句强化练习 2.2. 将一个表拆为多个表 3. python操作MySQ ...

  8. boxplot用法 python,[Python画图笔记]利用Python画箱型图boxplot

    [Python画图笔记]利用Python画箱型图boxplot [Python画图笔记]利用Python画箱型图boxplot 最近在学习使用Python画图,想用subplot画两幅箱型图,分别用来 ...

  9. 【读书笔记】Python编程:从入门到实践-埃里克·马瑟斯,python基础体系巩固和常见场景练习

    [概述] 书名:Python编程:从入门到实践 作者:埃里克·马瑟斯 日期:2021年09月01日 读书用时:1632页,100小时,27个笔记 [读书笔记] ◆ 第4章 操作列表 >> ...

  10. 【读书笔记】Python网络爬虫从入门到实践(第2版)-唐松,爬虫基础体系巩固和常见场景练习

    [概述] 书名:Python网络爬虫从入门到实践(第2版) 作者:唐松 日期:2021年08月01日 读书用时:1568页,100小时,59个笔记 [读书笔记] ◆ 1.2 网络爬虫是否合法 爬虫协议 ...

最新文章

  1. LeetCode实战:Nim 游戏
  2. 开源软件:信息共赢和开放心态
  3. STM32外部中断与各通道对应关系
  4. NUP2105L CAN BUS总线端口静电保护器件
  5. linux下安装oracle sqlplus以及imp、exp工具
  6. MongoDB Replication
  7. 【C++探索之旅】第一部分第四课:内存,变量和引用
  8. 收藏 | 详解目标检测(MMdetection)-Runner
  9. c#类中字段和方法中变量的声明问题
  10. ORACLE TRUNC()函数
  11. android pickerview 多行,Android PickerView实现三级联动效果
  12. docker查看java版本_Linux 安装jdk,查看版本,docker
  13. 技嘉主板更新版BIOS
  14. 码云 注册 注册个性域名报错---已经解决
  15. tomcat 虚拟目录配置appBase和docBase的区别
  16. nc文件处理学习资料
  17. 苹果CMS绑定分类失败,刷新就丢失!
  18. rtsp直播流转m3u8
  19. 边缘设备、系统及计算杂谈(1)
  20. 积累20180604

热门文章

  1. 【leetcode】复写零
  2. Cesium.js学习第二天(立方体)
  3. SpringBoot参数传递bean自动填充
  4. Myeclipse中web project各种常见错误及解决方法(持续更新)
  5. 将JQuery框架集成到SharePoint 2010中
  6. Microsoft Office Mobile 2010 Beta 于 4 月 5 日过期
  7. new Image().src资源重复请求问题
  8. 基本数据类型与格式化输出
  9. ASP.NET没有魔法——ASP.NET MVC使用Area开发一个管理模块
  10. 由脚本创建的新元素事件不触发和用的easyUI插件中的多选框不起作用的解决方法...