Python基础教程2--列表、元组和字典

本文主要介绍Python中的列表、元组和字典相关知识,以及如何通过这些数据结构将单个数据组织在一起。

这三个也是在编码过程中最常用的数据结构。

列表list

列表可以称为Python的"劳模",基本上所有的代码中都会用到,列表是可变的,列表的内容可以被修改。

第一篇文章中,序列部分介绍的内容对于列表全部适用。

创建列表

列表主要有两种构造方法:

  • 最简单也是最常用的方式就是直接用方括号[]定义
                            
                                >>> list1 = []          # 空列表
                                >>> list2 = [1, 2, 3]   # 有1,2,3三个初始值的列表
                            
                        
  • 通过list([iterable])函数生成列表,此方法常用于将filter/map等函数返回的iterable可迭代对象转换为list对象
                            
                                >>> list1 = list()              # []
                                >>> list3 = list([1, 2, 3])     # [1, 2, 3]
                                >>> list2 = list('Hello')       # ['H', 'e', 'l', 'l', 'o']
    
                                >>> list(filter(lambda x: x%2==0, range(0,10))) # filter返回结果是一个filter类对象,通过list()函数返回列表对象使用
                                [0, 2, 4, 6, 8]
                            
                        

列表方法

以下方法很多都可以通过分片赋值来实现,但是代码可读性不如使用方法

  • append(value_item): 在列表末尾追加新对象。
                            
                                >>> n_list = [1, 2]
                                >>> n_list.append(3)          # 直接修改原列表,返回值为空,对应的分片赋值实现为:n_list[len(n_list):] = [3]
                                >>> n_list
                                [1, 2, 3]
                            
                        
  • extend(list_item): 在列表末尾一次性追加另外一个序列的多个值。
                            
                                >>> n_list = [1, 2]
                                >>> n_list.extend([3, 4])     # 直接扩展原列表,返回值为空,对应的分片赋值实现为:n_list[len(n_list):] = [3, 4]
                                >>> n_list
                                [1, 2, 3, 4]
                            
                        

    extend和连接操作的主要区别是: extend操作扩展原列表,而连接操作会返回一个全新的列表。

                            
                                >>> list1 = [1, 2]
                                >>> list2 = [3, 4]
                                >>> list1 + list2
                                [1, 2, 3, 4]            # 生成新的里表
                                >>> list1               # 原列表不变
                                [1, 2]
                            
                        
  • count(value_item): 统计某个元素在列表中的出现次数,如果是多维列表,只统计出现在第一维的次数。
                            
                                >>> list1 = [1, 2, 3, 3, 6]
                                >>> list1.count(1)                          # 1在list1中出现了1次
                                1
                                >>> list1.count(3)                          # 1在list1中出现了2次
                                2
                                >>> list2 = [[1, 2], 1, 3, [1, 3, [1, 2]]]
                                >>> list2.count(1)                          # 1在list1中出现了1次
                                1
                                >>> list2.count([1, 2])
                                1
    
                            
                        
  • index(value, start ,stop): 从列表中找出某个值第一个匹配项的索引位置,可以指定起始和结束位置(类似切片表示法),默认从列表的开始位置开始查找,直到结尾。

    如果未找到匹配值,会引发异常,一般会先通过in判断是否存在,再调用index方法获取位置。

                            
                                >>> n_list = [11, 22, 33, 22, 66]
                                >>> n_list.index(22)             # 第一个匹配项的索引
                                1
                                >>> n_list.index(22, 2)          # 从第2个索引处(包含2)开始查找,找到的第一个匹配项的索引
                                3
                                >>> n_list.index(66)
                                ValueError: 66 is not in list
                            
                        
  • insert(index ,value): 将对象插入到列表中的指定索引之前。
                            
                                >>> n_list = [11, 22, 44, 55]
                                >>> n_list.insert(2, 33)     # 在位置2(44)前插入33,直接扩展原列表,返回值为空,对应的分片赋值实现为:n_list[2:2] = [33]
                                >>> n_list
                                [11, 22, 33, 44, 55]
                            
                        
  • pop(index): 移除列表中指定索引处的一个元素(默认是最后一个)。

    pop方法是列表中唯一一个既修改列表又返回元素值的方法。

                            
                                >>> n_list = [11, 22, 33, 44, 55]
                                >>> n_list.pop()            # 移除最后一个元素,并将元素返回
                                55
                                >>> n_list
                                [11, 22, 33, 44]
                                >>> n_list.pop(1)           # 移除索引为1的元素(22),并将元素返回
                                22
                            
                        

    通过append和pop方法可以实现两种常用的数据结构-栈(后进先出LIFO)和队列(先进先出FIFO)。

  • remove(value): 移除列表中某个值的第一个匹配项。

    类似于index,如果值在列表中不存在会引发异常,可以先通过in进行判断,再调用remove方法。

                            
                                >>> n_list = [11, 22, 33, 22, 66]
                                >>> n_list.remove(22)       # 从列表中移除22,返回值为空
                                >>> n_list
                                [11, 33, 22, 66]            # 可以看到只有第一个匹配项被移除了,另外一个22仍然存在于列表中
                                >>> n_list.remove(55)       # 因为55在列表中不存在,引发异常
                                ValueError: list.remove(x): x not in list
                            
                        
  • reverse: 将列表中的元素反向存放。
                            
                                >>> n_list = [1, 2, 3]
                                >>> n_list.reverse()        # 返回值为空
                                >>> n_list                  # 修改原列表
                                [3, 2, 1]
                            
                        

    如果不想修改原列表,而是生成一个新的反向列表,可以通过先分片复制再调用reverse方法,或者list(reversed(n_list))方法来实现。

                            
                                >>> n_list = [1, 2, 3]
                                >>> list1 = n_list[:]               # 复制列表
                                >>> list1.reverse()                 # 将新列表的元素反向
                                >>> list1
                                [3, 2, 1]
                                >>> list2 = list(reversed(n_list))  # reversed方法会返回一个反向的迭代器对象,再调用list函数即可生成新的列表
                                >>> list2
                                [3, 2, 1]
                            
                        

