跳至主要内容

Python 列表最常见的 18 个问题

了解如何在 Python 中创建列表、选择元素、append() 与 extend() 的区别、为何使用 NumPy,以及更多内容。
· 15分钟 阅读

使用 AI 探索

本文将提供简单的解决方案、易于理解的直观解释,以及一些您可以借助交互式练习即刻上手的技巧!

如果您希望通过课程系统地学习列表,建议查看我们的 数据科学 Python 入门 课程,其中有一章专门讲解 Python 列表。

背景

列表是刚开始学习 Python 以及已经有一定经验的开发者都越来越关注的话题。如果您相信 Google 趋势 的搜索结果,您会发现对此主题的搜索兴趣每年都在上升。

如果您经常访问一些用于提问或回答 Python 编程问题的论坛,比如 Stack Overflow、Quora 或 Reddit,您也许知道其中的原因。

许多 Python 问题都会到这些论坛上来,并持续发酵;用户会将部分问题标记为“重复”或“置顶”,为其投票,或者与他人讨论正确的解决方案。

通过这篇博文,DataCamp 希望以一种互动的方式,帮助您攻克一个主题:关于 Python 列表的常见问题。

Python 列表到底是什么?

列表是 Python 中四种内置数据结构之一,另外三种是 元组、字典和集合。列表用于存储有序的元素集合,这些元素可以是不同类型,但通常并不会这么用。列表中的元素以逗号分隔,并用方括号括起来。如下例所示:

# These list elements are all of the same type
zoo = ['bear', 'lion', 'panda', 'zebra']
print(zoo)

# But these list elements are not
biggerZoo = ['bear', 'lion', 'panda', 'zebra', ['chimpanzees', 'gorillas', 'orangutans', 'gibbons']]
print(biggerZoo)

您会看到第二个变量 biggerZoo 与 zoo 类似,但同时也包含了另一个列表,其中列出了不同种类的猴子。

由于 Python 列表存储的是有序的元素或对象集合,我们可以说它们属于序列类型,因为它们的行为像一个序列。其他也被认为是序列类型的还有字符串和元组。

您也许会想,序列类型到底有什么特别之处?简单来说,这意味着程序可以对其进行迭代!这就是为什么列表、字符串、元组和集合被称为“可迭代对象”。

请记住,这个概念非常重要,您在其他用于数据科学的编程语言(例如 Scala)中也可能会再次遇到它!

现在进入正题!让我们深入探讨作为 Python 程序员,哪些关于列表的问题可能困扰过您。以下是本教程将回答的所有问题列表:

1. 何时使用 Python 列表,何时使用元组、字典或集合

只看介绍时似乎很直观,但当您真正编写一个小脚本或做一个项目时,在列表与其他序列类型之间做选择也许并不那么明确。

然而,为您的数据选择合适的数据结构至关重要!

继续阅读了解更多。

列表 vs 元组

元组用于收集不可变的有序元素列表。这意味着:

  • 您不能向元组添加元素。元组没有 append() 或 extend() 方法,
  • 您不能从元组中移除元素。元组没有 remove() 或 pop() 方法,
  • 您可以在元组中查找元素,因为这不会改变元组。
  • 您也可以使用 in 运算符检查某元素是否存在于元组中。

因此,如果您要定义一组常量值,且只会对其进行迭代,使用元组而不是列表。它比列表更快,也更安全,因为元组的数据是“写保护”的。

列表 vs 字典

  • 列表存储有序的元素集合,因此保留顺序。字典则不保证顺序。
  • 字典是键到值的映射,而列表只包含值。

当您有一组无序且唯一的键映射到值时,使用字典。

注意,由于键和值彼此关联,在检查元素成员资格时,字典的性能通常优于列表。

列表 vs 集合

  • 与字典类似,集合中元素是无序的,而列表是有序的。
  • 集合要求其包含的元素可哈希,列表则可以存储不可哈希的元素。
  • 集合要求元素唯一且不可变。集合不允许重复元素,而列表允许重复且可变。

当您有一组无序、唯一、不可变且可哈希的值时,应使用集合。

不确定哪些值是可哈希的?

请看下表以确认:

可哈希不可哈希
浮点数字典
整数集合
元组列表
字符串 
frozenset() 

提示:不要只听我们说!在下方自己动手试试,并在 IPython shell 中多做实验 :)

# Import the `collections` library
____ ____

