Python 常见数据结构

5.1. More on Lists¶

列表数据类型还有一些更多的方法。以下是列表对象的所有方法:

list.append(x)

将项目添加到列表末尾。相当于 a[len(a):] = [x]。

list.extend(iterable)

通过附加可迭代对象中的所有项目来扩展列表。相当于 a[len(a):] = iterable。

list.insert(i, x)

在给定位置插入项目。第一个参数是要在其之前插入的元素的索引,因此 a.insert(0, x) 在列表的前面插入,并且 a.insert(len(a), x) 相当于 a.append( X)。

list.remove(x)

从列表中删除第一个值等于 x 的项目。如果没有这样的项目,它会引发 ValueError。

list.pop([i])

删除列表中给定位置的项目并将其返回。如果未指定索引,a.pop() 将删除并返回列表中的最后一项。如果列表为空或索引超出列表范围,则会引发 IndexError。

list.clear()

从列表中删除所有项目。相当于 del a[:]。

list.index(x[, start[, end]])

返回列表中第一个值等于 x 的项目的从零开始的索引。如果不存在这样的项目,则引发 ValueError。

可选参数 start 和 end 被解释为切片符号,并用于将搜索限制为列表的特定子序列。返回的索引是相对于完整序列的开头而不是起始参数计算的。

list.count(x)

返回 x 在列表中出现的次数。

list.sort(*, key=None, reverse=False)

对列表中的项目进行排序(参数可用于自定义排序,请参阅sorted() 了解其说明)。

list.reverse()

将列表中的元素反转到位。

list.copy()

返回列表的浅表副本。相当于a[:]。
  • 使用大部分列表方法的示例:
fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
fruits.count('apple')

fruits.count('tangerine')

fruits.index('banana')

fruits.index('banana', 4)  # Find next banana starting at position 4

fruits.reverse()
fruits

fruits.append('grape')
fruits

fruits.sort()
fruits

fruits.pop()

您可能已经注意到,仅修改列表的插入、删除或排序等方法没有打印返回值——它们返回默认的 None。 这是Python中所有可变数据结构的设计原则。
您可能会注意到的另一件事是,并非所有数据都可以排序或比较。例如,[None, 'hello', 10] 不会排序,因为整数无法与字符串进行比较,而 None 无法与其他类型进行比较。此外,有些类型没有定义的排序关系。例如,3+4j < 5+7j 不是有效的比较。

1.1. Using Lists as Stacks

列表方法使将列表用作堆栈变得非常容易,其中最后添加的元素是检索到的第一个元素(“后进先出”)。要将项目添加到堆栈顶部,请使用append()。要从堆栈顶部检索项目,请使用 pop() 而不使用显式索引。例如:

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

stack.pop()

stack

stack.pop()

stack.pop()

stack

1.2. Using Lists as Queues¶

也可以将列表用作队列,其中添加的第一个元素是检索到的第一个元素(“先进先出”);然而,列表对于此目的效率不高。虽然从列表末尾追加和弹出的速度很快,但从列表开头插入或弹出的速度很慢(因为所有其他元素都必须移动一位)。
要实现队列,请使用 collections.deque,它的设计目的是从两端快速追加和弹出。例如:

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

queue.popleft()                 # The second to arrive now leaves

queue                           # Remaining queue in order of arrival

1.3. List Comprehensions¶

列表推导式提供了一种创建列表的简洁方法。常见的应用是创建新列表,其中每个元素都是应用于另一个序列或可迭代的每个成员的某些操作的结果,或者创建满足特定条件的这些元素的子序列。
例如,假设我们要创建一个正方形列表,例如:

squares = []
for x in range(10):
    squares.append(x**2)

squares

请注意,这将创建(或覆盖)一个名为 x 的变量,该变量在循环完成后仍然存在。我们可以使用以下方法计算没有任何副作用的正方形列表:

squares = list(map(lambda x: x**2, range(10)))

或者

squares = [x**2 for x in range(10)]

这更加简洁和可读。
列表推导式由包含表达式的括号组成,后跟 f o r for for 子句,然后是零个或多个 f o r for for i f if if 子句。结果将是一个新列表,该列表是在其后面的 f o r for for i f if if 子句的上下文中评估表达式而产生的。例如,如果两个列表的元素不相等,则此 l i s t c o m p listcomp listcomp 会合并它们:

