Python中的 “苦力” —— 列表

定义

列表是 Python 中最基本的数据结构。在 Python 中,用一个方括号表示一个列表——[]。列表中的每个元素都分配有一个数字—它的位置,或索引,第一个索引是0,第二个是1,以此类推。列表与数组是比较相似的。

创建列表

创建一个空列表

>>> a = []
>>> type(a)
<class 'list'>

>>> bool(a)
False
>>> print(a)
[]

上面创建了一个空列表,我们用type()函数查看其类型为list,然后用bool()函数查看并把其打印出来,也证明了我们创建的是一个空列表。下面创建一个非空列表:

>>> a = [1,2.0,'Python',None]
>>> a
[1,2.0,'Python',None]
>>> type(a)
<class 'list'>
>>> bool(a)
True
>>> print(a)
[1,2.0,'Python',None]

上述的例子中,我们创建了一个非空列表类型对象。上面我们说到列表与数组很相似,但从这里我们可以观察到明显的区别。可以看到在上述的例子中,列表包含了四种类型:整型、浮点型、字符串、None;数组内的元素要求必须同一类型,但列表不同,列表中的元素可以是任意类型,当然也包括嵌套列表;不仅如此,列表中的元素个数可以是无限个,但数组不能。

>>> a = [1,'Python']
>>> b = [2.0,None]
>>> c = [a,b]
>>> c
[[1, 'Python'], [2.0, None]]
访问列表
  • 索引

    与数组类似,列表也可以用索引的方式来访问元素,比较特殊的一点是 Python 支持负索引。

    列表a251834
    正索引012345
    负索引-6-5-4-3-2-1
    >>> a = [2,5,1,8,3,4]
    >>> a[0]
    2
    >>> a[3]
    8
    >>> a[5]
    4
    >>> a[-1]
    4
    >>> a[-3]
    8
    >>> a[-6]
    2
    

    对于负索引,如a[-3]实际上就是a[6+(-3)] = a[3],理解这一点,在接下来要介绍的切片中我们也会用到负索引。

  • 切片

    除了通过索引访问单个元素的方式外,Python 中还有一种切片的访问方式可以一次性访问多个元素。(实际上我们在讲字符串时已经介绍过)基本操作就是在冒号的两边加上要访问的索引值。

    
    >>> lst = [2,5,3,1,4]
    >>> lst[1:4]
    [5, 3, 1]
    

    如上例子,可以实现一次性访问索引1到4但不包过4的子序列。接下来看下负索引的方式:

    >>> lst[-3:-1]
    [3, 1]
    

    需要注意的是,不管是用正索引还是负索引,在冒号的两边,左边的值必须是小于右边的值否则会返回空列表。

    >>> lst[4:1]
    []
    >>> lst[-1:-3]
    []
    

    还有一些特殊的边界值:

    # lst[0:4] 等效于 lst[:4]
    >>> lst[0:4]
    [2, 5, 3, 1]
    >>> lst[:4]
    [2, 5, 3, 1]
    
    # lst[1:5] 等效于 lst[1:]
    >>> lst[1:5]
    [5, 3, 1, 4]
    >>> lst[1:]
    [5, 3, 1, 4]
    
    # lst[:]可以访问整个列表元素
    >>> lst[:]
    [2, 5, 3, 1, 4]
    

    上面只用了正索引作为示例,负索引也是同样的,这里不再举例。但是,这里发现一个不一样的地方,lst长度为5,在上例中lst[1:5]是可以正常访问的,但按道理是没有5这个索引的,不过由于我们前面说过,冒号右边的索引是不包括在内的,也就是只访问该索引的前一个位置。看似正常,那我们继续来看下面的例子:

    >>> lst[1:8]
    [5, 3, 1, 4]
    >>> lst[1:10]
    [5, 3, 1, 4]
    

    可以看到冒号右边的数字已然超过列表的长度值,但访问的结果与lst[1:5]是一致的。实际上如果冒号右边的数字大于或等于列表的长度,但由于列表没有那么多元素,所以访问到的最多依然是列表的最后一个值;虽说如此,但不建议这样使用。

    上面是使用单个冒号的情况,在切片访问方式中还可以使用两个冒号获取带间隔的序列元素,两个冒号最后的数值就是间隔长度:

    >>> lst = [1,2,3,4,5,6,7,8,9,10]
    >>> lst[0:11:2]
    [1, 3, 5, 7, 9]
    

    start:stop:interval实际上这就是最全的切片结构,如上所示,获得的切片为:索引从0到11间隔(步长)为2的序列元素组成的子序列。

