流畅的python读书笔记_流畅的Python_读书笔记(一)

这周正式开始阅读另一本Python的书籍《流畅的Python》,这周阅读的部分有Python数据模型、序列构成的数组和字典和集合。下面分享我的读书笔记。

首先书中用一个简单的代码展示了如何实现__getitem__和__len__特殊方法。

针对上述代码,我最大的启发就是Python特殊方法真的很有用。通过实现__getitem__和__len__特殊方法,我们直接可以用len()计算卡片的长度,而且这个函数也变成了可迭代的。

给扑克牌排序的函数。

二维向量类的定义

真的好美。

另外__repr__和__str__的区别在于,__str__是在str()函数被使用,或是在用print函数打印一个对象的时候才被调用,并且它返回的字符串对终端用户更友好。

使用列表推导式计算笛卡尔积

使用生成器表达式计算笛卡尔积

用元组用作记录

定义和使用具名元组

切片知识

s[a:b:c]——意味着对s在a和b之间以c为间隔取值。c的值可以为负,负值意味着反向取值。

特殊方法一览

跟运算符无关的特殊方法

字符串/字节序列表示形式

__repr__、__str__、__format__、__bytes__

数值转换

__abs__、__bool__、__complex__、__int__、__float__、__hash__ __index__

集合模拟

__len__、__getitem__、__setitem__、__delitem__、__contains__

迭代枚举

__iter__、__reversed__、__next__

可调用模拟

__call__

上下文管理

__enter__、__exit__

实例创建和销毁

__new__、__init__、__del__

属性管理

__getattr__、__getattribute__、__setattr__、__delattr__、__dir__

属性描述符

__get__、__set__、__delete__

跟类相关的服务

__prepare__、__instancecheck__、__subclasscheck__

跟运算符相关的特殊方法

类别

方法名和对应的

一元运算符

__neg__ -、__pos__ +、__abs__ abs( )

众多比较运算符

__lt__ 、__ge__ >=

算术运算符

__add__ +、__sub__ -、__mul__ *、__truediv__ /、__floordiv__ //、__mod__ %、__divmod__ divmod( )、__pow__ **或pow( )、__round__ round( )

反向算术运算符

__radd__、__rsub__、__rmul__、__rtruediv__、__rfloordiv__、__rmod__、__rdivmod__、__rpow__

增量赋值算术运算符

__iadd__、__isub__、__imul__、__itruediv__、__ifloordiv__、__imod__、__ipow__

对象模型的定义:计算机编程语言中对象的属性。

元对象定义:元对象所指的是那些对建构语言本身来讲很重要的对象,以此为前提,协议也可以看作接口。

容器序列存放的是它们所包含的任意类型对象的引用。扁平序列里存放的是值而不是引用,它是一段连续的内存空间,只能存放诸如字符、字节和数值这种基础类型。

可变序列:list、bytearray、array.array、collections.deque和memoryview。

不可变序列:tuple、str和bytes。

元组不仅仅是不可变的列表,它还可以用于没有字段名的记录。元组其实是对数据的记录:元组中的每个元素都存放了记录中一个字段的数据,外加这个字段的位置。正是这个位置信息给数据赋予了意义。

列表或元组的方法和属性

列表

元组

s.__add__(s2)

s + s2, 拼接

s.__iadd__(s2)

s += s2, 就地拼接

s.append(e)

在尾部添加一个新元素

s.clear( )

删除所有元素

s.__contains__(e)

s是否包含e

s.copy( )

列表的浅复制

s.count(e)

e在s中出现的次数

s.__delitem__(p)

把位于p的元素删除

s.extend(it)

把可迭代对象it追加给s

s.__getitem__(p)

s[p], 获取位置p的元素

s.__getnewargs__( )

在pickle中支持更加优化的序列化

s.index(e)

在s中找到元素e第一次出现的位置

s.insert(p, e)

在位置p之前插入元素e

s.__iter__( )

获取s的迭代器

s.__len__( )

len(s), 元素的数量

s.__mul__(n)

s * n, n个s 的重复拼接

s.__imul__(n)

s *= n, 就地重复拼接

s.__rml__(n)

n * s, 反向拼接 *

s.pop([p])

删除最后或者是(可选的)位于p的元素,并返回它的值

s.remove(e)

删除s中的第一次出现的e

s.reverse( )

就地把s的元素倒序排列

s.__reversed( )

返回s的倒叙迭代器

s.__setitem__(p, e)

s[p] = e, 把元素e放在位置p,替代已经在那个位置的元素

s.sort([key], [reverse])

就地对s中的元素进行排序,可选的参数有键(key)和是否倒叙(reverse)

sorted和list.sort背后的排序算法是Timsort,它是一种自适应算法,会根据原始数据的顺序特点交替使用插入排序和归并排序,以达到最佳效率。

在字典和集合这一章中,书中首先提出散列表是字典类型性能出众的根本原因。一般用户自定义的类型对象都是可散列的,散列值就是它们的id()函数的返回值。

字典推导的应用

散列表其实是一个稀疏数组(总是有空白元素的数组称为稀疏数组)。散列表里的单元叫做表元(bucket)。在dict的散列表当中,每个键值对都占用一个表元,每个表元都有两个部分,一个是对键的引用,另一个是对值的引用。因为所有表元的大小一致,所以可以通过偏移量来读取某个表元。

而如果要把一个对象放入散列表,首先要计算元素键的散列值。Python中可以通过hash( )方法来做这件事情。

有几个方面需要注意:

散列值和相等性

如果两个对象在比较的时候是相等的,那它们的散列值必须相等。为了让散列值能够胜任散列表索引这一角色,它们必须在索引空间中尽量分散开来。因此在最理想的情况下,越是相似但不相等的对象,它们的散列值的差别应该是越大。

散列表算法

为了获取mydict[search_key]背后的值 ,Python首先会调用hash(search_key)来计算search_key的散列值,把这个值最低的几位数字当作偏移量,在散列表里查找表元(具体取几位,得看当前散列表的大小)。

键必须是可散列的

一个可散列的对象必须满足以下要求:

支持hash()函数,并且通过__hash__()方法所得到的散列值是不变的。

支持通过__eq__()方法来检测相等性。

若 a == b为真,则hash(a) == hash(b) 也为真。

字典在内存上的开销巨大

由于字典使用了散列表,而散列表又必须是稀疏的,这导致它在空间上的效率低下。

键查询很快

键的次序取决于添加顺序

往字典里添加新键可能会改变已有键的顺序

不要对字典同时进行迭代和修改。

你可能感兴趣的:(流畅的python读书笔记)