列表排序

  • sort(key=None, reverse=False): 用于在原位置对列表进行排序,会改变原列表,而不是返回一个排序后的副本
                            
                                >>> n_list = [6, 3, 2, 5, 4, 1]
                                >>> result = n_list.sort()
                                >>> print(result)               # 方法返回值为空
                                None
                                >>> n_list
                                [1, 2, 3, 4, 5, 6]              # 原列表发生改变
                            
                        

    如果想保持原列表不变,只想获取一个排序以后的列表副本,有两个实现方式

    • 通过分片生成一个列表副本,再通过sort方法对副本列表进行排序
                                      
                                          >>> n_list = [6, 3, 2, 5, 4, 1]
                                          >>> c_list = n_list[:]          # 生成列表副本
                                          >>> c_list.sort()               # 对副本进行排序
                                          >>> c_list
                                          [1, 2, 3, 4, 5, 6]              # 原列表发生改变
                                      
                                  
    • 使用sorted(iterable, *, key=None, reverse=False)函数,sorted函数返回结果为排序以后的列表副本,而不改变原列表
                                      
                                          >>> n_list = [6, 3, 2, 5, 4, 1]
                                          >>> c_list = sorted(n_list)     # 生成排序以后的列表副本
                                          >>> c_list
                                          [1, 2, 3, 4, 5, 6]
                                          >>> n_list
                                          [6, 3, 2, 5, 4, 1]              # 原列表不变
                                      
                                  
  • 高级排序
    • 反向排序

      sort方法排序以后的原列表或者sorted生成的排序以后的列表副本,都是从小到大排列的, 如果想得到从大到小结果,可以通过将reverse参数设置为True,默认为False。

                                      
                                          >>> n_list = [6, 3, 2, 5, 4, 1]
                                          >>> n_list.sort(reverse = True)     # 调用排序方法,并传入reverse参数
                                          >>> n_list
                                          [6, 5, 4, 3, 2, 1]                  # 结果反向排列
                                      
                                  
    • 自定义比较函数

      sort默认排序结果很可能不是我们希望得到的结果,比如以下对于一个IP地址列表的排序

                                      
                                          >>> ip_list = ['192.168.10.200',
                                                         '192.168.9.210',
                                                         '192.168.9.230',
                                                         '192.168.10.60']
                                          >>> ip_list.sort()
                                          >>> ip_list
                                          ['192.168.10.200',
                                           '192.168.10.60',
                                           '192.168.9.210',
                                           '192.168.9.230']
                                      
                                  

      而我们希望的结果是

                                      
                                          ['192.168.9.210',
                                           '192.168.9.230'
                                           '192.168.10.60',
                                           '192.168.10.200']
                                      
                                  

      在Python2中sort方法可以通过cmp参数自定义比较函数cmp,但是Python3中移除了cmp参数。

      自定义比较函数可以通过key参数定义,请注意,参数key是一个在排序过程中使用的函数,但是该函数并不是直接用来确定对象的大小,而是为每个元素创建一个用于比较的键,该函数也只有一个参数。

      使用key自定义比较函数主要有两种实现方法

      • key函数作为转换器,将元素转换成比较大小的值,比如将IP地址转换成数字返回,然后根据返回的值进行排序。
                                                
                                                    def key_converter(ip):
                                                        ip1s = ip.split('.')
                                                        int_value = int(ip1s[0]) * 0x1000000 + int(ip1s[1]) * 0x10000 + int(ip1s[2]) * 0x100 + int(ip1s[3]) * 1
                                                        return int_value
        
        
                                                    ip_list = ['192.168.10.200',
                                                               '192.168.9.210',
                                                               '192.168.9.230',
                                                               '192.168.10.60']
        
                                                    ip_list.sort(key=key_converter)
                                                    print(ip_list)
                                                    #['192.168.9.210', '192.168.9.230', '192.168.10.60', '192.168.10.200']
                                                
                                            
      • 定义cmp(a,b)比较函数,参数为两个待比较的值,通过functools模块的cmp_to_key方法生成key参数。 (该函数主要用作从Python 2转换而来的程序的过渡工具)
                                                
                                                    import functools
        
                                                    def key_cmp(ip1, ip2):
                                                        ip1s = ip1.split('.')
                                                        ip2s = ip2.split('.')
                                                        ip1v = int(ip1s[0]) * 0x1000000 + int(ip1s[1]) * 0x10000 + int(ip1s[2]) * 0x100 + int(ip1s[3]) * 1
                                                        ip2v = int(ip2s[0]) * 0x1000000 + int(ip2s[1]) * 0x10000 + int(ip2s[2]) * 0x100 + int(ip2s[3]) * 1
                                                        if ip1v < ip2v:
                                                            return -1
                                                        if ip1v > ip2v:
                                                            return 1
                                                        return 0
        
                                                    ip_list = ['192.168.10.200',
                                                               '192.168.9.210',
                                                               '192.168.9.230',
                                                               '192.168.10.60']
                                                    ip_list.sort(key=functools.cmp_to_key(key_cmp))
                                                    print(ip_list)
                                                    #['192.168.9.210', '192.168.9.230', '192.168.10.60', '192.168.10.200']
                                                
                                            