反转列表

所谓反转列表,即将[1,2,3]变为[3,2,1],因为在编程中经常会用到,所以这里独立出来单独讲。

  • 使用切片

    实际上我们用到的还是上面提到的列表的切片访问方式。

    >>> lst = [1,2,3,4,5,6]
    >>> lst[::-1]
    [6, 5, 4, 3, 2, 1]
    >>> lst
    [1, 2, 3, 4, 5, 6]
    

    可以看到,反转后对原来的对象并没有影响。lst[::-1]的本质是从右边(符号,表示反方向)看这个列表,然后按照步长为1取列表中的元素,并生成新的列表。如果将-1换成1,即lst[::1],这与lst[::]结果一致。虽然切片操作简单,但在实际应用中不推荐使用,下面介绍一种较常用的方法。

  • 使用 reversed() 函数

    使用reversed()函数可以将列表对象进行反转,不过该函数返回的是一个迭代器,因此我们还需要将其再转化为列表再表示出来。

    >>> a = [3,2,5,4]
    >>> b = list(reversed(a))
    >>> b
    [4, 5, 2, 3]
    

    注: 迭代器后续会详述。

  • 使用reverse() 函数

    >>> a = [3,2,5,4]
    >>> a.reverse()
    >>> a
    [4, 5, 2, 3]
    

    reverse()reversed()虽然名字很相似,都可以反转列表元素,但不同的是,reverse()是原地反转,即对原列表作用,是没有返回值的。

  • 使用 sorted()sort() 函数

    >>> a = [1,2,3,4,5]
    >>> b = sorted(a,reverse=True)
    >>> b
    [5, 4, 3, 2, 1]
    >>> a = [5, 4, 3, 2, 1]
    
    >>> a.sort()
    >>> a
    [1, 2, 3, 4, 5]
    

    利用sorted()函数及reverse参数对原列表进行排序后反转,resverse = True表示按降序排序,resverse = False表示按升序排序。如上所述,需要对列表对进行排序,所以这种方法只适用于有序列表的反转。

