【Python】List列表类 & Tuple元组类

List列表类型

列表是包含0个或多个对象引用的有序序列,是一种序列类型。和接下来要介绍的元组不同,列表的长度和元素都是可变的,用户可以自由地对列表中的元素进行增加、修改、删除、替换。列表没有长度限制,并且其中的元素不要求类型相同,比如说我可以建立一个这样的列表:
a='str'
b=8
c=9.0
d=[1,2,3]
list=[a,b,c,d]
print(list)
for item in list: print(type(item))
【Python】List列表类 & Tuple元组类
这一段代码中展示了我们如何创建一个列表,只需要像第五行那样,list=[列表中的内容]即可。而list=[ ]则是创建一个不包括任何元素的空列表。列表还可以使用其他的序列类型来初始化,比如:
tuple=('you',5,9.0,[123,456])
list=list(tuple)
print(list)
print(type(list))
print(type(tuple))
【Python】List列表类 & Tuple元组类
tuple是一个元组类型的变量,我们用tuple中的元素来初始化这个列表,形式为L=list(tuple)
list()方法可以认为是list列表类型的构造器(constructor),这是一个面向对象编程的概念。之前我们提到过不可变对象和可变对象的概念。String或者int那样的不可变对象在引用同一个值,例如‘abcd’或者8时,其实都是同一个对象:
str1='abcd'
str2=str1
a=8
b=a
print(id(str1))
print(id(str2))
print(id(a))
print(id(b))
【Python】List列表类 & Tuple元组类
但我们对其中一个进行修改时,并不会影响到另一个变量所引用的内容,承接上面的代码,我们调整代码如下:
str1='abcd'
str2=str1
a=8
b=a
print(id(str1))
print(id(str2))
print(id(a))
print(id(b))
str2='abc'
b=9
print(str1)
print(str2)
print(id(str1))
print(id(str2))
print(a)
print(b)
print(id(a))
print(id(b))
【Python】List列表类 & Tuple元组类
可以看到str1和str2从引用同一个字符串到花开两朵天各一方,对其中一个的修改并不会影响到另外一个。而List列表类就是很典型的可变对象,可变对象在面临这样的操作时,会有怎样的不同呢。
list1=[1,2,3,4]
list2=list1
print(id(list1))
print(id(list2))
【Python】List列表类 & Tuple元组类
可以看到,list1和list2的id是相同的,这似乎和String以及int的行为一致。如果List也是不可变对象,那我们对list2所引用的值做出修改,例如list2[0]=999,就不会改变list1所引用的对象。list2自己去引用[999,2,3,4],而list1继续引用[1,2,3,4]。事实如何呢?
list1=[1,2,3,4]
list2=list1
print(id(list1))
print(id(list2))
list2[0]=999
print(list1)
print(list2)
print(id(list1))
print(id(list2))
【Python】List列表类 & Tuple元组类
我们看到,仅仅对list2做了修改,但list1中的内容却也受到了影响,这是和String类型行为不同的地方。这一结果印证了我们的猜想,List所引用的对象是可以修改的,我最初引用的内容是[1,2,3,4],想把内容修改为别的是可行的。而String类型则不同,我最初引用的内容是’abcd’,我希望将内容修改为’abc’,这是做不到的,我只能去引用’abc’这个对象,而’abcd’还在那里,它并没变成’abc’。这就是两段代码中,id()函数反映出来的存储实质。
总结一下,当希望变量内容发生变化时,如果可以直接对内容进行修改而不用重新引用对象,就是可变对象;反之如果无法对内容进行修改而需要重新引用新的、所期望的对象时,就是不可变对象。

List的创建

如前所述,list可以通过[ ]来创建,也可以通过list()函数来创建。[ ]创建列表时需要列出表中的元素,而list()函数则可以将字符串、元组、range等类型转化为列表。
list1=[1,2,3]
tuple,str,range=(1,2,3),'acbdefg',range(1,6)
list2,list3,list4=list(tuple),list(str),list(range)
print(list2)
print(list3)
print(list4)
【Python】List列表类 & Tuple元组类
类似第二行和第三行的批量赋值操作也是Python的亮点之一,至少在我目前学过的语言中,这样的操作独此一家。批量赋值的实质时元组的赋值,即将赋值运算符的左右两边都看作元组类型。而上面提到的range类型是由range()函数返回的一种可迭代类型。
print(type(range(100)))
【Python】List列表类 & Tuple元组类
而如果我们希望创建一个内容相同,但互相独立的list对象,也就是——复制一份原来的对象内容。可以使用copy()方法或者list(rawlist)。后面一种方法类似于C++中复制构造器的概念。
list1=[1,2,3]
list2=list1.copy()
list3=list(list1)
print(list1)
print(list2)
print(list3)
print("%d\n%d\n%d"%(id(list1),id(list2),id(list3)))
list2[0]=999
list3[1]=998
print(list1)
print(list2)
print(list3)
print("%d\n%d\n%d"%(id(list1),id(list2),id(list3)))
【Python】List列表类 & Tuple元组类
实际上从第7行的输出结果我们就可以看出,list1、list2和list3直接仅仅是内容相同,它们并没有任何依赖关系,改变其中一个list除了影响它自己,没有任何副作用。

