本章内容提要:

  • 列表及操作
  • 元组及操作
  • 字典及操作
  • 集合简介

第2章介绍了数字(整数、浮点数)、逻辑值和字符串等Python内置的基本数据类型。在实际的操作中,仅仅依赖它们很难高效地完成复杂的数据处理任务。基于对基本数据类型的构建,Python拓展出列表、元组、字典与集合等更为实用的数据结构,以简化程序的编写与任务的实现。这些数据结构内置于Python,是数据分析经常要操作的对象。

3.1 列表

列表(list)是Python中最常用的内置类型之一,是处理一组有序项目的数据结构,或者说,是一个有序对象的集合。通俗地理解,列表即序列,它是一系列数值的序列。在前文介绍的字符串中,字符串包含的值是一个个字符。而在列表中,值可以是任意类型。列表的值一般也称为列表的元素,通过英文逗号分隔,并包含在方括号内。

3.1.1 列表的创建

下面创建一个简单的列表,存储英文中的5个元音字母:

In [1]: vowels = ['a', 'e', 'i', 'o', 'u']
In [2]: vowels
Out[2]: ['a', 'e', 'i', 'o', 'u']

我们可以不添加任何元素来初始化一个列表:

In [3]: array_init = []
In [4]: array_init
Out[4]: []

如果要提取列表中的元素,使用索引是一种方法,将索引值写在变量名后的方括号内,如提取列表vowels中的i:

In [5]: vowels[2]
Out[5]: 'i'

方括号内填入的是2,而不是3。这与Python的索引机制有关—— Python的索引是从0开始的(当然也有从1开始索引的语言,比如数据分析中也非常流行的R语言)。

因此,列表vowels元素与其索引之间有以下对应关系:

a e i o u
0 1 2 3 4

列表的元素可以是任意类型,因此列表可以嵌套列表。例如,用以下列表来依次表示两个长方形的名称、面积与相应的长和宽:

In [7]: rectangle = ['长方形1', 20, [4, 5], '长方形2', 16, [4, 4]]
In [8]: rectangle
Out[8]: ['长方形1', 20, [4, 5], '长方形2', 16, [4, 4]]

如果列表太长,不方便直接观察列表的长度,那么可以利用len()函数进行计算。

In [9]: len(rectangle)
Out[9]: 6

结果显示rectangle长度为6,我们可以使用索引值0~5提取rectangle的元素。再次注意,Python索引值是从0开始的,如果使用的索引值超出界限,Python会报错,提示我们使用的列表索引超出范围。

In [10]: rectangle[6]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-10-6e16763c0048> in <module>()
----> 1 rectangle[6]

IndexError: list index out of range

除了从头创建一个列表,也可以使用list()函数将其他数据类型转换为列表,如下面的字符串:

In [17]: aseq = "atggctaggc"
In [18]: list(aseq)
Out[18]: ['a', 't', 'g', 'g', 'c', 't', 'a', 'g', 'g', 'c']

3.1.2 修改列表元素

和字符串不同,列表是可以修改的,只需对指定的列表元素重新赋值即可。

例如,用一个列表存储10以内的奇数:

In [11]: odd_numbers = [1, 3, 5, 7, 8]

即使发现最后一个元素写错了,也不需要像下面这样重新创建列表。

In [12]: odd_numbers = [1, 3, 5, 7, 9]

我们不需要重新输入创建一个新的列表来纠正之前错误的输入,只需要修改写错的元素,即利用索引对错误的元素重新赋值。

In [13]: odd_numbers = [1, 3, 5, 7, 8]
In [14]: odd_numbers[4] = 9
In [15]: odd_numbers
Out[15]: [1, 3, 5, 7, 9]

除了使用自然数进行索引元素,还可以使用负整数进行反向索引,比如odd_numbers[-1]也对应着9:

In [16]: odd_numbers[-1]
Out[16]: 9

我们依旧可以用之前的列表vowels来表示列表元素与反向索引之间的对应关系,如下:

a   e   i   o   u
-5 -4 -3 -2 -1

