Skip to content

Python 学习笔记 5. 数据结构

5.1 列表的更多特性

  • append(x) :在列表的末尾添加一个元素。
  • extend(iterable) :使用可迭代对象中的所有元素来扩展列表。
  • insert(i, x) :在给定的位置插入一个元素。
  • remove(x) :移除列表中第一个值为 x 的元素。
  • pop([i]) :删除列表中给定位置的元素并返回它。
  • clear() :删除列表中所有的元素。如果没有给定位置,将会删除并返回列表中的最后一个元素。
  • index(x[, start[, end]]) :返回列表中第一个值为 x 的元素的从零开始的索引。
  • count(x) :返回元素 x 在列表中出现的次数。
  • sort(key=None, reverse=False) :对列表中的元素进行排序
  • reverse() :反转列表中的元素。
  • copy() :返回列表的一个浅拷贝。
python
>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
>>> fruits.count('apple')
2
>>> fruits.count('tangerine')
0
>>> fruits.index('banana')
3
>>> fruits.index('banana', 4)  # Find next banana starting a position 4
6
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
>>> fruits.append('grape')
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
>>> fruits.sort()
>>> fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
>>> fruits.pop()
'pear'

5.1.1. 列表作为栈使用

“后进先出”

使用 append()pop() 方法实现。

python
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]

5.1.2. 列表作为队列使用

“先进先出”

原生的列表实现起来性能较差,改为使用 collections.dequecollections.deque 被设计用于快速地从两端操作。

python
>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry")           # Terry arrives
>>> queue.append("Graham")          # Graham arrives
>>> queue.popleft()                 # The first to arrive now leaves
'Eric'
>>> queue.popleft()                 # The second to arrive now leaves
'John'
>>> queue                           # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])

5.1.3. 列表推导式

列表推导式的结构是由一对方括号所包含的以下内容:一个表达式,后面跟一个 for 子句,然后是零个或多个 forif 子句。

其结果将是一个新列表,由对表达式依据后面的 forif 子句的内容进行求值计算而得出。

创建一个平方列表:

python
>>> squares = [x**2 for x in range(10)]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

组合两个数组中不相等的元素:

python
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

其它一些示例:

python
>>> vec = [-4, -2, 0, 2, 4]
>>> # create a new list with the values doubled
... [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # filter the list to exclude negative numbers
... [x for x in vec if x >= 0]
[0, 2, 4]
>>> # apply a function to all the elements
... [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # call a method on each element
... freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> # create a list of 2-tuples like (number, square)
... [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # the tuple must be parenthesized, otherwise an error is raised
... [x, x**2 for x in range(6)]
  File "<stdin>", line 2
    [x, x**2 for x in range(6)]
               ^
SyntaxError: invalid syntax
>>> # flatten a list using a listcomp with two 'for'
... vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

列表推导式可以使用复杂的表达式和嵌套函数:

python
>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']

5.1.4. 嵌套的列表推导式

交换矩阵的行和列:

python
>>> matrix = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 11, 12],
... ]
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

上述的代码有一个内置的函数 zip(*iterables)

python
>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

5.2. del 语句

从列表按照给定的索引来移除一个元素。

python
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]

del 也可以被用来删除整个变量。

python
>>> a = []
>>> a
[]
>>> del a
>>> a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

5.3. 元组和序列

一个元组由 几个被逗号隔开的值 组成。

python
>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples may be nested:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> # Tuples are immutable:
... t[0] = 88888
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: 'tuple' object does not support item assignment
>>> # but they can contain mutable objects:
... v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])

元组在输出时总是被圆括号包围的,以便正确表示嵌套元组。
输入时圆括号可有可无,不过经常会是必须的(如果这个元组是一个更大的表达式的一部分)。

元组是 immutable (不可变的)。

空元组可以直接被一对空圆括号创建,含有一个元素的元组可以通过在这个元素后添加一个逗号来构建(圆括号里只有一个值的话不够明确)。

python
>>> empty = ()
>>> singleton = 'hello',    # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)

序列解包

python
>>> t = 12345, 54321, 'hello!'
>>> x, y, z = t
>>> x
12345
>>> y
54321
>>> z
'hello!'

5.4. 集合

集合是由 不重复 元素组成的 无序 的集。

花括号或 set() 函数可以用来创建集合。
注意:要创建一个空集合你只能用 set() 而不能用 {},因为后者是创建一个空字典,

python
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)                      # show that duplicates have been removed
{'pear', 'orange', 'banana', 'apple'}
>>> 'orange' in basket                 # fast membership testing
True
>>> 'crabgrass' in basket
False
>>> # Demonstrate set operations on unique letters from two words
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  # unique letters in a
{'d', 'c', 'b', 'a', 'r'}
>>> a - b                              # letters in a but not in b
{'b', 'd', 'r'}
>>> a | b                              # letters in a or b or both
{'d', 'c', 'b', 'a', 'l', 'r', 'm', 'z'}
>>> a & b                              # letters in both a and b
{'a', 'c'}
>>> a ^ b                              # letters in a or b but not both
{'m', 'd', 'r', 'z', 'b', 'l'}

集合也支持推导式形式:

python
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}