# Check if a dictionary is hashable
print(isinstance({}, collections.Hashable))

# Check if a float is hashable
print(isinstance(0.125, collections.Hashable))

注意,由于您使用的是可哈希元素,在检查成员资格(测试某个元素是否属于序列)时,集合通常会比列表更快。

2. 如何从列表中选取元素

如果您想要正确地使用这些列表,就需要知道如何访问它们。

您通常会访问列表来更改、更新或删除某些值,或对其执行其他操作。访问列表以及所有其他序列类型的方式,是使用索引运算符 [ ]。方括号中放入一个整数值。

来看一个示例:

# Select the first list element
oneZooAnimal = biggerZoo[0]

# Print `oneZooAnimal`
print(____)

运行代码看看返回了什么。您会注意到返回的是 biggerZoo 的第一个元素。尽管起初可能有点令人困惑或意外,但索引是从 0 开始而不是 1。

如何获取列表中的最后一个元素

这个问题是在上一小节说明的补充。

尝试在索引运算符中使用负数,比如 -1 或 -2,以检索 biggerZoo 列表的最后一个元素!

# Pass -1 to the index operator on `biggerZoo`
monkeys = ___
print(monkeys)

# Pass -2 to the index operator on `biggerZoo`
zebra = ___
print(zebra)

并不难,对吧?

“Index out of range” 错误是什么意思?

这个错误您会经常见到,尤其当您是编程或 Python 新手时。

理解该错误最好的方式就是自己试一下。

使用上面的交互式学习挂件,尝试传入特别小的负数或特别大的正数!

如您所见,当您传递给索引运算符的整数大于或远小于列表长度时,可能会出现 “Index Out Of Range” 错误!这意味着您引用了一个不存在(尚未存在)的索引或在该位置赋值。

下面这段代码会触发 “Index Out Of Range” 错误。运行它亲自看看!

# Run this code to trigger an "Index Out Of Range" Error
biggerZoo[6]

列表中的切片表示法

当您刚接触编程或 Python 时,这可能是一个特别容易混淆的话题。

一般来说,当您想一次选择多个列表元素时,就会用到切片表示法。与只从列表中选择一个元素相似,您会使用方括号。特别之处在于,此时除了整数之外,您还要在方括号内放一个 :,像这样:

# Use the slice notation like this
someZooAnimals = biggerZoo[2: ]

# Print to see what you exactly select from `biggerZoo`
print(someZooAnimals)

# Try putting 2 on the other side of the colon
otherZooAnimals = biggerZoo[____]

# Print to see what you're getting back
print(otherZooAnimals)

您会发现,我们得到了 biggerZoo 列表中从索引 2 的 panda 开始到末尾的部分。换言之,我们从索引 2 开始,一直到列表末尾,因为没有指定结束索引。

如果反过来会怎样呢?

运行上面的代码块看看您是否猜对了!

您会看到现在只返回了 bear 和 lion。现在我们从索引 0 开始到索引 2。与预期相反,结果不包含 panda。

一般而言,遵循以下规则:

# items start through the end (but the end is not included!)
a[start:end]

# items start through the rest of the array
a[start:]    

# items from the beginning through the end (but the end is not included!)
a[:end]

提示:只传入一个冒号 :,就会复制整个列表!

除了基础的切片表示法外,还有步长(step)值,可以概括为:

# start through not past end, by step
a[start:end:step]

那么步长到底是做什么的?

它允许您按步长“跳跃”地遍历列表,只选择被步长覆盖到的那些元素。例如:

# Print to see how the step value influences your result
print(biggerZoo[2::2])
print(biggerZoo[1::3])

注意,如果不指定步长,默认值为 1。此时遍历列表时不会跳过任何元素。

请记住,切片表示法中指定的结束索引本身不包含在结果中!

如何随机选择列表中的元素

您可以使用 random 包从列表中随机选择一个元素:

# Import `choice` from the `random` library
from ____ import ____

# Construct your `list` variable with a list of the first 4 letters of the alphabet
list = ['_','_','_','_']

# Print your random 'list' element
print(choice(list))

如果您还想通过列表的索引来随机选择一个元素,可以使用 random 库中的 randrange 方法:

# Import `randrange` from the `random` library
from ____ import ____

# Construct your `randomLetters` variable with a list of the first 4 letters of the alphabet
randomLetters = ['_','_', '_', '_']

