一、序幕

1. 数据模型

数据模型其实是对 Python 框架的描述,它规范了这门语言自身构建模块的接口,这些模块包括但不限于序列、迭代器、函数、类和上下文管理器。

特殊方法

magic 和 dunder:魔术方法(magic method)是特殊方法的昵称,比如特殊方法getitem 。Steve Holden 把这念做“双下-getitem”(dunder-getitem)。于是,特殊方法也叫双下方法(dunder method)。

  • collections.namedtuple 用以构建只有少数属性但是没有方法的对象,比如数据库条目。
  • abs 是一个内置函数,如果输入是整数或者浮点数,它返回的是输入值的绝对值;如果输入是复数(complex number),那么返回这个复数的模。

  • 迭代通常是隐式的,譬如说一个集合类型没有实现 contains 方法,那么 in 运算符就会按顺序做一次迭代搜索。

  • 特殊方法的调用是隐式的,比如for i in x:这个语句,背后其实用的是 iter(x),而这个函数的背后则是 x.iter() 方法。
  • 如果没有实现 repr,当我们在控制台里打印一个向量的实例时,得到的字符串可能会是
  • 交互式控制台和调试程序(debugger)用 repr 函数来获取字符串表示形式。
  • reprstr 的区别在于,后者是在 str() 函数被使用,或是在用 print 函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更友好。
  • bool(x) 的背后是调用 x.bool() 的结果;如果不存在 bool 方法,那么 bool(x) 会尝试调用 x.len()。若返回 0,则 bool 会返回 False;否则返回 True。

二、数据结构

  • 不管是哪种数据结构,字符串、列表、字节序列、数组、XML 元素,抑或是数据库查询结果,它们都共用一套丰富的操作:迭代、切片、排序,还有拼接。

    2. 序列构成的数组

    2.1 内置序列的类型

    容器序列:list、tuple 和 collections.deque 这些序列能存放不同类型的数据。
    扁平序列:str、bytes、bytearray、memoryview 和 array.array,这类序列只能容纳一种类型。
    容器序列存放的是它们所包含的任意类型的对象的引用,而扁平序列里存放的是值而不是引用。换句话说,扁平序列其实是一段连续的内存空间。由此可见扁平序列其实更加紧凑,但是它里面只能存放诸如字符、字节和数值这种基础类型。

序列类型还能按照能否被修改来分类。
可变序列:list、bytearray、array.array、collections.deque 和 memoryview。
不可变序列:tuple、str 和 bytes。

2.2 列表推导和生成器表达式

  • Python 会忽略代码里 []、{} 和 () 中的换行,因此如果你的代码里有多行的列表、列表推导、生成器表达式、字典这一类的,可以省略不太好看的续行符 \。
  • 列表推导、生成器表达式,以及同它们很相似的集合(set)推导和字典(dict)推导,在 Python 3 中都有了自己的局部作用域,就像函数似的。表达式内部的变量和赋值只在局部起作用,表达式的上下文里的同名变量还可以被正常引用,局部变量并不会影响到它们
  • 列表推导的作用只有一个:生成列表。如果想生成其他类型的序列,生成器表达式就派上了用场。虽然也可以用列表推导来初始化元组、数组或其他序列类型,但是生成器表达式是更好的选择。这是因为生成器表达式背后遵守了迭代器协议,可以逐个地产出元素,而不是先建立一个完整的列表,然后再把这个列表传递到某个构造函数里
  • 生成器表达式的语法跟列表推导差不多,只不过把方括号换成圆括号而已。

image.png
image.png

2.3 元组不仅仅是不可变的列表

有些 Python 入门教程把元组称为“不可变列表”,然而这并没有完全概括元组的特点。除
了用作不可变的列表,它还可以用于没有字段名的记录。

  • 元组其实是对数据的记录:元组中的每个元素都存放了记录中一个字段的数据,外加这个字段的位置。正是这个位置信息给数据赋予了意义。
  • for 循环可以分别提取元组里的元素,也叫作拆包(unpacking)。因为元组中第二个元素对我们没有什么用,所以它赋值给“_”占位符。拆包让元组可以完美地被当作记录来使用。
  • 元组拆包可以应用到任何可迭代对象上,唯一的硬性要求是,被可迭代对象中的元素数量必须要跟接受这些元素的元组的空档数一致。除非我们用 * 来表示忽略多余的元素。
  • Python 中,函数用 *args 来获取不确定数量的参数算是一种经典写法了。
  • 在平行赋值中,* 前缀只能用在一个变量名前面,但是这个变量可以出现在赋值表达式的任意位置:
  • 这里元组拆包的用法则是让一个函数可以用元组的形式返回多个值,然后调用函数的代码就能轻松地接受这些返回值。比如 os.path.split() 函数就会返回以路径和最后一个文件名组成的元组 (path, last_part):