[(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)]

等价于:

combs = []
for x in [1,2,3]:
    for y in [3,1,4]:
        if x != y:
            combs.append((x, y))

combs

请注意这两个片段中 for 和 if 语句的顺序是如何相同的。
如果表达式是元组(例如上例中的 (x, y)),则必须将其放在括号中。

vec = [-4, -2, 0, 2, 4]
# create a new list with the values doubled
[x*2 for x in vec]

# filter the list to exclude negative numbers
[x for x in vec if x >= 0]

# apply a function to all the elements
[abs(x) for x in vec]

# call a method on each element
freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
[weapon.strip() for weapon in freshfruit]

# create a list of 2-tuples like (number, square)
[(x, x**2) for x in range(6)]

# the tuple must be parenthesized, otherwise an error is raised
[x, x**2 for x in range(6)]
  File "<stdin>", line 1
    [x, x**2 for x in range(6)]
     ^^^^^^^
SyntaxError: did you forget parentheses around the comprehension target?
# 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]

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

from math import pi

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

1.4. Nested List Comprehensions¶

列表推导式中的初始表达式可以是任何任意表达式,包括另一个列表推导式。
考虑以下 3x4 矩阵的示例,该矩阵实现为由 3 个长度为 4 的列表组成的列表:

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]]

正如我们在上一节中看到的,内部列表理解是在其后面的 for 上下文中进行评估的,因此此示例相当于:

transposed = []
for i in range(4):
    transposed.append([row[i] for row in matrix])

transposed

same as

transposed = []
for i in range(4):
    # the following 3 lines implement the nested listcomp
    transposed_row = []
    for row in matrix:
        transposed_row.append(row[i])
    transposed.append(transposed_row)

transposed

在现实世界中,您应该更喜欢内置函数而不是复杂的流语句。 zip() 函数非常适合这个用例:

list(zip(*matrix))

2. The del statement¶

有一种方法可以从给定索引而不是值的列表中删除项目:del 语句。这与返回值的 pop() 方法不同。 del 语句还可用于从列表中删除切片或清除整个列表(我们之前通过将空列表分配给切片来完成此操作)。例如:

a = [-1, 1, 66.25, 333, 333, 1234.5]
del a[0]
a

del a[2:4]
a

del a[:]
a

del 也可用于删除整个变量:

del a

此后引用名称 a 是错误的(至少在为其分配另一个值之前)。稍后我们会发现 del 的其他用途。

3. Tuples and Sequences

我们看到列表和字符串有许多共同的属性,例如索引和切片操作。它们是序列数据类型的两个示例(请参阅序列类型 — 列表、元组、范围)。由于 Python 是一种不断发展的语言,因此可能会添加其他序列数据类型。还有另一种标准序列数据类型:元组。
元组由多个用逗号分隔的值组成,例如:

t = 12345, 54321, 'hello!'
t[0]

t

# Tuples may be nested:
u = t, (1, 2, 3, 4, 5)
u

# Tuples are immutable:
t[0] = 88888


# but they can contain mutable objects:
v = ([1, 2, 3], [3, 2, 1])
v

如您所见,输出元组始终括在括号中,以便正确解释嵌套元组;它们可以带或不带括号输入,尽管括号通常是必需的(如果元组是较大表达式的一部分)。不可能分配给元组的各个项目,但是可以创建包含可变对象的元组,例如列表。
尽管元组看起来与列表相似,但它们经常用于不同的情况和不同的目的。元组是不可变的,通常包含通过解包(请参阅本节后面部分)或索引(甚至在命名元组的情况下通过属性)访问的异构元素序列。列表是可变的,它们的元素通常是同质的,并且可以通过迭代列表来访问。
一个特殊的问题是包含 0 或 1 个项目的元组的构造:语法有一些额外的怪癖来适应这些。空元组由一对空括号构造;包含一项的元组是通过在值后面加上逗号来构造的(将单个值括在括号中是不够的)。丑陋,但有效。例如:

empty = ()
singleton = 'hello',    # <-- note trailing comma
len(empty)