元组tuple

元组与列表一样,也是一种序列。唯一的不同是元组不能修改(字符串也不能修改,后续章节会做相关介绍)

创建元组

元组主要有三种构造方法:

  • 最简单也是最常用的方式就是直接用圆括号()定义
                            
                                >>> tuple1 = ('success', 200)
                                >>> tuple1
                                ('success', 200)
                                >>> tuple2 = ('success',)       # 请注意,如果只有一个值,也必须加逗号,否则就变成了字符串,('success')表示一个字符串,而不是元组
                                >>> tuple2
                                ('success',)
                            
                        
  • 省略圆括号,直接用逗号隔开
                            
                                >>> tuple1 = 'success', 200
                                >>> tuple1
                                ('success', 200)
                                >>> tuple2 = 'success',                     # 请注意,如果只有一个值,也必须加逗号,否则就变成了字符串,而不是元组
                                >>> tuple2
                                ('success',)
                                >>> ('success', 200,) == ('success', 200)   # 如果有两个及以上的元素,最后一个逗号不起作用
                                True
                            
                        
  • 通过tuple()函数生成元组
                            
                                >>> tuple1 = tuple(['success', 200])
                                >>> tuple1
                                ('success', 200)
                                >>> tuple2 = tuple('abc')
                                >>> tuple2
                                ('a', 'b', 'c')
                                >>> tuple3 = tuple((1, 2, 3))
                                >>> tuple3
                                (1, 2, 3)
                            
                        

存在意义

大多数情况,元组都可以用列表来代替,除了以下两种情况

  • 元组是不可变的,所以可以作为字典的键值,而列表不能。
  • 元组是很多内建函数的返回值。

字典dict

字典是一种通过名字来引用值的映射数据结构,字典中的值没有顺序,值都存储在一个特定的键下,这个键可以是数字、字符串或是元组(不可变)。

可以把字典想象成类似于书本目录的结构,可以根据目录快速的找出要看的章节页,然后快速翻到对应页。

与列表的区别