5.5. 字典

字典是以 关键字 为索引的,关键字可以是 任意不可变类型 ,通常是字符串或数字。

如果一个元组只包含字符串、数字或元组,那么这个元组也可以用作关键字。
列表不能用作关键字。

python
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel)
['jack', 'guido', 'irv']
>>> sorted(tel)
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False

dict() 构造函数可以直接从键值对序列里创建字典。

python
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}

字典推导式可以从任意的键值表达式中创建字典。

python
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

可以直接通过 关键字参数 来指定键值对。

python
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}

5.6. 循环的技巧

当在字典中循环时,用 items() 方法可将关键字和对应的值同时取出。

python
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
...     print(k, v)
...
gallahad the pure
robin the brave

当在序列中循环时,用 enumerate() 函数可以将索引位置和其对应的值同时取出。

python
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print(i, v)
...
0 tic
1 tac
2 toe

当同时在两个或更多序列中循环时,可以用 zip() 函数将其内元素一一匹配。

python
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.

当逆向循环一个序列时,先正向定位序列,然后调用 reversed() 函数。

python
>>> for i in reversed(range(1, 10, 2)):
...     print(i)
...
9
7
5
3
1

如果要按某个指定顺序循环一个序列,可以用 sorted() 函数,它可以在不改动原序列的基础上返回一个新的排好序的序列。

python
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
...     print(f)
...
apple
banana
orange
pear

有时可能会想在循环时修改列表内容,一般来说改为创建一个新列表是比较简单且安全的。

python
>>> import math
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = []
>>> for value in raw_data:
...     if not math.isnan(value):
...         filtered_data.append(value)
...
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]

5.7. 深入条件控制

whileif 条件句中可以使用任意操作,而不仅仅是比较操作。

比较操作符 innot in 校验一个值是否在(或不在)一个序列里。操作符 isis not 比较两个对象是不是同一个对象,这只跟像列表这样的可变对象有关。所有的比较操作符都有相同的优先级,且这个优先级比数值运算符低。

*比较操作可以传递。*例如 a < b == c 会校验是否 a 小于 b 并且 b 等于 c

比较操作可以通过布尔运算符 andor 来组合,并且比较操作(或其他任何布尔运算)的结果都可以用 not 来取反。这些操作符的优先级低于比较操作符;在它们之中,not 优先级最高, or 优先级最低,因此 A and not B or C 等价于 (A and (not B)) or C。和之前一样,你也可以在这种式子里使用圆括号。

布尔运算符 andor 也被称为 短路 运算符:它们的参数从左至右解析,一旦可以确定结果解析就会停止。
例如,如果 AC 为真而 B 为假,那么 A and B and C 不会解析 C当作用于普通值而非布尔值时,短路操作符的返回值通常是最后一个变量。

也可以把比较操作或者逻辑表达式的结果赋值给一个变量,例如

python
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'

比较操作符作用与非布尔型变量时的处理同大多数其它语言不通,大部分的编程语言布尔操作的结果都是布尔型,但 python 不一样,返回的是 最后一个变量

关于上面的例子,我的理解如下(初学 python , 不知是否正确):
由于 string1 值为 '',转化为布尔型值为 False
因为用的 or 操作符所以会继续解析 string2
string2 转化为布尔型结果为 True ,然后整个布尔运算返回的就是 string2 这个变量,而不是这个变量解析后的布尔值。

5.8. 序列和其它类型的比较

序列对象通常可以与相同序列类型的其他对象比较。

这种比较使用 字典式 顺序:

  • 首先比较开头的两个对应元素,如果两者不相等则比较结果就由此确定;
  • 如果两者相等则比较之后的两个元素,以此类推,直到有一个序列被耗尽。
  • 如果要比较的两个元素本身又是相同类型的序列,则会递归地执行字典式顺序比较。
  • 如果两个序列中所有的对应元素都相等,则两个序列也将被视为相等。
  • 如果一个序列是另一个的初始子序列,则较短的序列就被视为较小(较少)。对于字符串来说,字典式顺序是使用 Unicode 码位序号对单个字符排序。

下面是一些相同类型序列之间比较的例子:

python
>>> (1, 2, 3)              < (1, 2, 4)
True
>>> [1, 2, 3]              < [1, 2, 4]
True
>>> 'ABC' < 'C' < 'Pascal' < 'Python'
True
>>> (1, 2, 3, 4)           < (1, 2, 4)
True
>>> (1, 2)                 < (1, 2, -1)
True
>>> (1, 2, 3)             == (1.0, 2.0, 3.0)
True
>>> (1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)
True

关于 'ABC' < 'C' < 'Pascal' < 'Python' 我的理解是其等价于 'ABC' < 'C' and 'C' < 'Pascal' and 'Pascal' < 'Python' ,不知道理解的是否正确。
按照上一节的说法: 比较操作可以传递 ,应该是对的。

Page Layout Max Width

Adjust the exact value of the page width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the page layout
A ranged slider for user to choose and customize their desired width of the maximum width of the page layout can go.

Content Layout Max Width

Adjust the exact value of the document content width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the content layout
A ranged slider for user to choose and customize their desired width of the maximum width of the content layout can go.