# Select a random index from 'randomLetters`
randomIndex = randrange(0,len(randomLetters))

# Print your random element from `random`
print(randomLetters[randomIndex])

提示:建议了解一下 random 库,在 Python 编程时会很有用!

3. 如何将 Python 列表转换为其他数据结构

有时,列表并不是您所需要的。在这些情况下,您可能希望知道如何将列表转换为更合适的数据结构。下面列出了一些最常见的转换问题及其答案。

如何将列表转换为字符串

可以使用 ''.join() 将列表转换为字符串。该操作会把列表中的所有字符串拼接起来并返回一个字符串。更多信息见此处。

# List of Strings to a String
listOfStrings = ['One', 'Two', 'Three']
strOfStrings = ''.join(listOfStrings)
print(strOfStrings)

# List Of Integers to a String
listOfNumbers = [1, 2, 3]
strOfNumbers = ''.join(str(n) for n in listOfNumbers)
print(strOfNumbers)

注意,如果列表只包含整数,则应先将元素转换为字符串,再进行拼接。上方交互式代码块的第二个示例演示了这一点。

从整数到字符串的转换,是通过遍历原始 listOfNumbers 列表完成的。如需进一步了解如何遍历列表,请查看问题 12 及其答案。

如何将列表转换为元组

在 Python 中可以使用 tuple() 函数将列表转换为元组。把列表作为参数传递给它,您就会得到一个元组!

请记住:元组是不可变的。转换后您无法再修改它!

提示:您可以在下一节的交互练习中尝试将 listOfStrings 转换为元组。

如何将列表转换为集合

如您所记得,集合是无序的唯一元素集合。这意味着,一旦将列表转换为集合,原列表中的任何重复元素都会被去除,而且列表元素的顺序也会丢失。

可以使用 set() 函数将列表转换为集合。只需把列表传给它即可!

现在在这里练习将 listOfStrings 转换为元组和集合:

# Pass your list to `tuple()`
tuple(___)

# Transform your list into a set
set(___)

如何将列表转换为字典

字典使用键和值,因此从列表到字典的转换可能没那么直接。假设您有一个这样的列表:

helloWorld = ['hello','world','1','2']

您需要确保 'hello' 与 'world',以及 '1' 与 '2' 被解释为键值对。做法是用切片选出它们并传给 zip()。

zip() 的作用正如其名:把元素拉链式地配对。在本例中,当您对 helloWorld[0::2] 和 helloWorld[1::2] 做 zip 时,输出将是:

list(zip(helloWorld))

注意,需要用 list() 才能打印出 zip() 函数的结果。

接着将其传递给 dict() 函数,hello 会被解释为键,world 为值。同样,1 为键,2 为值。

运行下方代码确认一下:

# Convert to a dictionary
helloWorldDictionary = dict(zip(helloWorld[0::2], helloWorld[1::2]))

# Print out the result
print(helloWorldDictionary)

注意,传给 zip() 的第二个元素使用了步长值,以确保只选择 world 和 2。同理,第一个元素的步长选择了 hello 和 1。

如果列表很大,您可能会用类似下面的方法:

a = [1, 2, 3, 4, 5]

# Create a list iterator object
i = iter(a)

# Zip and create a dictionary
print(dict(zip(i, i)))

注意,可迭代对象可以生成迭代器。迭代器具有 .__next__ 方法,保存了迭代中的当前位置:它知道下一个元素是什么。

4. 如何确定列表的大小

可以将列表传给 len() 函数以获得列表长度。

在下面试一试!我们已经为您准备了一个 justAList 列表可供使用。

# Pass `justAList` to `len()`
len(____)

注意,len() 并不仅用于获取列表长度,也可用于其他序列或集合,例如字典、集合、字符串等。更多内置函数见这里。

5. Python 的 append() 与 extend() 有何区别?

先回顾一下开头提到的“可迭代对象”概念:

请记住,当程序可以对某个值进行迭代时,我们称其为可迭代。换言之,可迭代对象代表一个或多个值构成的序列。正如您在第一节中看到的,列表是序列类型,Python 中所有序列类型的实例都是可迭代的。

提示:您可以检查变量是否可迭代——看它是否有 .__iter__ 方法,例如下面这段代码:

# This is your list
list = [5, 6, 7, 8, 9]

# Check whether it's iterable
list.__iter__

运行上面的代码,并在 IPython shell 中试试其他简单示例!