与列表的主要区别如下:

  • 键类型: 列表是通过数字位置索引顺序存储的元素,而字典是通过名字索引(数字、字符串或是元组等不可变类型)来引用的值。
  • 顺序: 列表中的元素是有序的,而字典是无序的。
  • 自动添加: 列表不能操作超出列表长度的元素,否则会报错,而字典可以随意增/删元素。
  • 成员资格: 表达式 k in items, 字典查找的是键,而列表查找的是值

有些场景可能字典比列表更加适用,比如:

  • 电话簿: 通过姓名可以很容易的查找到对应的电话号码。
  • 书本目录: 通过章节目录可以很容易的找到对应的页数。

创建字典

字典由多个键及其对应的值构成的键-值对(或称为项)组成,键和值之间用冒号(:)隔开,项之间用逗号(,)隔开。

字典主要有两种构造方法:

  • 最简单也是最常用的方式就是直接用大括号{}定义
                            
                                ph_dict = {
                                    'zht': '10000', # 'zht'是键,'10000'是值
                                    'lz': '10086'
                                }
    
                            
                        
  • 也可以使用dict函数创建(此种方式较少使用)
                            
                                ph_dict = dict(zht='10000',
                                               lz='10086')
                            
                        

字典基本操作

  • d[k] --获取关联到键k上的值
  • d[k] = v --将值v关联到键k上,项可自动添加,如果键不存在则为它赋值,如果存在则替换
  • del d[k] --删除键为k的项,如果k在d中不存在,会引发KeyError异常,可以使用d.pop(k, None)方法来删除键值
  • k in d --检查d中是否含有为k的项,True/False
  • k not in d --检查d中是否不包含键为k的项,True/False
  • len(d) --返回d中项(键-值)数量