操作列表(访问、添加、删除)
  • 添加元素

    列表与数组、字符串不一样的是其元素时可以改变的,且支持动态添加元素。

    使用append()insert()添加单个元素:

    >>> a = [1,2,3,4]
    >>> a.append(5)			# append()方法默认在列表尾部添加元素
    >>> a
    [1, 2, 3, 4, 5]
    >>> a.insert(2,6)		# insert()方法在指定索引处添加元素,这里在索引为2处添加元素6,原始该位置及后面的元素都往后挪动
    >>> a
    [1, 2, 6, 3, 4, 5]
    

    使用extend()+实现一次添加多个元素:

    >>> a.extend([7,8])
    >>> a
    [1, 2, 6, 3, 4, 5, 7, 8]
    >>> b = a + [9,10]
    >>> a
    [1, 2, 6, 3, 4, 5, 7, 8]
    >>> b
    [1, 2, 6, 3, 4, 5, 7, 8, 9, 10]
    

    值得注意的是,在使用extend()方法为列表添加多个元素时,并没有创建一个新的列表,而是直接在原列表进行添加,这被称为in-place即就地。而使用 +则是创建了一个新的列表对象,所以在上述例子中我们用一个新的变量接收了该对象,也因此此种方式不是就地批量添加元素。

    >>> a = [1,2,3,]
    >>> b = "abc"
    >>> a.extend(b)
    >>> a
    [1, 2, 3, 'a', 'b', 'c']
    

    从上面的例子中可以看到,在extend字符串时,字符串是被以字符为单位拆开后追加到了a里面。extend()的参数必须是iterable类型对象,即可迭代对象。这里再提一点就是我们常用的+=操作符实际就是调用extend()方法来进行合并运算。

  • 删除元素

    remove()直接删除元素,若被删除元素不在列表中,则会报错,若在列表内重复出现多次,则只删除第一次出现的那个。

    >>> a = [1, 2, 3,2, 'a',2, 'b', 'c']
    >>> a.remove('d')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: list.remove(x): x not in list
    >>> a.remove(1)
    >>> a
    [2, 3, 2, 'a', 2, 'b', 'c']
    >>> a.remove(2)
    >>> a
    [3, 2, 'a', 2, 'b', 'c']
    
    

    pop()删除指定索引位置的元素,若不带参数则默认删除列表最后一个元素,该方法返回的结果是被成功删除的元素。

    >>> lang = ['Python','Java','PHP','C++','SQL']
    >>> lang.pop()
    'SQL'
    >>> lang
    ['Python', 'Java', 'PHP', 'C++']
    >>> lang.pop(2)
    'PHP'
    >>> lang
    ['Python', 'Java', 'C++']
    

    同时,如果索引超出范围,也是同样会报错的

    >>> lang
    ['Python', 'Java', 'C++']
    >>> lang.pop(3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    IndexError: pop index out of range
    

    delpop相似,同样是删除索引处的元素,不一样的是del一次性可以删除多个元素,通过切片方式:

    >>> del lang[-1]
    >>> lang
    ['Python', 'Java']
    >>> del lang[:]
    >>> lang
    []
    
  • 列表与 in

    列表是可迭代的,除了使用常规的索引遍历外,还支持for item in alist这种直接遍历元素的方法。

    >>> lst = [2,5,6,4,3,1]
    >>> for item in lst:
    ...     print(item)
    ... 
    2
    5
    6
    4
    3
    1
    

    in与可迭代器结合,还可以用于判断某个元素是否属于此列表:

    >>> lst = [2,5,6,4,3,1]
    >>> 4 in lst
    True
    >>> 7 in lst
    False
    
  • 列表与数字

    内置的list通过*与数字结合,可以实现重复序列的操作:

    >>> a = ["hi","Hello"]*3
    >>> a
    ['hi', 'Hello', 'hi', 'Hello', 'hi', 'Hello']
    

    使用列表与数字相乘构建二维列表:

    >>> a = [[]] * 3
    >>> a[0] = [1,2]
    >>> a[1] = [3,4]
    >>> a[2] = [5,6]
    >>> a
    [[1, 2], [3, 4], [5, 6]]
    
  • 求列表长度

    使用len求取列表长度:

     >>> a = [1,2,3,4,5,6]
    >>> len(a)
    6
    
  • 求列表中元素的最值

    >>> a = [1,2,3,4,5,6]
    >>> max(a)
    6
    >>> min(a)
    1
    
  • 修改列表元素

    前面讲到列表与数组、字符串不同的地方就是列表是可变的,这也意味着它的元素时可以改变的:

    >>> a = ['Python','java','C','C++']
    >>> a[1] = 'PHP'
    >>> a
    ['Python', 'PHP', 'C', 'C++']
    
列表生成式

列表生成式是创建列表的一种简洁又方便且强大的方法。
使用列表生成式创建1到100的所有偶数列表:

>>> lst = [i for i in range(1,100) if i%2==0 ]
列表常用函数

实际上在上面已经提到许多方法了,除此之外列表还有如下常用封装的方法。

  • clear()

    clear用于清空列表内的所有元素:

    >>> a = [1,2,3,4,5,6]
    >>> a.clear()
    >>> a
    []
    
  • index()

    index()用于查找指定元素在列表内的索引:

    >>> a = [1,2,3,4,5,6]
    >>> a.index(5)
    4
    
  • count()

    count用于统计某元素在列表中出现的次数:

    >>> a = [1,2,3,5,2,6,7,2]
    >>> a.count(2)
    3
    
  • sort()

    sort用于对列表内元素进行排序,方法有两个参数,sort(key=None,reverse=False) ,其中参数key定制排序规则, reverse参数决定排序方式。

    # 默认情况下,key=None,reverse=False,即没有排序规则,按升序排序
    >>> a = [2,5,3,1,4,6]
    >>> a.sort()
    >>> a
    [1, 2, 3, 4, 5, 6]
    

    reverse参数比较好理解,下面介绍下参数key怎么使用。

    按照字符串元素长度大小进行排序:

    >>> lst = ['Python','C','Java','C++','JavaScript']
    >>> lst.sort(key=len)
    >>> lst
    ['C', 'C++', 'Java', 'Python', 'JavaScript']
    

    如下示例,列表元素为元组,根据元组第二个值由小到大排序:

    >>> a=[(3,1),(4,2),(1,3),(5,4),(9,-10)]
    >>> a.sort(key=lambda x:x[1])
    >>> a
    [(9, -10), (3, 1), (4, 2), (1, 3), (5, 4)]
    

    在前面我们介绍过sorted()函数,两者的区别在于sort是应用在list上的方法,而sorted可以对所有可迭代的对象进行排序操作。且listsort方法是对列表进行就地操作,没有返回值,而sorted返回的是一个新的list

列表包含自身

列表的赋值操作,有一个有意思的问题:

>>> a = [1,2,3,4]
>>> a[2] = a
>>> a
[1, 2, [...], 4]

上面的输出结果,中间省略号表示无限循环,也就是说a[2] = a这种赋值操作导致了无限循环,这是为什么呢?

在执行a = [1,2,3,4]的时候, Python 会先创建一个列表对象[1,2,3,4],然后变量a指向该对象。而在执行a[1] = a时,Python 做的是把列表对象的第二个元素指向a所引用的列表对象,即列表对象本身。这样,如果画图的话实际上这已经形成一个环路:a[1]->a->a[1],所以会导致无限循环。在实际编程中切记避免此种赋值操作。

列表和字符串转化

在符合某些条件的情况下,可以实现列表和字符串之间的转化。这里会使用到split()join(),这两个方法实际上我们在介绍字符串时已经有介绍过。

  • split()

    >>> str = "Python is a clean language"
    >>> str.split(" ")
    ['Python', 'is', 'a', 'clean', 'language']
    
  • join()

    join可以说是split的逆运算,将列表元素连接成一个字符串

    >>> a = ['Python', 'is', 'a', 'clean', 'language']
    >>> "".join(a)
    'Pythonisacleanlanguage'
    >>> " ".join(a)					# 以空格作为连接分隔符
    'Python is a clean language'
    
深浅拷贝

list封装有cpoy方法实现对列表的浅拷贝,何为浅拷贝?

>>> a = [1,3,6]
>>> a1 = a.copy()
>>> a1
[1, 3, 6]
>>> a1[1] = 4
>>> a1
[1, 4, 6]
>>> a
[1, 3, 6]

a1a分别指向不同内存地址的对象,从上述a1改变列表内元素就可以知道这点,但如果列表有嵌套情况:

>>> a = [[1,2],3,4]
>>> a1 = a.copy()
>>> a1
[[1, 2], 3, 4]
>>> a1[1] = 5
>>> a1
[[1, 2], 5, 4]
>>> a
[[1, 2], 3, 4]
>>> a1[0][1] = 3
>>> a1
[[1, 3], 5, 4]
>>> a
[[1, 3], 3, 4]

从上面的示例结果中可以看出,对于copy来说内嵌的列表并没有实现拷贝,而是指向原列表对象中的嵌套列表,所以在a1更改嵌套列表中元素时,a中的嵌套列表的元素也随之发生改变。如果不想更改a中的元素,我们可以用copy模块中的深度拷贝deepcopy来实现。

>>> a = [[1,2],3,4]
>>> import copy
>>> a1 = copy.deepcopy(a)
>>> a1
[[1, 2], 3, 4]
>>> a1[0][1] = 5
>>> a1
[[1, 5], 3, 4]
>>> a
[[1, 2], 3, 4]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值