现在可迭代对象的概念已经清晰了,我们可以开始理解这两个方法的区别。extend() 接受一个可迭代对象(没错,它可以是列表、集合、元组或字符串!),并将该可迭代对象中的每个元素依次添加到列表中。

而 append() 会将其参数作为单个元素追加到列表末尾,这意味着当 append() 的参数是可迭代对象时,它会把整个对象当作一个元素处理。

学习下面的代码块,并自己动手尝试,以理解这两个方法的差异:

# Append [4,5] to `shortList`
shortList.append([4, 5])

# Use the print() method to show shortList
print(____)

# Extend `longerList` with [4,5]
longerList.extend([4, 5])

# Use the print() method to see longerList
print(____)

您可以看到,extend() 比多次调用 append() 更高效。

6. 如何拼接列表

要拼接列表,使用 + 运算符。它会返回一个新的列表,包含两个列表的拼接结果,并且不会修改原列表。

注意,拼接与追加可能会让人困惑。append() 与 + 运算符不同,而 + 与 extend() 又很相似,但并不完全相同……使用 append() 和 extend() 会修改原列表,而 + 运算符可以创建一个新的列表变量。

自己试试,并与上一代码块中的示例进行比较:

# Concatenate `shortList` with `[4,5]`
plusList = shortList + [4,5]

#Use the `print()` method to see `plusList`
print(____)

7. 如何对列表排序

有两种非常简单的方法可以将列表中的值按升序或降序排序:

  • 使用 sort() 方法
  • 或使用 sorted() 函数并将列表传给它

sorted() 可以应用于任何可迭代对象,这意味着它也接受字符串、集合和字典(当它们被传入时)!

提示:自己来试试吧!我们已经创建了两个列表 rooms 和 orders,您可以用上面两种技术进行排序 :)

# Use sort() on the rooms list
____.sort()

# Print out rooms to see the result
print(____)

# Now use the sorted() function on orders
sorted(____)

# Print out orders
print(____)

注意,排序后原列表会被修改。建议在不再需要原列表时使用这种方式。

8. 如何克隆或复制列表

复制或克隆列表的方法很多:

  • 您可以切片原列表并存入新变量:newList = oldList[:]
  • 可以使用内置的 list() 函数:newList = list(oldList)
  • 可以使用 copy 库:
    • 使用 copy() 方法:newList = copy.copy(oldList)
    • 如果列表包含对象且您希望也复制这些对象,可以使用 copy.deepcopy():copy.deepcopy(oldList)

在下面试试吧!我们已经创建了一个 groceries 列表,您可以复制并分享给朋友。

# Copy the grocery list by slicing and store it in the `newGroceries` variable
newGroceries = groceries[__]

# Copy the grocery list with the `list()` function and store it in a `groceriesForFriends` variable
groceriesForFriends = list(____)

# Import the copy library as c
import ____ as __

# Create a `groceriesForFamily` variable and assign the copied grocery list to it
groceriesForFamily =  c.copy(____)

# Use `deepcopy()` and assign the copied list to a `groceriesForKids` variable
groceriesForKids= c.deepcopy(____)

注意,当您使用“简单”的复制方法时,原列表会被修改;而使用 deepcopy() 方法则可以避免这种情况。运行下面的代码看看会发生什么:

# This is your list
objectList = ['a','b',['ab','ba']]

# Copy the `objectList`
copiedList = objectList[:]

# Change the first list element of `copiedList`
copiedList[0] = 'c'

# Go to the third element (the nested list) and change the second element
copiedList[2][1] = 'd'

# Print out the original list to see what happened to it
print(objectList)

如果您已经把 Python 列表的知识提升到了更深入的层次,下面的章节会为您的一些复杂问题提供简洁的答案。

不过,如果您想知道除了列表之外还有什么,或是内置列表结构之外的库能提供哪些优势,下一节可能更适合您。

继续阅读,了解更多!

9. Python 中的列表推导式如何工作?

简单来说,列表推导式是一种优雅构造列表的方式。对于热爱数学的人来说,最棒的是它看起来很像数学中的集合/列表。自己感受一下:

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

这是什么意思呢?您取 0 到 10(不含 10)之间的所有数字,并将其中每个数字都乘以 2。运行上面的代码看看结果是否符合预期。

并不难,对吧?我们再来进阶一点!

假设您想构造与之前相同的列表,但只想把能被 2 整除的数字拿来乘 2。有了列表推导式,这也很简单:

[x**2 for x in range(10) if x%2==0]

您只需在原表达式中添加一个条件,用 if 表示。如果条件为 TRUE,就会将该数字乘以 2。在这里,我们的条件是数字能够被 2 整除(即除法运算没有余数)。

从更一般的角度说,您也可以使用列表推导式将一个列表转换为另一个列表,听起来更难,对吗?

看看下面的例子:

myList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[(lambda x: x*x)(x) for x in myList]

在这个例子中,您已经有了一个列表 myList,并通过列表推导式对其进行转换:再次看到 for x in myList,意味着对 myList 中的每个元素执行某些操作。

接着,您看到包含了 (lambda x: x*x)。为简单起见,您只需知道 lambda 函数是运行时构造的匿名函数。把那段代码视为一个提示,您可以先忽略它,关注 x*x 即可。

这个函数取一个元素并与自身相乘。由于是匿名函数,您需要把列表元素传给它,确保该 lambda 函数把这个元素作为输入。这就是为什么您看到 (lambda x: x*x)(x)。

换句话说,您可以把上面代码块的第二行改写为:

f = lambda x: x*x
[f(x) for x in range(10)]

其中,您首先定义了一个将元素与自身相乘的 lambda 函数,然后在第二行将列表元素传给它,确保每个列表元素都与自身相乘。

注意,除了列表推导式,还有集合与字典推导式。

这已经相当丰富了,但关于列表推导式还有更多值得探索的内容。

继续阅读以下常见问题,了解嵌套列表推导式以及更多实用示例,看看它在 Python 编程中的意义!

10. 如何统计列表项出现的次数

由于列表不像集合那样要求元素唯一,您可能不仅想知道某个特定列表项在列表中出现了多少次,还想知道每个列表项各自出现了多少次。

下面您会找到这两个问题的答案!

统计某个元素在列表中的出现次数

要统计单个列表项的出现次数,可以使用 count() 方法:

# Count the occurrences of the number 4
print([1, 2, 9, 4, 5, 4, 1].count(4))

# Count the occurrences of the letter "a"
list = ["d", "a", "t", "a", "c", "a", "m", "p"]
list.count("a")

统计所有列表项的出现次数也称为“计数”列表,或创建一个计数器。

结合 count() 统计所有列表项

要统计列表中各元素的出现次数,也可以将列表推导式与 count() 结合使用:

list= ["a","b","b"]
[[x,list.count(x)] for x in set(list)]

在这段代码中,首先将列表转换为集合。这会确保只保留唯一的列表项,即 a 和 b。然后,您对集合中的每个元素,传入 count() 方法。

请记住,集合只包含唯一元素!

在这个例子中,首先会将 a 传给 list.count(x),并统计原列表中所有 a 的出现次数。

使用 Counter() 统计所有列表项

另外,也可以使用更快的 collections 库中的 Counter() 方法:

# Import `Counter` from the `collections` library
from collections import Counter

# This is your list
list = ["a","b","b"]

# Pass `list` to `Counter()`
Counter(list)

注意,当要统计所有列表项时,Counter() 通常更快。

11. 如何将列表均匀切分为若干块

要把列表按相同大小切分为若干部分,可以将 zip() 与 iter() 结合使用:

# Your list `x`
x = [1,2,3,4,5,6,7,8,9]

# Split `x` up in chunks of 3
y = zip(*[iter(x)]*3)

# Use `list()` to print the result of `zip()`
list(y)

上述代码的工作方式如下:

  • iter() 是对一个序列的迭代器。
  • [iter(x)] * 3 会生成一个包含三个 listiterator 对象的列表:每个迭代器都迭代 x。
  • 传给 zip() 函数前面的 * 会把序列解包为参数,于是您向 zip() 三次传入了相同的迭代器,zip() 每次从迭代器中取一个元素。

等等。最后一步可能还是不清楚。

我一步步来说明:

  1. 您会有三个列表迭代器对象,可以把它们想象为:

[1,2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9]


  1. 第一次,zip() 会按顺序各取一个元素,于是得到:

[1][2][3]


注意,迭代器对象会为您记录下一个要迭代的元素!

  1. 第二次,会把元素添加到刚创建的三个列表中,得到:

[1, 4], [2, 5], [3, 6]


  1. 最后一次,重复相同步骤,得到:

[1, 4, 7], [2, 5, 8], [3, 6, 9]


  1. 将这三个列表再拉链在一起,就得到:

[1, 2, 3], [4, 5, 6], [7, 8, 9]

如果您决定把这套逻辑封装成函数,可以按以下方式实现:

# Method to split up your lists into chunks
def chunks(list, chunkSize):
    """Yield successive chunkSize-sized chunks from list."""
    for i n range(0, len(list), chunkSize):
        yield list[i:i + chunkSize]

# Use your `chunks` function to print out chunks of the same size
import pprint
pprint.print(list(chunks(range(10, 75), 10)))

chunks() 函数接受列表和块大小作为输入。它会打印:“从列表中连续产出(您的块大小)大小的块”。

然后,range(0, len(list), chunkSize):该序列从 0 开始,到列表长度结束,步长设置为传入 chunks() 的 chunkSize。

对 range() 生成的每个元素,您会从原列表中产出一个切片,从当前索引开始,到索引 + 块大小结束。

例如,如果索引 0 通过 for 循环,就会得到列表的 [0:2];在下一次迭代中,索引 2 通过 for 循环(注意步长!),会输出原列表的 [2:4] 这一块。

注意,这里使用了 pprint()(pretty print)来打印结果。

您也可以用列表推导式来代替编写函数:

12. 如何遍历列表

您可以很容易地用 for 循环遍历任何列表。对于不太熟悉 for 循环的读者,它的工作方式是:for 循环允许您对列表中的每个元素执行某个操作。

在 Python 中,for 循环看起来像这样:

# This is your list
mylist = [[1,2,3],[4,5,6,7],[8,9,10]]

# Loop over your list and print all elements that are of size 3
for x in mylist:
      if len(x)==3:
        print(x)

您有一个列表 mylist,并告诉 Python 应检查每个列表元素的长度是否等于 3。由于我们的列表中包含两个长度为 3 的子列表,即 [1,2,3] 和 [8, 9,10],因此只有它们会被打印出来。

注意,上面的代码中 x 也可以改为 element、listElement 或任何您喜欢的名字。到控制台里试试!

您会注意到,这里的结果并未显示元素的索引。如果您希望通过索引来遍历列表,可以这样做:

# This is your list
myList = [3,4,5,6]

# Loop over `myList` and print tuples of all indices and values 
for i, val in enumerate(myList):
     print(i, val)

因为您想打印出列表元素及其索引,当然需要在 for 循环中体现。这就是我们使用 enumerate(myList) 的原因,它会返回索引和值。自然地,我们也要在 for 语句开头显示这一点:您会看到 i, val,以表明我们期望得到索引和值。

注意,您也可以用列表推导式来遍历列表。在这里,结果会直接打印出来,无需再调用 print():

[x for x in myList if len(x)==3]

13. 如何将“列表的列表”拍平成一维列表

要把一个列表的列表拍平成简单列表,您可以使用 sum() 函数。别忘了传入列表以及一个空列表作为起始值:

# Your initial list of lists
list = [[1,2],[3,4],[5,6]]

# Flatten out your original list of lists with `sum()`
sum(list, [])

您也可以用另外两种方法来拍平列表:

# You can reduce the lists of lists of lists like this
from functools import reduce
print(reduce(lambda x,y: x+y,listOfLists))

# Or you can use list comprehension
print([item for sublist in listOfLists for item in sublist])
    • 可以使用 reduce() 方法,并传入一个 lambda 函数。reduce() 的作用是把可迭代对象“归约”为一个值。以何种方式归约,则由您传入的函数来决定。在这个例子中,您希望累加求和:也就是把 [1,2] 加到 [3,4] 上,然后再把结果加到 [5,6] 上。可以用下式表示:
([1,2]+[3,4])+[5,6]

请记住,+ 运算符会拼接列表。

  • 第二种方法是使用列表推导式。这种嵌套的 for 循环写法不算直观。本质上,它先取 list 中的每个 sublist,也就是 [1, 2]、[3, 4]、[5, 6],然后再对每个 sublist 里的 item 进行遍历并逐一输出。

另一种理解嵌套列表推导式的方式,是把它改写为常规的 for 循环:

list1 = []

for sublist in list:
  for item in sublist:
    list1.append(item)

14. 如何求两个列表的交集

使用列表推导式求交集

如果想获得两个列表的交集,可以使用 filter() 函数。

下方练习进行了演示。我们已为您定义了两个列表 list1 和 list2。如需查看内容,请使用 IPython shell。