len(singleton)

singleton

语句 t = 12345, 54321, '你好!'是元组打包的示例:值 12345、54321 和 'hello!'被打包在一个元组中。也可以进行相反的操作:

x, y, z = t

这被称为序列解包,适用于右侧的任何序列。序列解包要求等号左侧的变量数量与序列中的元素数量一样多。请注意,多重赋值实际上只是元组打包和序列拆包的组合。

4. Sets¶

Python 还包含集合的数据类型。集合是无重复元素的无序集合。基本用途包括成员资格测试和消除重复条目。集合对象还支持数学运算,例如并集、交集、差值和对称差值。
大括号或 set() 函数可用于创建集合。注意:要创建一个空集,您必须使用 set(),而不是 {};后者创建一个空字典,这是我们在下一节中讨论的数据结构。
这是一个简短的演示:

basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
print(basket)                      # show that duplicates have been removed

'orange' in basket                 # fast membership testing

'crabgrass' in basket


# Demonstrate set operations on unique letters from two words

a = set('abracadabra')
b = set('alacazam')
a                                  # unique letters in a

a - b                              # letters in a but not in b

a | b                              # letters in a or b or both

a & b                              # letters in both a and b

a ^ b                              # letters in a or b but not both
a = {x for x in 'abracadabra' if x not in 'abc'}

a
{'r', 'd'}

5. Dictionaries

Python 中内置的另一个有用的数据类型是字典(请参阅映射类型 — dict)。字典有时在其他语言中被称为“关联存储器”或“关联数组”。与通过数字范围索引的序列不同,字典通过键索引,键可以是任何不可变类型;字符串和数字始终可以作为键。如果元组仅包含字符串、数字或元组,则可以将其用作键;如果元组直接或间接包含任何可变对象,则它不能用作键。您不能使用列表作为键,因为可以使用索引分配、切片分配或诸如append()和extend()之类的方法就地修改列表。
最好将字典视为一组键:值对,并要求键是唯一的(在一个字典内)。一对大括号创建一个空字典:{}。将逗号分隔的键:值对列表放在大括号内会将初始键:值对添加到字典中;这也是字典在输出上的写入方式。
字典的主要操作是存储带有某个键的值并提取给定键的值。也可以使用 del 删除键:值对。如果您使用已在使用的密钥进行存储,则与该密钥关联的旧值将被忘记。使用不存在的键提取值是错误的。
对字典执行 list(d) 会返回字典中使用的所有键的列表,按插入顺序排列(如果您希望对其进行排序,只需使用排序(d)即可)。要检查单个键是否在字典中,请使用 in 关键字。
这是一个使用字典的小例子:

tel = {'jack': 4098, 'sape': 4139}
tel['guido'] = 4127
tel

tel['jack']

del tel['sape']
tel['irv'] = 4127
tel

list(tel)

sorted(tel)

'guido' in tel

'jack' not in tel

dict() 构造函数直接从键值对序列构建字典:

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

此外,字典推导式可用于从任意键和值表达式创建字典:

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

当键是简单字符串时,有时使用关键字参数指定对会更容易:

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

6. Looping Techniques¶

循环字典时,可以使用 items() 方法同时检索键和相应的值。

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

循环序列时,可以使用 enumerate() 函数同时检索位置索引和相应的值。

for i, v in enumerate(['tic', 'tac', 'toe']):

    print(i, v)


0 tic
1 tac
2 toe

要同时循环两个或多个序列,可以将条目与 zip() 函数配对。

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() 函数。

for i in reversed(range(1, 10, 2)):

    print(i)

要按排序顺序循环序列,请使用sorted()函数,该函数返回一个新的排序列表,同时保持源不变。

basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']

for i in sorted(basket):

    print(i)


apple
apple
banana
orange
orange
pear

在序列上使用 set() 可以消除重复元素。在序列上结合使用 Sorted() 和 set() 是按排序顺序循环序列中唯一元素的惯用方法。

basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']

for f in sorted(set(basket)):

    print(f)


apple
banana
orange
pear

有时,在循环遍历列表时很容易更改列表;然而,创建一个新列表通常更简单、更安全。

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]
  • 23
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂的码泰君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值