Python 数据操作
本书由电子工业出版社出版,这里仅是在阅读本书中的个人学习笔记,如果侵权,请联系本人删除。纸质书购买请转到这里,本书作者提供的主页请点击这里,作者非常厉害,也是《Python Cookbook, 3rd》的作者。
迭代操作
迭代是一个重要的 Python 特性,所有 Python 容器(列表、元组、字典等)、文件以及生成器函数。下表中的操作可以应用于任何可迭代对象。
操作 | 描述 |
---|---|
for vars in s: | 迭代 |
v1, v2, ... = s | 变量析构 |
x in s, x not in s | 成员检查 |
[a, *s, b], (a, *s, b), {a, *s, b} | 在列表、元组或集合字面量中展开 |
可迭代对象最基本的操作是 for
循环。这是迭代的方式逐一检查这些值。所有其他操作都以此为基础。
x in s
运算符测试对象 x
是否作为以下运算符生成的项之一出现可迭代对象 s
并返回 True
或 False
。x not in s
运算符与 not (x in s)
。对于字符串,in
和 not in
运算符接受子字符串。例如,"hello" in "hello world"
中产生 True
。请注意, in
运算符不支持通配符或任何类型的模式匹配。
任何支持迭代的对象都可以将其值解包到一系列地址。
items = [3, 4, 5]
x, y, z = items
letters = "abc"
x, y, z = letters
左侧的地址不必是简单的变量名。任何可以出现在等号的左侧的有效地址是可以接受的。
items = [3, 4, 5]
d = {}
d["x"], d["y"], d["z"] = items
将值解包到位置时,左侧位置的数量必须恰好匹配右侧可迭代项中的项目数。对于嵌套数据结构,匹配位置和数据遵循相同的结构模式。
datetime = ((5, 9, 2008), (10, "30", "am"))
(month, day, year), (hour, minute, am_pm) = datetime
有时候,_
变量在拆包时可用来表示一个被丢弃的值:
(_, day, _), (hour, _, _) = datetime
如果不知道要解构的项数量,可以使用扩展形式通过包含带星号的变量来解包
items = [1, 2, 3, 4, 5]
a, b, *extra = items
*extra, a, b = items
a, *extra, b = items
*extra
接收所有额外项目,它始终是一个列表。当解构一个可迭代对象时,最多只能使用一个带星号的变量。然而,当解构更复杂的包含不同可迭代对象的数据结构时,则可以使用多个星号变量:
(month, *_), (hour, *_) = datetime
在列出列表、元组和集合字面量时,任何可迭代对象都可以被展开。这也是使用星号(*
)完成。
>>> items = [1, 2, 3]
>>> a = [10, *items, 11]
>>> b = (*items, 10, *items)
>>> c = {10, 11, *items}
>>> a
[10, 1, 2, 3, 11]
>>> b
(1, 2, 3, 10, 1, 2, 3)
>>> c
{1, 2, 3, 10, 11}
>>>
在这个例子中,items
的内容只是粘贴到列表、元组或集合中,就像在该位置键入内容一样。这种扩展称为“泼溅”。定义字面量时,可以根据需要包含任意数量的 *
扩展。但是,许多可迭代对象(例如文件或生成器)仅支持一次迭代。如果你使用 *
扩展,内容将被消耗,并且迭代器不会再产生任何内容后续迭代中的值。
各种内置函数都接受任何可迭代对象作为输入。下表列出了一些这些操作。
函数 | 描述 |
---|---|
list(s) | 从 s 创建一个列表 |
tuple(s) | 从 s 创建一个列表 |
set(s) | 从 s 创建一个 Set |
min(s [,key]) | s 中的最小项 |
max(s [,key]) | s 中的最大想 |
any(s) | 如果 s 中的任一项为真,则返回 True |
all(s) | 如果 s 中的所有项为真,则返回 True |
sum(s [, initial]) | 所有项的总和,带有一个可选的初始值 |
sorted(s [, key]) | 创建一个排序列表 |
序列操作
序列是一个可迭代的容器,具有一定的大小,并允许通过从 0 开始的整数索引,比如说字符串、列表和元组。除了所有涉及迭代的运算,下表中的运算符可以应用于序列。
操作符 | 描述 |
---|---|
s + r | 连接 |
s * n , n * s | 将 s 赋值 n 次,其中 n 是整数 |
s[i] | 索引 |
s[i:j] | 切片 |
s[i:j:stride] | 拓展的切片 |
len(s) | 求长度 |
+
运算符连接两个相同类型的序列。例如:
>>> a = [1, 2, 3]
>>> b = [4, 5]
>>> a + b
[1, 2, 3, 4, 5]
>>>
s * n
运算符制作序列的 n
个副本。然而,这些都是浅拷贝,仅通过引用复制元素。考虑以下代码:
>>> a = [3, 4, 5]
>>> b = [a]
>>> c = 4 * b
>>> c
[[3, 4, 5], [3, 4, 5], [3, 4, 5], [3, 4, 5]]
>>> a[0] = -7
>>> c
[[-7, 4, 5], [-7, 4, 5], [-7, 4, 5], [-7, 4, 5]]
>>>
请注意对 a
的更改如何修改列表 c
的每个元素。在本例中,对列表 a
的引用被放置在列表 b
中。复制 b
时,创建了四个对 a
的附加引用。最后,当 a
被修改时,此更改会传播到 a
的所有其他副本。这种序列乘法的行为通常不是程序员的意图。解决该问题的一种方法是通过复制 a
的内容来手动构建复制序
列。这是一个例子:
a = [3, 4, 5]
c = [list(a) for _ in range(4)]
负索引可用于从序列末尾获取字符。尝试访问超出范围的元素会导致 IndexError
异常。允许使用负索引,并假定为相对于序列的结尾。
>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[2:5]
[2, 3, 4]
>>> a[:3]
[0, 1, 2]
>>> a[-3:]
[7, 8, 9]
>>> a[::2]
[0, 2, 4, 6, 8]
>>> a[::-2]
[9, 7, 5, 3, 1]
>>> a[0:5:2]
[0, 2, 4]
>>> a[5:0:-2]
[5, 3, 1]
>>> a[:5:1]
[0, 1, 2, 3, 4]
>>> a[:5:-1]
[9, 8, 7, 6]
>>> a[5::1]
[5, 6, 7, 8, 9]
>>> a[5::-1]
[5, 4, 3, 2, 1, 0]
>>> a[5:0:-1]
[5, 4, 3, 2, 1]
>>>
复杂的切片可能会导致代码在之后难以理解。切片可以使用 slice()
来命名。
>>> first_five = slice(0, 5)
>>> s = "hello world"
>>> print(s[first_five])
hello
>>>
可变序列操作
列表或其他可变序列可以使用下表中的运算符进行就地修改。
Operation | Description |
---|---|
s[i] = x | 索引赋值 |
s[i:j] = r | 切片赋值 |
s[i:j:stride] = r | 拓展切片赋值 |
del s[i] | 删除一个元素 |
del s[i:j] | 删除切片 |
del s[i:j:stride] | 删除切片拓展 |
s[i] = x
运算符更改序列的元素i以引用对象 x
,增加 x
的引用计数。负索引相对于列表的末尾和尝试为超出范围的索引分配值会导致 IndexError
异常。切片赋值运算符 s[i:j] = r
将元素 k
(其中 i <= k < j
)替换为元素来自序列 r
。索引与切片的含义相同。如有必要,序列可以扩大或缩小尺寸以容纳 r
中的所有元素。
>>> a = [1, 2, 3, 4, 5]
>>> a[1] = 6
>>> a
[1, 6, 3, 4, 5]
>>> a[2:4] = [10, 11]
>>> a
[1, 6, 10, 11, 5]
>>> a[3:4] = [-1, -2, -3]
>>> a
[1, 6, 10, -1, -2, -3, 5]
>>> a[2:] = [0]
>>> a
[1, 6, 0]
>>>
切片赋值可以提供可选的步长参数。但是,那行为会受到更多限制,因为右侧的参数必须完全具有与被替换的切片具有相同数量的元素。
>>> a = [1, 2, 3, 4, 5]
>>> a[1::2] = [10, 11]
>>> a[1::2] = [30, 40, 50]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: attempt to assign sequence of size 3 to extended slice of size 2
>>>
del s[i]
运算符从序列中删除元素 i
,并减少其引用计数。del s[i:j]
删除切片中的所有元素。步长也可以提供,如 del s[i:j:stride]
中所示。
此处描述的语义适用于内置列表类型。涉及序列切片是第三方包中可供定制的丰富领域。你可能会发现非列表对象上的切片在重新分配、删除和删除方面有不同的规则对象共享。例如,流行的 numpy 包具有不同的切片语义比 Python 列表。
Set 操作
Set 是唯一值的无序集合。下表的操作可以在 Set 上执行:
Operation | Description |
---|---|
s | t | s 和 t 的并集 |
s & t | s 和 t 的交集 |
s – t | s 和 t 的差集 |
s ^ t | 对称差(并集-交集) |
len(s) | 集合中的项数 |
item in s , item not in s | 成员检查 |
s.add(item) | 添加一个项 |
s.remove(item) | 如果存在项,则移除,否则报错 |
s.discard(item) | 删除一个存在的项 |
>>> a = {"a", "b", "c"}
>>> b = {"c", "d"}
>>> a | b
{'a', 'b', 'c', 'd'}
>>> a & b
{'c'}
>>> a - b
{'a', 'b'}
>>> b - a
{'d'}
>>> a ^ b
{'a', 'b', 'd'}
>>>
集合操作也适用于字典的键视图和项目视图对象。例如,为了要找出两个字典有哪些共同的键,请执行以下操作:
>>> a = {"x": 1, "y": 2, "z": 3}
>>> b = {"z": 3, "w": 4, "q": 5}
>>> a.keys() & b.keys()
{'z'}
>>>
映射操作
映射是键和值之间的关联。内置的 dict
类型就是一个例子。下表中的操作可以应用于映射。
Operation | Description |
---|---|
x = m[k] | 通过键来索引 |
m[k] = x | 通过键来赋值 |
del m[k] | 通过键来删除一项 |
k in m | 成员检查 |
len(m) | 映射的项目数 |
m.keys() | 返回键的视图 |
m.values() | 返回值的视图 |
m.items() | 返回键值对的视图 |
键值可以是任何不可变的对象,例如字符串、数字和元组。当使用元组作为键,可以省略括号并写入逗号分隔的值:
>>> d = {}
>>> d[1, 2, 3] = "foo"
>>> d[1, 0, 3] = "bar"
>>> d
{(1, 2, 3): 'foo', (1, 0, 3): 'bar'}
>>>
使用元组作为键是创建组合键的常用技术。