# Intersect both lists with list comprehension
intersection = [list(filter(lambda x: x in list1, sublist)) for sublist in list2]

# Print the result of the intersection
print(____)

在上面的代码中,filter() 会取每个子列表的元素并检查是否存在于源列表 list1 中。列表推导式则对 list2 中的每个子列表执行这一过程。

注意,由于 filter() 返回的是可迭代对象而不是列表,因此需要用 list() 包裹传给 filter() 的内容。试着去掉 list() 自己感受一下!

如果想提高可读性,您也可以使用嵌套列表推导式:

# An intersection of both lists, stored in `intersection`
intersection = [[x for x in sublist if x in list1] for sublist in list2]

# Print the result of the intersection
print(intersection)

使用 set() 求交集

如果不关心元素的顺序,也不需要考虑重复元素,那么可以使用集合的交集:

# Make use of the list and set data structures
print(list(set(list1) & set(list2)))

# Use `intersection()`
print(list(set(list1).intersection(list2)))

在第一个例子中,您将两个列表转换为集合,求交后再转换回列表。输出列表中的顺序将是任意的。您也可以将两个列表中更大的那个转换为集合,然后使用 intersection() 与任何可迭代对象求交集。

注意,要使这两种方法有效,列表中不应包含其他列表。这也是 list2 看起来不同的原因。打印出来确认一下!

15. 如何从列表中移除重复元素

如本博文第一节所述,如果您要存储唯一且无序的元素集合,最好使用集合。

要从任何可迭代对象创建集合,只需将其传给内置的 set() 函数。如果之后需要重新得到列表,可以同样将集合传给 list() 函数。

请记住,可迭代对象可以是列表、字典等……您可以通过调用 .__iter__ 方法来检查一个变量是否为可迭代对象。

下面的示例清晰地演示了如何使用 set() 删除列表中的所有重复元素:

# Your list with duplicate values
duplicates = [1, 2, 3, 1, 2, 5, 6, 7, 8]

# Print the unique `duplicates` list
print(list(set(duplicates)))

# A list with small numbers 
smallNumbers = [1, 2, 3]

# Print the unique `duplicates` list without the small numbers
list(set(duplicates) - set(smallNumbers))

如您所见,在最后一个结果中,当您从 smallNumbers 的集合元素中做差后,原列表的顺序不再保留。正如您现在所知道的,集合本身是无序的,因此列表中原有的顺序会丢失。当您把集合转换回列表时,会得到一个任意的顺序。

如果元素顺序对您很重要,那么您需要使用其他机制。此页面对此有更详细的讨论。

16. 为什么选择 NumPy 而不是 Python 列表?

总体而言,Python 程序员更喜欢使用 NumPy 数组而不是 Python 列表有四个原因:

  • NumPy 数组比列表更紧凑。
  • 读取与写入元素时,NumPy 访问更快。
  • NumPy 更便于使用,因为很多向量与矩阵运算都“开箱即用”。
  • NumPy 的实现更高效,因此使用起来更高效。

17. 如何创建空的 NumPy 数组

如果您只想创建一个空的 NumPy 数组,可以执行以下代码:

import numpy
numpy.array([])

不过,如果您的目标是初始化一个 NumPy 数组,也可以使用以下方法之一:

# Make a NumPy array of four rows and two columns and filled with 0
numpy.zeros(shape=(4,2))

# Make a NumPy array of 1 values of three columns
numpy.ones(3)

# Make an empty NumPy array
numpy.empty(shape=(0,0))

如果您在寻找其他初始化 NumPy 数组的方法,请查看这份文档。

18. 如何用列表做数学运算

很多时候,我们不仅用列表来存储一组值,还会对其执行一些数学运算。本节将涵盖关于在列表上进行数学运算的常见问题。

如何计算加权平均数

加权平均与普通平均类似,但略有不同:加权平均取决于数值与权重这两个变量。

这个定义起初可能有点抽象。让我们看一个例子:

cost = [0.424, 0.4221, 0.4185, 0.4132, 0.413]
cases = [10, 20, 30, 40, 50]

您可以用下面的代码轻松计算加权平均:

for c in range(len(cost)):
  cost[c] = (cost[c] * cases[c] / sum(cases))
cost = sum(cost)
print(cost)

当然,您也可以选择以下两种方式之一。首先是:

sum(cost[c] * cases[c] / sum(cases) for c in range(len(cost)))

或者使用这个:

sum(cost[c] * cases[c] for c in range(len(cost))) / sum(cases)

最后,您还可以借助 zip() 函数来计算加权平均。

提示:创建一个变量 costAndCases,将 zip() 函数赋给它,并把 cost 和 case 变量传入。您会看到创建了一个只包含元组的新列表。每个元组的第一个值表示 cost,第二个值表示对应的 cases。

# See what `zip()`d does to your `cost` and `cases`
print(list(zip(____, ____)))

# Calculate the weighted average
print(sum([x * y for x, y in zip(cost, cases)]) / sum(cases))

请记住,zip() 对列表做了什么?很简单,就是把两个列表“拉链”在一起。例如,zip(cost, cases) 会输出 [(0.424, 10), (0.4221, 20), (0.4185, 30), (0.4132, 40), (0.413, 50)]。

如何计算分位数

在对数据集做摘要时,除了最小值与最大值外,分位数也至关重要。常用的有第 25、50、75 百分位数,也称为第一四分位数、中位数和第三四分位数。

这意味着,概括整个数据集需要五个数字:最小值、最大值、中位数和两个四分位数。

数据集的最小值和最大值很容易找到,但那些百分位数怎么求呢?

很简单。假设数据集中从小到大排序后有 25 个样本:

  • 第 25 百分位(第一四分位)通过 0.25 × 25 计算得到。结果为 6.25,向上取整为 7,这表示第 7 个数即为第 25 百分位数。
  • 第 75 百分位(第三四分位)通过 0.75 × 25 计算得到。结果为 18.75,向上取整为 19,表示第 19 个数是第 75 百分位数。
  • 中位数通过 0.5 × 25 计算得到。因为 12.5 向上取整为 13,故第 13 个数是中位数。

但如何将其编程实现呢?

最简单的方法是使用 NumPy。按照下面交互练习中的说明来计算列表的百分位数:

# Import numpy as np
import ____ as __

# Make a NumPy array
a = np.array([1,2,3,4,5])

# Return the 50th percentile of our NumPy array
p = np.percentile(a, 50)

#Print the result
print(____)

如何对两个列表做逐元素求和

假设您有两个列表:

list1=[1, 2, 3]
list2=[4, 5, 6]

您希望按元素位置一一相加:也就是 1+4、2+5、3+6。您有两种实现方法。

继续阅读了解更多!

……使用原生 Python

可以结合 operator.add 使用 map 来执行逐元素相加:

from operator import add
list(map(add, list1, list2))

结果将是列表 [5, 7, 9]。

注意,需要使用 list() 才能打印出 map() 的结果。

或者,您可以将 zip() 与列表推导式结合使用:

[sum(x) for x in zip(list1, list2)]

……使用 NumPy

前面的示例对这些小列表很合适,但当您处理的数据稍多一些时,最好使用 numpy 让过程更简单。

此时应确保先导入 numpy 库,并将列表转换为 NumPy 数组:

# Import numpy as np
import ____ as __

# Make your lists into NumPy arrays
vector1 = np.array([1, 2, 3])
vector2 = np.array([4, 5, 6])

# Element-wise addition
sum_vector = vector1 + vector2 

# Print the result
print(____)

您在 Python 数据科学之旅的下一步?

您已经完成了我们关于 Python 列表的交互式教程。内容不少、工作量也不小,但您做到了!

列表只是您 Python 数据科学之旅中一个小但必不可少的组成部分。还有太多值得探索!我们的交互式 数据科学 Python 入门 课程非常适合想进一步了解 Python 及其在数据科学中用法的读者:课程从 Python 的最基础开始,循序渐进地向您介绍 NumPy,将您温和地带到中级水平。非常适合在(Python)编程方面经验不多的读者!

如果您已经是更进阶的 Python 用户,可以考虑查看我们的 中级 Python:面向数据科学 课程,专为准备将数据科学与 Python 技能更上一层楼的学习者设计。本课程会进一步提升您的数据科学学习!课程将带您了解更多库(如 matplotlib 和 pandas)、概念(如逻辑、控制流与筛选),并包含一个数据科学案例研究,帮助您巩固技能。

您已经在用 Python 做数据科学,但还想走得更远?不妨看看我们的 Python 数据导入 课程,或阅读我们的 数据科学与机器学习流程中预处理的重要性 系列教程!

主题