序列的修改、散列和切片

接着造Vector2d类

要达到的要求

为了编写Vector(3, 4) 和 Vector(3, 4, 5) 这样的代码,我们可以让 init 法接受任意个参数(通过 *args)

如果 Vector 实例的分量超过 6 个,repr() 生成的字符串就会使用 ... 省略一部
分,使用 reprlib 模块可以生成长度有限的表示形式

from array import array
import reprlib
import mathclass Vector:typecode = 'd'def __init__(self, components):self._components = array(self.typecode, components)def __iter__(self):return iter(self._components)# 这里是重点def __repr__(self):components = reprlib.repr(self._components)components = components[components.find('['):-1]return 'Vector({})'.format(components)print(Vector([3.1, 4.2]))
print(Vector((3, 4, 5)))
print(Vector(range(10)))

❸ 使用 reprlib.repr() 函数获取 self._components 的有限长度表示形式(如
array('d', [0.0, 1.0, 2.0, 3.0, 4.0, ...]))。
❹ 把字符串插入 Vector 的构造方法调用之前,去掉前面的 array('d' 和后面的 )。

协议和鸭子类型

在面向对象编程中,

  1. 协议是非正式的接口,只在文档中定义,在代码中不定义。
  2. 例如,Python 的序列协议只需要 lengetitem 两个方法。
  3. 任何类(如 Spam),只要使用标准的签名和语义实现了这两个方法,就能用在任何期待序列的地方。

第一章的代码再次给出

import collectionsCard = collections.namedtuple('Card', ['rank', 'suit'])class FrenchDeck:ranks = [str(n) for n in range(2, 11)] + list('JQKA')suits = 'spades diamonds clubs hearts'.split()def __init__(self):self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]def __len__(self):return len(self._cards)def __getitem__(self, position):return self._cards[position]

Vector类第2版:可切片的序列

from array import array
import reprlib
import mathclass Vector(object):typecode = 'd'def __init__(self, components):self._components = array(self.typecode, components)def __iter__(self):return iter(self._components)def __repr__(self):components = reprlib.repr(self._components)components = components[components.find('['):-1]return 'Vector({})'.format(components)def __str__(self):return str(tuple(self))def __bytes__(self):return (bytes([ord(self.typecode)]) +bytes(self._components))def __eq__(self, other):return tuple(self) == tuple(other)def __abs__(self):return math.sqrt(sum(x * x for x in self))def __bool__(self):return bool(abs(self))@classmethoddef frombytes(cls, octets):typecode = chr(octets[0])memv = memoryview(octets[1:]).cast(typecode)return cls(memv)def __len__(self):return len(self._components)def __getitem__(self, index):return self._components[index]v1 = Vector([3, 4, 5])
print(len(v1))print(v1[0], v1[-1])v7 = Vector(range(7))
print(v7[1:4])

现在连切片都支持了,不过尚不完美。如果 Vector 实例的切片也是 Vector
实例,而不是数组,那就更好了。

把 Vector 实例的切片也变成 Vector 实例,我们不能简单地委托给数组切片。我们
要分析传给 getitem 方法的参数,做适当的处理。

切片原理

class MySeq:def __getitem__(self, index):return indexs = MySeq()
print(s[1])print(s[1:4])print(s[1:4:2])print(s[1:4:2, 9])print(s[1:4:2, 7:9])

❸ 1:4 表示法变成了 slice(1, 4, None)。
❹ slice(1, 4, 2) 的意思是从 1 开始,到 4 结束,步幅为 2。
❺ 神奇的事发生了:如果 [] 中有逗号,那么 getitem 收到的是元组。
❻ 元组中甚至可以有多个切片对象。

查看 slice 类的属性

print(slice)
print(dir(slice))