List的修改、添加、删除

List类型的修改相当便捷,就像C语言的数组操作那样,用[ ]作为索引来访问到要修改的单元,直接赋值即可完成修改。上面在讨论list的可变对象性质时已经做过展示。
而List类型的添加操作,常用的有三种方法:

  1. append(obj)方法,即把obj添加到list对象的最末尾;
  2. extend(seq)方法,可以批量地将另一个序列类型的对象seq全加到list对象末尾;
  3. insert(index,obj)方法,可以在index指定的位置来插入对象obj。

看到这里,了解C++和Java的人可能意识到了。List类型的操作和C++STL中的vector动态数组泛型对象以及Java中的ArrayList对象如出一辙。它们的区别就在于List可以包含不同类型的元素对象,而vector和ArrayList还受制于“数组”这一概念。这样的设置也说不清楚孰优孰劣,但只要运用娴熟的当,这些可变长的线性数据结构,是我们编程的一大助力。
list=[1,2,3,4]
print(list)
list.append(5)
list.extend(list)
print(list)
list.insert(2,10086)
print(list)
list.
【Python】List列表类 & Tuple元组类
对应的列表删除操作,也有三种方法:

  1. pop(index=-1),删除列表中 index 索引处的元素,默认 index=-1,返回值是该删除元素的值.
  2. remove(obj),删除列表中第一次出现的 obj 元素
  3. clear(),删除列表所有元素
    list=[1,2,3,4]
    a=list.pop()
    print(a)
    print(list)
    list.remove(3)
    print(list)
    list.clear()
    print(list)
    del list
    print(list)
    【Python】List列表类 & Tuple元组类
    这里我们需要区分一下的就是list.clear()del list,前者只是清空列表元素,后续还可以继续添加以及对这个列表进行各种操作;而后者是直接删除列表,是析构器(destructor)的概念,这个列表就结束生命周期了。

List的查找、计数、排序、翻转

首先我们讨论查找和计数,这两中操作在列表和字符串中的功能大同小异。
list=[1,1,2,3,4,5,99]
print(list.index(4))
print(list.count(1))
【Python】List列表类 & Tuple元组类
排序则是列表中经常用到的一个操作,这里我们不需要关心这个排序算法的细节,是插入排序还是选择排序。Python内置函数已经封装了实现细节,我们只需要明白它的接口如何使用,如何正确传递参数给它就可以。
list=[9,5,63,1,0,3]
list.sort()
print(list)
list.sort(reverse=True)
print(list)
【Python】List列表类 & Tuple元组类
通过实验不难发现,sort可以有升序、降序两种版本,而reverse参数控制了这一设置。reverse默认情况下为False表示不启用降序排序,而令reverse=True则启用了降序排列。
最后我们介绍list的reverse()方法,它可以将list的内容前后翻转,reverse()方法没有返回值。
list=[1,2,3]
print(list)
list.reverse()
print(list)
【Python】List列表类 & Tuple元组类

List推导

List推导也是在C++、Java这些语言中没有出现过的一种语法。List推导给我的感觉类似于C++ algorithm.h中的transform()函数,能够对遍历时的每一个元素做出同样的操作。当然,在代码简短与可读性之间,List推导不能免俗,它选择了代码简短,却丧失了一定的可读性,至少对于C风格的程序设计语言的人来说是这样。
list=list(range(11))
newList=[num**2 for num in list]
NewList=[num**3 for num in list if num <=5]
print(newList)
print(NewList)
【Python】List列表类 & Tuple元组类
这里代码中的**,是Python的乘方运算。通过上述实例,我们可以总结出这样的规律:
【Python】List列表类 & Tuple元组类

Tuple元组类型

元组类型也是Python中,很独到的一种类型,据说C++正在引进这一类型,也不知道什么时候能够见到。Tuple与List的差异之处很少,首先Tuple也是一种不可变类型,和String一样。通过下面一个实例,我们来验证这一点。
tuple1=(1,2,3)
tuple2=tuple1
print("%d\n%d"%(id(tuple1),id(tuple2)))
tuple2=(3,2,1)
print(tuple1)
print(tuple2)
print("%d\n%d"%(id(tuple1),id(tuple2)))
【Python】List列表类 & Tuple元组类
可见tuple类型所引用的对象是无法修改的。上例中也给出了tuple类型的创建方法之一:()创建元组,其中tuple=()将创建一个空元组。另外,如果元组中只有一个Number元素,包括int、float、complex,创建时也需要特殊对待,tuple=(1,)注意后面的逗号,它表明这是个元组,而不是带着括号的数字1.
有的人可能会有疑问,既然有了功能强大的List,为什么还需要Tuple?

  1. 首先Tuple的速度比List快很对,因为功能强大意味着实现复杂。如果定义了一系列常量值,需要对这一组值进行遍历,那么Tuple会比List快很多;
  2. 元组可以对数据实现写保护,增强代码的安全性。