字典方法

  • get(key[, default]): 获取关联到键k上的值,相对于d[k],get方法更宽松,如果获取不存在的键上的值,d[k]会引发KeyError异常,而get方法不会

    如果使用过其他编程语言,可能习惯使用obj.key的方式访问属性,这种方式在Python中是不支持的,这可能是初学者经常会犯的错误。

    获取字典键值的操作,建议都通过get方法获取。

    另外还提供了一个default的参数,用于返回当键不存在时的默认值

                            
                                >>> d = {'name': 'zht', 'age': 18}
                                >>> d.get('name')               # 返回键name对应的值
                                'zht'
                                >>> d['city']                   # d[k]获取不存在的键的值,会引发异常
                                KeyError: 'city'
                                >>> d.get('city')               # get方法不会引发异常,而是得到了None值
                                None
                                >>> d.get('city','Shanghai')    # 如果键不存在,返回默认值'Shanghai'
                                'Shanghai'
                                >>> d.get('age','N/A')          # 如果键存在,默认值不起作用,返回的是键对应的值
                                18
                            
                        
  • clear: 清除原字典中所有的项
                            
                                >>> d = {'name': 'zht', 'age': 18}
                                >>> d.clear()           # 操作原字典,无返回值
                                >>> d
                                {}
                            
                        
  • copy: 返回一个具有相同键-值对的新字典
                            
                                >>> d = {'name': 'zht', 'age': 18}
                                >>> d1 = d.copy()
                                >>> d1
                                {'name': 'zht', 'age': 18}
                            
                        

    请注意,copy是浅复制,列表、字典或对象实例等属于引用值类型, 变量中保存的实际上只是一个指针,这个指针指向内存堆中实际的值,多个值指向同一个内存,如果修改了某个字典的值,其它字典也会改变。

                            
                                >>> d = {'name': 'zht', 'age': 18, 'address': {'country': 'China', 'city': 'Shandong'}}
                                >>> d1 = d.copy()
                                >>> d1['address']['city'] = 'Shanghai'  # 修改字典d1的address中的city值
                                >>> d1
                                {'name': 'zht', 'age': 18, 'address': {'country': 'China', 'city': 'Shanghai'}} # 字典d1的值发生了变化
                                >>> d
                                {'name': 'zht', 'age': 18, 'address': {'country': 'China', 'city': 'Shanghai'}} # 字典d的值也发生了变化
                            
                        

    可以通过copy模块的深复制函数deepcopy来避免这种相互影响

                            
                                >>> from copy import deepcopy
                                >>> d = {'name': 'zht', 'age': 18, 'address': {'country': 'China', 'city': 'Shandong'}}
                                >>> d1 = deepcopy(d)
                                >>> d1['address']['city'] = 'Shanghai'      # 修改字典d1的address中的city值
                                >>> d1
                                {'name': 'zht', 'age': 18, 'address': {'country': 'China', 'city': 'Shanghai'}}     # 字典d1的值发生了变化
                                >>> d
                                {'name': 'zht', 'age': 18, 'address': {'country': 'China', 'city': 'Shandong'}}     # 字典d的值保持不变
                            
                        
  • keys(): 获取字典中的键的一个视图对象,当字典更新时这个视图对象会自动更新
                            
                                >>> d = {'name': 'zht', 'age': 18}
                                >>> keys = d.keys()                     # 返回键的视图对象
                                >>> keys
                                dict_keys(['name', 'age'])
                                >>> len(keys)                           # len函数获取字典中键的数量
                                2
                                >>> d['city'] = 'Shanghai'
                                >>> keys
                                dict_keys(['name', 'age', 'city'])      # 字典更新时,视图对象会自动更新
                                >>> key_list = list(keys)               # 可以通过list函数将视图转为列表使用,这个列表不会随着字典键值变化而变化
                                >>> key_list
                                ['name', 'age', 'city']
    
                            
                        
  • values(): 获取字典中的值的一个视图对象,当字典更新时这个视图对象会自动更新
                            
                                >>> d = {'name': 'zht', 'age': 18}
                                >>> values = d.values()                     # 返回值的视图对象
                                >>> values
                                dict_values(['zht', 18])
                                >>> len(values)                             # len函数获取字典中值的数量
                                2
                                >>> d['city'] = 'Shanghai'
                                >>> values
                                dict_values(['zht', 18, 'Shanghai'])        # 字典更新时,视图对象会自动更新
                            
                        
  • items(): 获取字典项(键-值)的一个视图对象,当字典更新时这个视图对象会自动更新
                            
                                >>> d = {'name': 'zht', 'age': 18}
                                >>> items = d.items()                                               # 返回字典项的视图对象
                                >>> items
                                dict_items([('name', 'zht'), ('age', 18)])
                                >>> len(items)                                                      # len函数获取字典项的数量
                                2
                                >>> d['city'] = 'Shanghai'
                                >>> items
                                dict_items([('name', 'zht'), ('age', 18), ('city', 'Shanghai')])    # 字典更新时,视图对象会自动更新
                            
                        
  • setdefault(key[, default]): 设置字典中不存在的键的默认值,如果键已经存在,则setdefault不起作用。
                            
                                >>> d = {'name':'zht'}
                                >>> d.setdefault('name', 'N/A')     # 如果键存在,不起作用,返回键对应的值
                                'zht'
                                >>> d                               # 字典没变化
                                {'name': 'zht'}
                                >>> d.setdefault('city', 'N/A')     # 设置默认值,并返回默认值
                                'N/A'
                                >>> d                               # 字典被更新
                                {'name': 'zht', 'city': 'N/A'}
                                >>> d.get('city')                   # 键未设置,返回默认值
                                'N/A'
                                >>> d.get('city','None')            # 键未设置时,如果setdefault了,get方法的默认值不起作用
                                'N/A'
                                >>> d['city'] = 'Shanghai'          # 设置键值
                                >>> d.get('city')
                                'Shanghai'
                            
                        

    setdefault方法和d[k]=v表达式的主要区别时,当键存在时setdefault不起作用,而d[k]=v会更新字典。

  • update([other_dict]): 利用一个字典更新原字典,提供的字典中的项会被添加到原字典中,如果键相同则会覆盖。
                            
                                >>> d = {'name': 'zht', 'city': 'Shandong'}
                                >>> d.update({'age' : 18, 'city':'Shanghai'})       # age键值项会添加到字典d中,用新字典项中city对应的值,覆盖原字典项中city对应的值
                                >>>d
                                {'name': 'zht', 'city': 'Shanghai', 'age': 18}
                            
                        
  • pop(key[, default]): 获取给定键对应的值,然后将这个键值对从字典中移除。
                            
                                >>> d = {'name': 'zht', 'city': 'Shanghai'}
                                >>> d.pop('city')       # 返回键对应的值
                                'Shanghai'
                                >>>d                    # 将键值对从字典中移除
                                {'name': 'zht'}
                                >>> d.pop('age')        # 如果键不存在会引发异常
                                KeyError: 'age'
                                >>> d.pop('age',18)     # 如果键不存在,可以提供默认值
                                18