python列表(list)和元组(tuple)浅析

列表(list)元组(tuple)都是一种数据结构,python将这种数据结构统称为序列(sequence)。和序列对应的就是映射(map),还有一种不属于这两种那就是集合(set)。这三种其实都属于python的另一种数据结构,即容器(container)。也就是说,python的容器是一种通用的数据结构,包括上面所说的这三种数据结构,它是一种包含其他对象的对象。其中序列包含很多类型,常见的有list, tuplelist中可以包含多种数据类型的元素,比如:score这个序列中既包含字符串的name,也包含number类型的marks。

>>> zhangsan=['zhang san', 98]
>>> lisi=['li si', 60]
>>> score=[zhangsan, lisi]
>>> score
[['zhang san', 98], ['li si', 60]]
>>> 

列表和元组的主要区别在于:列表可以修改,元组则不能。

序列的通用操作

所有的序列类型都可以进行某些特定的操作,这些操作包括:索引(indexing)分片(sliceing)加 (adding)乘(multipling),以及检查某个元素是否在序列中的in,此外,python还提供了一些内建(build-in)函数,如:max,min, len等。

索引操作(包括列表与元组)

接下来,我们看看索引操作相关的例子,很明显它可以直接以变量的方式按索引取元素值,也可以直接以序列的方式取,还包括以函数返回值的方式取:


>>> greeting='hello'
>>> greeting[0] #以变量的方式
'h'
>>> greeting[-1]
'o'
>>> 'hello'[0] #以序列的方式
'h'
>>> 'hello'[-1]
'o'
>>> raw_input('Year: ')[3] #以函数返回值的方式
Year: 2017
'7'

切片操作(包括列表与元组)

接下来看一看切片操作的相关例子:

>>> numbers=[1,2,3,4,5,6,7,8,9,10]
>>> numbers[1,10]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not tuple
>>> numbers[1:10]
[2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> numbers[1:10]
[2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> numbers[0:10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> numbers[:]   
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> numbers[5::2]
[6, 8, 10]
>>> numbers[5::-2]
[6, 4, 2]
>>> numbers[5::]  
[6, 7, 8, 9, 10]
>>> numbers[5:] 
[6, 7, 8, 9, 10]
>>> numbers[:2]  
[1, 2]
>>> numbers[:2:]
[1, 2]
>>> numbers[-3:0]
[]

请仔细观察上面的各个例子,这样才能更好的理解以下几点:

  • 分片以:分隔
  • 分片从0开始计数索引
  • 要取序列的首和末只需要分别将它们的位置留空
  • 只要左索引比右索引晚出现在序列中,就是一个空序列
  • 默认步长是1
  • 如果没有左或者右索引,取数的顺序取决于它的步长是正还是负,正是顺,负是逆

加法操作(包括列表与元组)

我们来看看加操作,请看例子:

>>> [1,2,3]+[4,5,6]
[1, 2, 3, 4, 5, 6]
>>> 'hello,'+'world' 
'hello,world'
>>> 'hello,'+[1,2,3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'list' objects
>>> 

从例子中可见,加操作的两边必须是同类型,否则就出错。
请注意这个+不会改变被加的序列(列表),而 list的 extend方法会。详见:

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

乘法操作(包括列表与元组)

再看看乘操作:

>>> 'love'*5
'lovelovelovelovelove'
>>> [1,2,3]*5
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> [None]*5
[None, None, None, None, None]
>>> [1,2,3]*[1,2,3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't multiply sequence by non-int of type 'list'
>>> 'love'*'love'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't multiply sequence by non-int of type 'str'
>>> 

请仔细观察上面的各个例子,这样才能更好的理解以下几点:

  • 乘之前是什么类型还是该类型
  • 乘操作只能以int类型作为参数

in 操作(包括列表与元组)

这里说一说in运算符,对于早期python版本(<2.3),判断字符是否在字符串中可以用in,但是这个只能用于判断单个字符,只有在2.3版本后,才可以允许多个字符的判断。

>>> 'h' in "hello"
True
>>> 'hell' in "hello"
True
>>> 3 in (1,2,3)
True

max和min函数(包括列表与元组)

看下max与min的内建函数:

>>> numbers=[1,9,4,2]
>>> max(numbers)
9
>>> min(numbers)
1
>>> max(1,9,4,2)
9
>>> min(1,9,4,2)
1
>>> max((1,2,3))
3

前两个操作好理解是基于序列的,但后两个操作很污啊,为什么直接传多个数值也可以啊,是因为如果用,分隔值,则自动创建元组。所以逗号是很重要的,这个需要注意一下。

>>> 1,2,3
(1, 2, 3)
>>> 

接下来分别对比讲一下列表及元组、字符串等。

只适合列表(list)的操作

列表(list)函数

list函数可以将一个不能修改的序列(如:字符串,元组),转换成list。
其实,list并不是函数,而是一种类型。下面我们看看如何来修改一个字符串,比如:

>>> str='python'
>>> str[0]='P'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> lst=list(str)
>>> lst
['p', 'y', 't', 'h', 'o', 'n']
>>> lst[0]='P'
>>> lst
['P', 'y', 't', 'h', 'o', 'n']
>>> 

基本的列表(list)操作

基本的列表操作,包括元素赋值(=),删除(del), 分片赋值

>>> x=[1,2,3]
>>> x[1]=5
>>> x
[1, 5, 3]
>>> del x[1]
>>> x
[1, 3]
>>> x[1:1]=2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable
>>> x[1:1]=[2]
>>> x
[1, 2, 3]
>>> x[1:]=[]
>>> x
[1]
>>> 

列表(list)的方法

列表(list),实际上是一个对象,那么它就有方法,调用的方法一般是 对象.方法() 的形式。注意,()很重要,表示函数调用。它的方法有很多,比如:append, count, extend, index, insert, pop, remove, reverse, sort等,

append(...)
    L.append(object) -- append object to end

count(...)
    L.count(value) -> integer -- return number of occurrences of value

extend(...)
    L.extend(iterable) -- extend list by appending elements from the iterable

index(...)
    L.index(value, [start, [stop]]) -> integer -- return first index of value.
    Raises ValueError if the value is not present.

insert(...)
    L.insert(index, object) -- insert object before index

pop(...)
    L.pop([index]) -> item -- remove and return item at index (default last).
    Raises IndexError if list is empty or index is out of range.

remove(...)
    L.remove(value) -- remove first occurrence of value.
    Raises ValueError if the value is not present.

reverse(...)
    L.reverse() -- reverse *IN PLACE*

sort(...)
    L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;
    cmp(x, y) -> -1, 0, 1

这里主要列举一些比较容易出错的几点:

1、 pop是唯一的既修改了列表,又有返回值的方法

首先如何判断某个方法是否有返回值?
最简单直观的方法就是在python控制台敲该方法,如果回车后没有输出值就表示没有返回值,否则输出的该值就是有返回值。举例如下,reverse是没有返回值的,而pop有返回值。

>>> x=[1,2,3]
>>> x.reverse() #无输出
>>> x
[3, 2, 1]

>>> x=[1,2,3]
>>> x.pop() #有输出,3为其返回值
3
>>> x
[1, 2]

另外一种方法就是用 print打印出来,如果是None则无返回值,否则就有。

>>> x=[1,2,3]
>>> print x.reverse() #返回值为None表示空
None
>>> x
[1, 2, 3]
>>> print x.pop(1) #返回值为2
2
>>> x
[1, 3]
>>> 

2、 什么是原位置操作(in-place operation)?

原位置操作(in-place operation)表示:在执行某个操作后(一般是调用list的某个方法),改变了原列表的元素位置。即如果某个方法改变了原列表的元素位置,则该方法即是原位置方法。

>>> a=[1,2,3]
>>> b=[4,5,6]
>>> a+b #+号不是原位置操作
[1, 2, 3, 4, 5, 6]
>>> a
[1, 2, 3]
>>> a.extend(b) #extend是原位置操作方法
>>> a
[1, 2, 3, 4, 5, 6]

3、sort函数解密

现在我们来看看sort这个函数,它首先是一个原位置操作方法,会改变原列表的排序。

>>> numbers=[5,2,9,7]
>>> numbers.sort()
>>> numbers
[2, 5, 7, 9]

另外,它又不会返回值,所以想通过它来接收排序后的结果是得不到的。有时候我们经常希望保持排序前的数据不变,在调用sort后,然后将其赋值到某个列表,但这个想法是错误的,如下:

>>> x=[5,2,9,7]
>>> y=x.sort()
>>> x
[2, 5, 7, 9]
>>> print y
None

那要怎么办?看来不能直接通过返回值来接收,那我们先赋值呢?将y=x呢?
我们来看看:

>>> x=[5,2,9,7]
>>> y=x
>>> y
[5, 2, 9, 7]
>>> y.sort()
>>> y
[2, 5, 7, 9]
>>> x
[2, 5, 7, 9]

虽然实现了y的排序,但是x的值也跟着变了。。这并不是我们想要的。
我们很快会想到用分片操作,试试看:

>>> x=[5,2,9,7] 
>>> y=x[:]
>>> y
[5, 2, 9, 7]
>>> y.sort()
>>> y
[2, 5, 7, 9]
>>> x
[5, 2, 9, 7]

果然可以了。原因是y=x这种赋值是一种引用的方法,是浅拷贝。而y=x[:]是深拷贝。
我们都知道,浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。具体一点,对于字符串类型来说,浅复制是对值的复制,对于对象来说,浅复制是对对象地址的复制,并没有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变,而深复制则是开辟新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。
当然还有一种获取已排序的列表副本的方法是用sorted,使用如下:

>>> x=[5,2,9,7]
>>> y=sorted(x)
>>> y
[2, 5, 7, 9]
>>> x
[5, 2, 9, 7]

好了,我们看看sort的函数原型,它有三个参数,

sort(...)
    L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;
    cmp(x, y) -> -1, 0, 1

sort函数可以接收的参数有:一种用于排序的比较函数 compare(内建的是 cmp ,默认的也是cmp);一种用于按某种以关键字参数传递的key参数,还有一种关键字参数是reverse

>>> numbers=[5,2,9,7]
>>> numbers.sort(cmp)
>>> numbers
[2, 5, 7, 9]


>>> numbers=['a5','b2aaaa1','9ccc','dd7']
>>> numbers.sort(key=len)
>>> numbers
['a5', 'dd7', '9ccc', 'b2aaaa1']


>>> numbers=['a5','b2aaaa1','9ccc','dd7']
>>> numbers.sort(key=len,reverse=True)
>>> numbers
['b2aaaa1', '9ccc', 'dd7', 'a5']

参数key和cmp有点类似,必须提供一个在排序过程中使用的函数。然而该函数并不像cmp那样直接确定对象的大小,而是为每个元素创建一个以赋值给key的函数的返回值的键,然后所有的元素按照键来排序。如上所示,就是根据元素的长度来进行排序的。
最后,cmp, key, reverse参数也都可用于sorted的函数。


只适合元组(tuple)的操作

元组与列表一样,也是一种序列,唯一不同的是元组不能修改,其实字符串也是不能修改的。创建元组很简单,只要使用逗号隔开,自动就创建成了元组。

>>> 1,2,3
(1, 2, 3)
>>> 3,
(3,)

元组(tuple)函数

元组(tuple)和列表(list)一样,其实并不是函数,而是一种类型。它以一个序列作为参数,并转化成为元组,如果本身就是元组,那么直接返回。

>>> tuple('Python')
('P', 'y', 't', 'h', 'o', 'n')
>>> tuple(('P', 'y', 't', 'h', 'o', 'n'))
('P', 'y', 't', 'h', 'o', 'n')
>>> tuple([1,2,3])
(1, 2, 3)

元组(tuple) 的基本操作

元组只读的,一般除了访问元数和分片外,没有什么操作。分片后当然还是元组。

>>> x=1,2,3
>>> x[1]
2
>>> x[:]
(1, 2, 3)

元组(tuple) 的作用是什么?

一般元组能做到的,列表也能做到,但下面两个重要原因,是列表不能替代的:

  • 元组可以在映射或集合中充当键的作用,列表则不行。
  • 元组作为很多内建函数和方法的返回值存在。
posted @ 2017-08-31 20:08  chenqiangzhishen  阅读(517)  评论(0编辑  收藏  举报