3.1.3 遍历列表

想象一下,如果列表元素非常多,而我们想要对列表中的每一个元素进行操作或变换,难道要一个一个利用索引取出,然后修改吗?逐一访问列表的元素称为遍历列表。这里需要初步借助第4章介绍的循环来解决类似的问题。

循环的作用在于将单一枯燥的重复性工作交给机器去实现,而用户只需要关注去掉循环的操作本身。

最常用的循环结构是for循环。如果需要逐一打印10以内的奇数,我们不需要逐步使用print()函数打印列表的每一个元素。

print(odd_numbers[0])
print(odd_numbers[1])
print(odd_numbers[2])
print(odd_numbers[3])
print(odd_numbers[4])

只需要两行代码就可以实现列表的遍历,如下所示:

In [24]: for i in odd_numbers:
print(i)

1
3
5
7
9

这里列表odd_numbers中元素的值会逐个传给i,然后通过print()函数将i的值输出打印。使用循环除了使代码更清晰简洁外,另一个好处是用户不需要知道列表有多长!既然for循环可以遍历列表中所有的元素,那么如果元素是一个列表,它会对这个列表接着遍历吗?

假设创建一个列表存储小明、小红、小蓝3个人跳远的成绩记录,如下:

In [26]: nested_list = ['记录', 3, ['小明', '小红', '小蓝'], [2.30, 2.41, 2.33]]

使用for循环是将该列表中的所有元素一个一个输出,还是会输出其他的结果呢?

In [27]: for i in nested_list:
...: print(i)
...:
记录
3
['小明', '小红', '小蓝']
[2.3, 2.41, 2.33]

结果显示,for循环并没有将列表的所有元素单个传入变量i,而是将列表最外面一层的元素传入了变量i。打个比方,简单的列表像一层洋葱,而嵌套了列表的列表相当于多层洋葱,for循环只负责剥开一层。

因此,如果想剥开例子中的“两层洋葱”—— nested_list,我们需要使用两次for循环。for循环的操作和使用在第4章会详细介绍。

3.1.4 列表操作符

列表操作符用于便利地操作列表,使用它们如同使用数值的加、减、乘、除一样简单。

1.加号

加号 + 不仅能用于数字相加、字符连接,还能用于列表的拼接。

In [28]: a = [1, 2, 3]
In [29]: b = [4, 5, 6]
In [30]: a + b
Out[30]: [1, 2, 3, 4, 5, 6]

a + b的结果是将列表b中的元素拼接到列表a的后面,生成了一个新的列表。

如果两个列表是不同的数据类型,还能拼接吗?

In [31]: b = [4, 5, '6']
In [32]: a + b
Out[32]: [1, 2, 3, 4, 5, '6']

代码运行结果说明是可以的,列表包容万物,而含不同数据类型的列表拼接只是将它们放到了一起,并没有其他特殊的操作。

2.星号

星号 * 操作符可以将列表重复指定的次数,如下所示:

In [33]: a * 5
Out[33]: [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

3.1.5 列表切片

除了上一节提到的 + 操作符与 * 操作符,冒号 : 操作符可以对列表执行切片操作。切片操作是利用冒号操作符进行取子集的过程。因为该操作符经常使用,所以单列一节进行介绍。

例如,如果存在包含7个字母的列表如下:

In [34]: letters7 = ['a', 'b', 'c', 'd', 'e', 'f', 'g']

如果只想要a、b、c、d这4个字母,那么切片操作如下:

In [37]: you_want = letters7[0:4]
In [38]: you_want
Out[38]: ['a', 'b', 'c', 'd']

列表索引规则是start:stop:step,其中,stop值不被包含在内(即区间前闭后开)。上面代码中,start对应0(再次提醒Python索引从0开始),stop对应4,而step默认为1,可以省略。

理解了切片的规则,我们就可以知道下面的操作会得到一样的结果。

In [39]: letters7[0:4:1]
Out[39]: ['a', 'b', 'c', 'd']

索引的起始位置也可以省略,默认从0开始。

In [40]: letters7[:4]
Out[40]: ['a', 'b', 'c', 'd']

索引的终止位置也可以省略,默认为列表长度,也就是到最后一个元素。

In [41]: letters7[:7]
Out[41]: ['a', 'b', 'c', 'd', 'e', 'f', 'g']
In [42]: letters7[4:]
Out[42]: ['e', 'f', 'g']

注意,加 : 操作符与不加是不同的。加 : 操作符,结果返回的是一个列表,而不加返回的是元素本身。

In [43]: letters7[-1]
Out[43]: 'g'
In [44]: letters7[-1:]
Out[44]: ['g']

在理解了上面操作的基础上,理解下面的操作结果也顺理成章。

In [45]: letters7[::1]
Out[45]: ['a', 'b', 'c', 'd', 'e', 'f', 'g']
In [46]: letters7[::2]
Out[46]: ['a', 'c', 'e', 'g']

步长还可以取负整数,代表逆序切片。

In [47]: letters7[::-1]
Out[47]: ['g', 'f', 'e', 'd', 'c', 'b', 'a']
In [48]: letters7[::-2]
Out[48]: ['g', 'e', 'c', 'a']

另外,切片运算符放到赋值语句等号左边时可以对多个元素进行更新。

In [49]: letters7[0:2] = ['h', 'i']
In [50]: letters7
Out[50]: ['h', 'i', 'c', 'd', 'e', 'f', 'g']

注意,左右两边可以不等长。

In [51]: letters7[0:2] = ['a']
In [52]: letters7
Out[52]: ['a', 'c', 'd', 'e', 'f', 'g']
In [53]: letters7[0:1] = ['a', 'b']
In [54]: letters7
Out[54]: ['a', 'b', 'c', 'd', 'e', 'f', 'g']

如果是单个元素,等号右侧也可以不加方括号。

In [55]: letters7[0:2] = 'h'
In [56]: letters7
Out[56]: ['h', 'c', 'd', 'e', 'f', 'g']

3.1.6 列表方法、函数与操作

Python为列表提供了很多方法,用来简化列表的各项常用操作。常用操作包括添加元素、删除元素、插入元素等。

注意,当下文提及方法时,一般指在变量名后加点号然后加函数。例如,list.append()指对列表list使用append()方法。

1.添加元素

Python中有3种方法可以为列表添加元素,分别是append()、insert()和extend()。

(1)append(element):将元素element添加到列表的末尾。

In [59]: example_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [60]: example_list.append(11)
In [61]: example_list
Out[61]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

(2)insert(position, element):将元素element插入列表指定position位置。

In [62]: example_list.insert(2, 12)
In [63]: example_list
Out[63]: [1, 2, 12, 3, 4, 5, 6, 7, 8, 9, 10, 11]

(3)extend(list):使用另一个列表作参数,然后把所有的元素添加到一个列表上。

In [64]: example_list.extend([13,14])
In [65]: example_list
Out[65]: [1, 2, 12, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14]

2.删除元素

同样地,Python也有3种方法删除列表中的元素。

(1)pop([index]):移除索引位置index的元素并返回它的值,如果没有参数,则默认删除和返回最后一个。

In [67]: example_list.pop()
Out[67]: 14
In [68]: example_list.pop(2)
Out[68]: 12

(2)remove(element):移除参数中指定的元素element,如果存在多个同样的值,则移除最左边的。不同于pop(),这个方法不返回任何值。

In [69]: example_list.remove(13)
In [70]: example_list
Out[70]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

(3)另一种方式是使用del命令,del list[0]类似于list.pop(0),但前者不会返回被删除的元素。

In [71]: del example_list[10]
In [72]: example_list
Out[72]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

除了上面提到的3种方法,另有clear()方法可以清洗列表,它会清空列表中的所有元素。

In [73]: example_list.clear()
In [74]: example_list
Out[74]: []

3.列表排序

当把数值数据存储在列表后,一个常见的需求是对列表中的值进行排序,利用sort()方法可以实现。

In [78]: a = [3, 1, 2, 5, 4, 6]
In [79]: a.sort()
In [80]: a
Out[80]: [1, 2, 3, 4, 5, 6]

可以看到,使用sort()方法后,列表本身被改变了。如果不想改变原始列表,可以使用sorted()函数,并把结果赋值给新的变量。

In [81]: nums = [-1, 34, 0.2, -4, 309]
In [82]: nums_desc = sorted(nums, reverse=True)
In [83]: nums
Out[83]: [-1, 34, 0.2, -4, 309]
In [84]: nums_desc
Out[84]: [309, 34, 0.2, -1, -4]

reverse()方法可以将列表按位置翻转。

In [90]: nums
Out[90]: [1, 2, 2, 2, 3, 3, 4, 5]
In [91]: nums.reverse()
In [92]: nums
Out[92]: [5, 4, 3, 3, 2, 2, 2, 1]

4.简单统计

min()与max()函数可以计算列表最小值与最大值。

In [85]: min(nums)
Out[85]: -4
In [86]: max(nums)
Out[86]: 309

如果要对出现相同元素计数,可以使用count()方法。

In [87]: nums = [1, 2, 2, 2, 3, 3, 4, 5]
In [88]: nums.count(2)
Out[88]: 3
In [89]: nums.count(3)
Out[89]: 2

sum()函数可以对数值列表求和。

In [93]: sum(nums)
Out[93]: 22

5.逻辑判断

如果需要查看列表中是否存在某个元素,可以使用关键字in,结果返回的是逻辑值。

In [76]: 4 in example_list
Out[76]: False
In [77]: 3 in example_list
Out[77]: True

all()与any()函数用于逻辑值列表,all()判别列表所有值是否都为真,全真时返回True,否则返回False;any()用于判别只要任一元素为真,则返回True,否则返回False。

In [94]: conditions = [True, False, True]
In [95]: all(conditions)
Out[95]: False
In [96]: any(conditions)
Out[96]: True

如果要比较两个列表是否一致,则可以直接使用两个等号进行逻辑判断。

In [97]: a = [1, 2, 3, 4]

In [98]: a == [1, 2, 3, 5]
Out[98]: False
In [99]: a == [1, 3, 2, 4]
Out[99]: False
In [100]: a == [1, 2, 3, 4]
Out[100]: True

6.最常见操作方法汇总

上面介绍了大量的列表操作、函数与方法,但实际上可用的远不止这些。对Python有深入了解后,我们可以自己创建操作列表的方法和函数。

表3-1中列出了常见的操作方法。

表3-1 常见的列表操作方法汇总


表示



描述



l.append(x)



添加元素x到列表



l.count(x)



计算列表中x出现的次数



l.index(x)



返回元素x的位置(索引)



l.remove(x)



从列表中移除x



l.reverse()



翻转列表



l.sort()



列表排序


3.1.7 列表与字符串

字符串是一系列字符的序列,而列表是一系列值的序列,但一个由字符组成的列表是不同于字符串的。要把一个字符串转换成字符列表,可以用list()函数。

下例是将字符串转换为字符列表:

In [101]: s = 'interactive Python'
In [102]: t = list(s)
In [103]: t
Out[103]:
['i',
'n',
't',
'e',
'r',
'a',
'c',
't',
'i',
'v',
'e',
' ',
'P',
'y',
't',
'h',
'o',
'n']

在上面的代码中,list()函数将一个字符串分开成一个个字符(字母)。如果要把字符串切分成一个个单词,可以使用split()方法,如下所示:

In [104]: s.split()
Out[104]: ['interactive', 'Python']

注意,方法中有一个可选的参数是定界符,用于确定单词边界。

下例用短横线作为定界符来拆分两个单词:

In [105]: s = 'interactive-Python'
In [107]: s.split('-')
Out[107]: ['interactive', 'Python']

另一个方法join()的功能与split()方法的功能相反,它接收一个字符串列表,然后把所有元素拼接到一起作为字符串。join()是一个字符串方法,所以必须把join()放到定界符后面来调用,并且传递一个列表作为参数。

In [108]: t = ['我','是', '谁', '?']
In [109]: ''.join(t)
Out[109]: '我是谁?'

注意,上面代码中的定界符是一个空格字符。

3.1.8 列表对象与值

请思考:下面对象a与b是同一个对象吗?

In [4]: a = 'banana'
In [5]: b = 'banana'

如果把对象看作篮子,内容banana看作篮子里的鸡蛋。现在需要判断的是,变量名a和b是同一个篮子的两个便签(鸡蛋只有一个),还是两个不同篮子(每一个篮子都有一个鸡蛋)的便签?

使用is操作符,可以得到答案。

In [6]: a is b
Out[6]: True

从上面的代码运行结果来看,答案是第一种情况:Python只创建了一个字符串对象,内容为banana,a和b都是这个对象的便签。

另外,可以使用id()函数提取对象的唯一标识符。这就像居民身份证一样,虽然同一个人可能会有不同的称呼,但身份证号码只有一个。

In [10]: id(a)
Out[10]: 1691582590008
In [11]: id(b)
Out[11]: 1691582590008

从结果中可见,a和b确实是完全相同的。那么,如果改变a,那么b也会随之改变吗?

In [12]: a = "orange"
In [13]: b
Out[13]: 'banana'

结果是不会。实际上,Python对象是它指向的内容,变量名a和b本身只是一个方便使用的标签,所以当我们将另一个字符串赋值给变量a时,Python实际上是先创建了一个字符串对象,内容是orange,然后给这个对象打上标签a。

如果创建两个列表,尽管它们的内容相同,但它们也是不同的对象,下面的代码运行结果可以验证这一点。

In [14]: a = [1, 2, 3]
In [15]: b = [1, 2, 3]

In [16]: a is b
Out[16]: False

In [17]: id(a)
Out[17]: 1691581888264
In [18]: id(b)
Out[18]: 1691582794120

在这个情况下,可以说两个列表是相等的,因为它们有相同的元素,但不是同一个列表,因为它们并不是同一个对象。如果两个对象是同一个对象,那么它们必然是相等的;但如果它们相等,却未必是同一个对象。

注意,如果这里的b不是重新创建,而是将a赋值给b,那么a和b就是完全相同的,因为它们指向同一个列表对象。

In [19]: b = a

In [20]: a is b
Out[20]: True

In [21]: id(b)
Out[21]: 1691581888264

因此,我们尽量不要对Python的列表进行e=f=e=c=a这样的赋值操作,一旦修改了某一个元素,其他变量全部会跟着改变!

In [22]: e = a

In [23]: e
Out[23]: [1, 2, 3]
In [24]: a
Out[24]: [1, 2, 3]

In [25]: a[1] = 4
In [26]: e
Out[26]: [1, 4, 3]

到这里,本章一一介绍了Python列表的基础知识和相应操作。本章的大部分内容是在讲解列表,列表不仅是Python最核心的概念和数据结构,也是理解其他基础数据结构的桥梁。掌握好了列表,读者对本章接下来介绍的数据结构都可以触类旁通,其使用和操作方法大同小异。重要的差异会给出提示和强调,读者需要留心注意。

3.2 元组

元组(tuple)就是不可更改的列表,一旦创建,便不可更改。除了表示的方式有点不一样、元组的元素不可更改,元组的其他特性与列表基本一致。

3.2.1 元组的创建

In [1]: a_tuple = (1, 2, 3)
In [2]: a_list = [1, 2, 3]

上面代码分别创建了一个元组和列表,可以清晰地看到它们定义的差别所在。其实元组的语法是一系列用逗号分隔的值,也就是说括号是可以省略的。

In [6]: another_tuple = 1,2,3
In [7]: type(another_tuple)
Out[7]: tuple

作为初学者,创建元组时尽量使用括号,这样在书写和查看代码时可以非常清楚地区分什么是列表、什么是元组。Python中常见的数据类型在表示上都有着非常鲜明的特点,这可以帮助读者构建优良的代码。

当创建的元组只有一个元素时,需要特别注意:元组中的元素后需要一个逗号。

请看下面的代码:

In [8]: 1
Out[8]: 1

In [9]: (1)
Out[9]: 1

In [10]: 1,
Out[10]: (1,)

In [11]: (1,)
Out[11]: (1,)

前两个命令创建的都是数字1,后两个命令创建的才是元组,包含元素数字1。

除了使用逗号分隔创建元组,创建元组的另一种方式是使用tuple()函数。如果参数为一个序列(比如字符串、列表或者元组),结果就会得到一个以该序列元素组成的元组。

In [14]: tuple("Python")
Out[14]: ('P', 'y', 't', 'h', 'o', 'n')
In [15]: tuple(["I", "am", ["learning", "Python"]])
Out[15]: ('I', 'am', ['learning', 'Python'])

3.2.2 元组操作

适用于列表的操作符和方法基本也适用于元组。

1.操作符

代码如下:

In [16]: ('a',) + ('b',)
Out[16]: ('a', 'b')

In [17]: ('a',) * 3
Out[17]: ('a', 'a', 'a')

2.切片

代码如下:

In [18]: pythonName = tuple("Python")
In [19]: pythonName
Out[19]: ('P', 'y', 't', 'h', 'o', 'n')

In [20]: pythonName[0]
Out[20]: 'P'
In [21]: pythonName[0:3]
Out[21]: ('P', 'y', 't')
In [22]: pythonName[3:]
Out[22]: ('h', 'o', 'n')

3.修改

元组是不可修改的,所以不能使用append()和pop()等方法对元素进行添加、删除、修改等操作。

In [23]: pythonName[0] = 'p'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-23-19ded1757eee> in <module>()
----> 1 pythonName[0] = 'p'

TypeError: 'tuple' object does not support item assignment

但可以用另一个元组来替换已有的元组。

In [24]: newName = ('p',) + pythonName[1:]
In [25]: newName
Out[25]: ('p', 'y', 't', 'h', 'o', 'n')

4.变量值交换

利用中间变量对变量的值进行交换是一个常见的操作。

例如,要交换变量a和b的值,我们一般会采用如下策略:

# a和b是已经创建的变量,t是一个临时变量
t = a
a = b
b = t

有了元组,我们就可以使用下面一行代码简化这一过程。

a, b = b, a

3.2.3 元组与列表的区别

看到这里,读者可能会产生疑问:元组能做的事情列表好像都能做,列表还没有元组这么多的约束,那么只用列表不是更好吗?

元组相比于列表的优点之一是可以使代码更安全,特别是与数据有关的,元组不能修改的属性看起来是一层灵活性限制,其实也是一层安全性的保障,而且这个属性让元组像一个坐标系统(中学数学也用括号来填入坐标,并用逗号分隔),比如3个元素c(x,y,z),所以它广泛用于参数的传递。关于参数传递,本书在第5章会详述。另外,元组的一个隐形的优点是它比列表占用的内存更少,这在大数据计算时需要考量。

3.3 字典

字典的含义和表示都与其语义相似,就像我们小时候查找汉字,可以通过拼音字母(或笔画)进行检索。我们可以自己定义Python中的字典名字,然后通过这个名字查找到对应的数值。其中的名字叫作“键”,对应的数值简称“值”,所以字典也称“键值对”。需要注意的是,字典没有顺序一说,所有的值仅能用键获取。

简而言之,字典被看作无序的键值对或有名字的元素列表。

3.3.1 字典的创建与使用

下面代码使用字典存储了3个人的体重数据。

In [5]: weight = {'小红':65, '小明':45, '我':75}

字典的内容放在花括号内,键值对以英文冒号连接,不同的键值对以英文逗号隔开。

下面代码用于查看对字典的打印输出:

In [6]: weight
Out[6]: {'小明': 45, '小红': 65, '我': 75}

从结果中可以看到,输出的顺序与键入的顺序是有差别的(也有可能相同)。

有了字典,我们可以用比列表更简单和直观地提取对应内容的数据。例如,可以使用下面的代码获取小明的体重。

In [7]: weight['小明']
Out[7]: 45

既然字典有键与值的区分,那么该如何获取键与值的内容呢?为此Python提供了两个方法,分别是keys()和values()。

In [8]: weight.keys()
Out[8]: dict_keys(['小红', '小明', '我'])
In [9]: weight.values()
Out[9]: dict_values([65, 45, 75])

因为字典需要唯一的键去提取正确的内容(值),所以并不是所有的对象都可以用作键。只有不能改变的元组、数字、字符串等能作为键。

如果要初始化字典,类似于列表使用符号[]、元组使用符号()、字典使用符号{}。

In [10]: int_dict = {}
In [11]: int_dict
Out[11]: {}

除了重新创建字典,还可以把从其他数据类型转换为字典。例如,下面有一个存储了RGB16进制的列表,我们使用dict()函数将其转换为字典。

In [13]: rgb = [('red', 'ff0000'), ('green', '00ff00'), ('blue', '0000ff')]

In [14]: dict(rgb)
Out[14]: {'blue': '0000ff', 'green': '00ff00', 'red': 'ff0000'}

此外,还可以以传递参数给dict()函数的方式创建字典。下面代码创建的字典与上面代码创建的字典完全相同。

In [15]: dict(red='ff0000',green='00ff00', blue='0000ff')
Out[15]: {'blue': '0000ff', 'green': '00ff00', 'red': 'ff0000'}

如果需要不断地往字典中添加键值,那么要先初始化字典,然后使用赋值的方式添加键值对。

In [16]: rgb = {}

In [17]: rgb['red'] = 'ff0000'
In [18]: rgb['green'] = '00ff00'
In [19]: rgb['blue'] = '0000ff'

In [20]: rgb
Out[20]: {'blue': '0000ff', 'green': '00ff00', 'red': 'ff0000'}

3.3.2 字典操作

一些常见的函数和方法都可以用在字典上。

例如,提取字典长度。

In [21]: len(rgb)
Out[21]: 3

使用pop()方法可以从字典中删除某个值,并返回该值。注意,需要指明键。

In [22]: rgb.pop()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-22-1654217e28c5> in <module>()
----> 1 rgb.pop()

TypeError: pop expected at least 1 arguments, got 0

In [23]: rgb.pop('blue')
Out[23]: '0000ff'
In [24]: rgb
Out[24]: {'green': '00ff00', 'red': 'ff0000'}

使用del关键字可以删除字典。

In [25]: del rgb
In [26]: rgb
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-26-d412e57c3c38> in <module>()
----> 1 rgb

NameError: name 'rgb' is not defined

使用get()方法可以无意外地获取字典值,它需要提供两个参数,除了键,还需要指定如果查找不到应当返回的信息。

In [28]: rgb.get('red', '键不存在')
Out[28]: 'ff0000'
In [29]: rgb.get('yellow', '键不存在')
Out[29]: '键不存在'

如果不改变字典的顺序,可以使用collections模块的OrderedDict()函数。下面的代码将之前创建的字典rgb转换为了有序字典,还给出了一个新的创建示例,可以发现列表输出的顺序确实没有改变。

In [32]: from collections import OrderedDict

In [33]: OrderedDict(rgb)
Out[33]: OrderedDict([('red', 'ff0000'), ('green', '00ff00'), ('blue', '0000ff')])

In [35]: order_dict = OrderedDict()
In [36]: order_dict['a'] = 1
In [37]: order_dict['b'] = 2
In [38]: order_dict['c'] = 3
In [39]: order_dict
Out[39]: OrderedDict([('a', 1), ('b', 2), ('c', 3)])

3.4 集合

集合是无序的对象集,它和字典一样使用花括号,但没有键值对的概念。集合属于可变的数据类型,一般用于保持序列的唯一性—— 也就是同样的元素仅出现一次。

3.4.1 集合的创建

在使用集合时一定要注意集合的“无序”和“唯一”两个特点,避免出错。

在下面代码中,当集合出现不唯一的字符时,创建的集合中只会保存一个。

In [40]: a_set = {1, 2, 3, 4, 5, 5, 4}
In [41]: a_set
Out[41]: {1, 2, 3, 4, 5}

既然集合与字典都使用花括号,那么如果要初始化一个空集合,该怎么办?还能用花括号吗?

In [42]: a_set = {}
In [43]: a_set.add(1)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-43-2a4eeb394ac5> in <module>()
----> 1 a_set.add(1)

AttributeError: 'dict' object has no attribute 'add'

结果显示报错,信息显示字典没有add属性,说明花括号仅能初始化字典。

集合对应的函数是set(),因而我们必须使用它初始化或将其他数据类型转换为字典。

In [44]: a_set = set()
In [45]: a_set.add(1)
In [46]: a_set
Out[46]: {1}

3.4.2 集合操作

集合的常见用处是进行集合操作,这涉及3个基本方面:合集(并集)、交集和差集。

1.合集

合集使用union()方法如下。

In [47]: a_set = set([1, 2, 3, 4, 5])
In [48]: b_set = set([4, 5, 6, 7, 8])
In [49]: a_set
Out[49]: {1, 2, 3, 4, 5}
In [50]: b_set
Out[50]: {4, 5, 6, 7, 8}

In [51]: a_set.union(b_set)
Out[51]: {1, 2, 3, 4, 5, 6, 7, 8}

2.交集

交集使用intersection()方法如下。

In [52]: a_set.intersection(b_set)
Out[52]: {4, 5}

3.差集

差集使用difference()方法如下。

In [53]: a_set.difference(b_set)
Out[53]: {1, 2, 3}

3.4.3 冰冻集

上一节提到,集合是可变的数据类型。在实际的数据分析中,有时希望集合存储的数据不能改变,以防信息被恶意篡改或者出现其他数据失真的情况。

冰冻集(frozenset)提供了集合的不可变版本,它的内容不能改变,因此不存在add()与remove()方法。frozenset()函数可以将输入的迭代对象转换为冰冻集。

In [1]: fs = frozenset(['a', 'b'])
In [2]: fs
Out[2]: frozenset({'a', 'b'})

In [3]: fs.remove('a')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-b55f44b7e2c9> in <module>()
----> 1 fs.remove('a')

AttributeError: 'frozenset' object has no attribute 'remove'

In [4]: fs.add('c')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-34531eab3bc0> in <module>()
----> 1 fs.add('c')

AttributeError: 'frozenset' object has no attribute 'add'

冰冻集由于是不可变对象,所以可以用作字典的键。

3.5 章末小结

本章详细介绍了Python内置的4个重要基本数据结构,分别是列表、元组、字典和集合。

其中,列表是日常工作分析主要接触和使用的数据结构。元组与列表极为相似,但它们存在一个重要的区别—— 元组不可修改!字典实现了键与值的配对,可以快速实现内容的索引。集合相对少用些,它是存储数据唯一值的一个集合。四者使用的初始化符号或函数都是不同的,读者需要能够区分并熟练掌握。本章的核心在列表部分,列表的重要性不言而喻,理解列表也可以帮助读者快速理解其他几个数据结构的意义与操作方法。在接下来的章节中,本书也将更深入地介绍和运用它们。

本文摘自《交互的Python:数据分析入门》

Python内置的4个重要基本数据结构:列表、元组、字典和集合_python

Python具有强大的应用能力,以及便捷高效的数据分析和可视化扩展包系统。本书重点讲解Python数据分析的基础知识,使读者通过Python理解数据分析的逻辑,并掌握基本的Python编程知识和分析实现方法。本书系统全面、循序渐进地介绍了Python编程基础、数据导入、数据分析和可视化内容,包括条件判断与循环控制、从Excel中导入数据、使用Pandas库进行数据的转换和计算,以及使用Plotnine库绘制ggplot风格的图形等。此外,本书还涉及Markdown、基本的统计理论和IPython魔术命令等内容。