['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'indices', 'start', 'step', 'stop']

通过审查 slice,发现它有 start、stop 和 step 数据属性,以及 indices 方法。

indices 方法开放了内置序列实现的棘手逻辑,用于优雅地处理缺失索引和
负数索引,以及长度超过目标序列的切片。
这个方法会“整顿”元组,把 start、stop 和
stride 都变成非负数,而且都落在指定长度序列的边界内。

一句话 把负数索引和超出长度的索引调整成 正常的索引

aa = 'ABCDE'print(slice(None, 10, 2).indices(5))
print(slice(-3, None, None).indices(5))print('='*40)
print(slice(None, 10, 2).indices(len(aa)))
print(slice(-3, None, None).indices(len(aa)))print(aa[-3:])

能处理切片的__getitem__方法

from array import array
import reprlib
import math
import numbersclass Vector(object):typecode = 'd'def __init__(self, components):self._components = array(self.typecode, components)def __iter__(self):return iter(self._components)def __repr__(self):components = reprlib.repr(self._components)components = components[components.find('['):-1]return 'Vector({})'.format(components)def __len__(self):return len(self._components)##[1:4] 返回一个向量对象def __getitem__(self, index):cls = type(self)if isinstance(index, slice):return cls(self._components[index])elif isinstance(index, numbers.Integral):return self._components[index]else:msg = '{cls.__name__} indices must be integers'raise TypeError(msg.format(cls=cls))v7 = Vector(range(7))
print(v7[-1])print(v7[1:4])print(v7[-1:])

Vector类第3版:动态存取属性

我们可以在 Vector 中编写四个特性,但这样太麻烦。
特殊方法 getattr 提供了更好的方式。

属性查找失败后,解释器会调用 getattr 方法。
简单来说,对 my_obj.x 表达式,

Python 会检查 my_obj 实例有没有名为 x 的属性;
如果没有,到类(my_obj.__class__)中查找;
如果还没有,顺着继承树继续查找。
如果依旧找不到,调用 my_obj 所属类中定义的 getattr 方法,传入 self 和属性名称的字符串形式(如 'x')。

from array import array
import reprlib
import math
import numbersclass Vector(object):typecode = 'd'def __init__(self, components):self._components = array(self.typecode, components)def __iter__(self):return iter(self._components)def __repr__(self):components = reprlib.repr(self._components)components = components[components.find('['):-1]return 'Vector({})'.format(components)shortcut_names = 'xyzt'def __getattr__(self, name):cls = type(self)if len(name) == 1:pos = cls.shortcut_names.find(name)if 0 <= pos < len(self._components):return self._components[pos]msg = '{.__name__!r} object has no attribute {!r}'raise AttributeError(msg.format(cls, name))def __setattr__(self, name, value):cls = type(self)if len(name) == 1:# 如果 name 是 xyzt 中的一个,设置特殊的错误消息。if name in cls.shortcut_names:error = 'readonly attribute {attr_name!r}'# 如果 name 是小写字母,为所有小写字母设置一个错误消息。elif name.islower():error = "can't set attributes 'a' to 'z' in {cls_name!r}"#否则,把错误消息设为空字符串。else:error = ''#如果有错误消息,抛出AttributeError。if error:msg = error.format(cls_name=cls.__name__, attr_name=name)raise AttributeError(msg)# 默认情况:在超类上调用 __setattr__ 方法,提供标准行为。super().__setattr__(name, value)v = Vector(range(5))
print(v)# 这个设置法 没用
v.p = 10
print(v.x)print(v)

super() 函数用于动态访问超类的方法,对 Python 这样支持多重继承的动态
语言来说,必须能这么做。程序员经常使用这个函数把子类方法的某些任务委托给超
类中适当的方法

注意,我们没有禁止为全部属性赋值,只是禁止为单个小写字母属性赋值,以防与只读属
性 x、y、z 和 t 混淆。

Vector类第4版:散列和快速等值测试

functools.reduce() 可以替换成 sum()

这里的原理

  • 它的关键思想是,把一系列值归约成单个值。
  • reduce() 函数的第一个参数是接受两个参数的函数,第二个参数是一个可迭代的对象。 假如有个接受两个参数的 fn 函数和一个 lst
    列表。
  • 调用 reduce(fn, lst) 时,fn 会应用到第一对元素上,即 fn(lst[0],lst[1]),生成第一个结果r1。然后,fn 会应用到 r1 和下一个元素上,即 fn(r1,lst[2]),生成第二个结果 r2。
  • 接着,调用 fn(r2, lst[3]),生成 r3……直到最后一个元素,返回最后得到的结果 rN。

如:

>>> import functools
>>> functools.reduce(lambda a,b: a*b, range(1, 6))
120

reduce接着用

import functoolsaa = functools.reduce(lambda a, b: a ^ b, range(1,6))
print(aa)# operator--操作符函数
# https://blog.csdn.net/shengmingqijiquan/article/details/53005129
import operator
bb = functools.reduce(operator.xor, range(6))
print(bb)

使用我喜欢的方式编写 Vector.__hash__ 方法,我们要导入 functools 和

operator 模块。(任性的作者)


import functools  # ➊
import operator  # ➋class Vector:typecode = 'd'# 排版需要,省略了很多行...def __eq__(self, other):  # ➌return tuple(self) == tuple(other)def __hash__(self):hashes = (hash(x) for x in self._components)  # ➍return functools.reduce(operator.xor, hashes, 0)  # ➎# 排版需要,省略了很多行...

❹ 创建一个生成器表达式,惰性计算各个分量的散列值。
❺ 把 hashes 提供给 reduce 函数,使用 xor 函数计算聚合的散列值;第三个参数,0 是
初始值(参见下面的警告框)。

eq 方法更有效率

def __eq__(self, other):if len(self) != len(other):  # ➊return Falsefor a, b in zip(self, other):  # ➋if a != b:  # ➌return Falsereturn True  # ➍

❷ zip 函数生成一个由元组构成的生成器,元组中的元素来自参数传入的各个可迭代对
象。如果不熟悉 zip 函数,请阅读“出色的 zip 函数”附注栏。前面比较长度的测试是有
必要的,因为一旦有一个输入耗尽,zip 函数会立即停止生成值,而且不发出警告。

使用 zip 和 all 函数实现 Vector.__eq__ 方法

def __eq__(self, other):return len(self) == len(other) and all(a == b for a, b in zip(self, other))

zip 内置函数的使用示例

>>> zip(range(3), 'ABC') # ➊
<zip object at 0x10063ae48>
>>> list(zip(range(3), 'ABC')) # ➋
[(0, 'A'), (1, 'B'), (2, 'C')]
>>> list(zip(range(3), 'ABC', [0.0, 1.1, 2.2, 3.3])) # ➌
[(0, 'A', 0.0), (1, 'B', 1.1), (2, 'C', 2.2)]
>>> from itertools import zip_longest # ➍
>>> list(zip_longest(range(3), 'ABC', [0.0, 1.1, 2.2, 3.3], fillvalue=-1))
[(0, 'A', 0.0), (1, 'B', 1.1), (2, 'C', 2.2), (-1, -1, 3.3)]

❸ zip 有个奇怪的特性:当一个可迭代对象耗尽后,它不发出警告就停止。
❹ itertools.zip_longest 函数的行为有所不同:使用可选的 fillvalue(默认
值为 None)填充缺失的值,因此可以继续产出,直到最长的可迭代对象耗尽。

Vector类第5版:格式化

__format__提供格式化方法,详情和具体代码 page 348

小总结

  1. repr 如果信息展示过长. 用reprlib 模块可以缩短

2.切片原理
slice(None, 10, 2).indices(5) 负责转换成可用的索引
len_getitem 实现切片的重要方法

  1. 属性查找失败后,解释器会调用 getattr 方法。利用这个特性,可以搞一些事情

4.reduce 的使用方法

5.zip函数 简单理解矩阵对应

流畅的python读书笔记-第十章-序列的修改、散列和切片相关推荐

  1. 流畅的python读书笔记④:文本和字节序列

    人类使用文本,计算机使用字节序列. --Esther Nam 和 Travis Fischer "Character Encoding and Unicode in Python" ...

  2. 流畅的Python读书笔记

    流畅的Python 说明 我发现流畅的python更适合我现在看,因为它写的很详细.而effective python知识点不是很连贯,我先看完这本书,再去过一遍effective python吧! ...

  3. 流畅的python读书笔记-第一章Python 数据模型

    第一章 python数据类型 1 隐式方法 利用collections.namedtuple 快速生成类 import collectionsCard = collections.namedtuple ...

  4. python读书笔记2000_流畅的Python读书笔记

    特殊方法的存在是为了Python解释器调用的,你自己并不需要去调用他们,比如说my_object.len()这种写法是没有的,应该使用len(my_object).在使用len(my_object)的 ...

  5. python vector_[流畅的Python]读书笔记之十三运算符重载

    运算符重载 Python 关于运算符重载的规则: 不能重载内置类型的运算符 不能新建,只能重载 某些运算符不能重载--is.and.or 和 not 一元运算符 __neg__ __pos__ __i ...

  6. python用可变参数求积_流畅的python读书笔记-第八章-对象引用、可变性和垃圾回收...

    对象不是个盒子 class Gizmo: def __init__(self): print('Gizmo id: %d' % id(self)) x = Gizmo() print(x) y = G ...

  7. 流畅的Python读书笔记-第八章-对象引用、可变性和垃圾回收

    第8章:对象引用,可变性和垃圾回收 在Python里面变量不是盒子,而是便利贴,类似于Java中的引用变量,因此最好把它们理解为附加在对象上的标注. 因为变量不过是标注,因此无法阻止为对象贴上多个标注 ...

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

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

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

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

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

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

最新文章

  1. Python训练营2021:构建8个真实世界的Python项目
  2. eclipse配置struts.xml自动提示
  3. 区块链与边缘计算(1)基本概念
  4. 高校实验室管理系统_实验室信息管理系统(LIMS)全解
  5. Open Live Writer测试
  6. 多元相关性分析_研究 | 精神分裂症症状与超氧化物歧化酶相关性的性别差异
  7. python serial 发送ctl+c_[已解决]shell 脚本 给命令发送 Ctrl+C信号
  8. linux select 服务器,Linux下用select()实现异步的Echo服务器
  9. python中如何表示_新行在Python中是如何表示的?
  10. winyyy sys hcpidesk sys mtlrd sys uldfhjfh sys servets exe等1
  11. extjs 渲染之前的方法_extjs重新渲染组件
  12. 石油化工设备维护检修规程_超级石化好文推荐:提升石化设备管理水平 最新修订版维护检修规程!...
  13. 贵州等保测评机构工程师(DJCP)目录-贵州等级保护测评机构工程师名单
  14. 如何获取qq空间图片的url
  15. 神经网络分类算法是什么,神经网络分类算法简介
  16. 中序遍历二叉树-非递归方式实现-附C++代码
  17. 微软向行业推介《欧盟通用数据保护条例》遵从指南
  18. 长沙理工大学计算机与通信工程学院院长,徐蔚鸿教授
  19. 2020年防爆电气模拟考试及防爆电气实操考试视频
  20. ue4技术方向学习路线如何安排?

热门文章

  1. 【网络流24题】分配问题 最小最大费用最大流
  2. Scrapy架构及其组件之间的交互
  3. 未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序 解决方法
  4. 《Unix环境高级编程》学习笔记
  5. php5配置与IIS中isapi筛选器不能加载PHP的解决办法
  6. [Reference][Castle AR] 1. Starter
  7. Ali-Tomcat 安装
  8. 解决微信小程序要求TLS版本不低于1.2问题
  9. 设计模式-1-单例模式
  10. HDU2050 由直线分割平面推广到折线分割平面