0. 什么是列表? 什么是元组?
在python中, 列表以关键字list表示, 元组则以关键字tuple表示
先说列表, 列表相当于其他编程语言中的数组, 但也不完全等价. 同类数据元素的集合我们称之为数组, 在python的列表中不仅可以存放同类型的数据元素, 还可以存放不同类型的数据元素
再说元组, 元组跟列表的功能类似, 但也有明显的区别:
元组不可修改, 列表可以修改 (即元组在定义完后就不可以再对其修改了)
元组可以作为字典的key, 列表不可以 (即元组是可hash的), 至于什么是字典, 下一篇马上介绍
列表、元组、以及上一篇介绍的字符串都属于序列, 即列表、元组也支持切片操作, 并且切片操作在列表、元组中使用的非常频繁
1. 列表、元组的定义方式
1.1 列表的定义
在python中, 定义列表可以使用以下几种方式:
以左方括号[开头、右方括号]结尾, 中间的就是列表的内容, 列表中的每一项称之为元素, 元素之间使用逗号,分隔
# -*- coding: utf-8 -*-
#以左方括号[开头、右方括号]结尾, 中间的就是列表的内容, 列表中的每一项称之为元素, 元素之间使用逗号,分隔
l1 = [1, 2, 3]
print l1
#输出: [1, 2, 3]
print type(l1)
#输出:
#列表中可以包含元素, 也可以不包含任何元素, 不包含任何元素的列表称为空列表
l2 = []
print l2
#输出: []
print type(l2)
#输出:
#只包含一个元素的列表逗号可加可不加
l3 = [1]
print l3
#输出: [1]
print type(l3)
#输出:
l4 = [1,]
print l4
#输出: [1]
print type(l4)
#输出:
#列表的元素也可以是不同类型的
l5 = [1, 0.2, 'Hello', 3L]
print l5
#输出: [1, 0.2, 'Hello', 3L]
print type(l5)
#输出:
#列表的元素也可以是一个列表
l6 = [1, 2, l5, 10]
print l6
#输出: [1, 2, [1, 0.2, 'Hello', 3L], 10]
print type(l6)
#输出:
将其他可迭代的数据类型通过list的构造方法生成一个新的列表对象
什么是可迭代的数据类型? 简单的说就是可以使用for语句的数据类型, 先不用关心. 这里我们再学习一个python的内置方法isinstance, 该方法可以判断一个对象是否是一个已知的类型. 即可以用这个方法来判断一个对象是否是可迭代的数据类型
# -*- coding: utf-8 -*-
#判断一个对象是否可迭代需要先导入collections模块中的Iterable类
from collections import Iterable
#首先来判断一下之前学习过的数据类型中, 哪些是可迭代的数据类型
#int
n = 1
print isinstance(n, Iterable)
#输出: False
#float
f = 0.2
print isinstance(f, Iterable)
#输出: False
#long
nl = 2L
print isinstance(nl, Iterable)
#输出: False
#complex
x = 123-12j
print isinstance(x, Iterable)
#输出: False
#bool
i = True
print isinstance(i, Iterable)
#输出: False
#str
s = 'Hello World'
print isinstance(s, Iterable)
#输出: True
#可以看到, 在我们之前学习过的数据类型中只有字符串是可迭代的
# -*- coding: utf-8 -*-
#判断一个对象是否可迭代需要先导入collections模块中的Iterable类
from collections import Iterable
s = 'Hello World'
print isinstance(s, Iterable)
#输出: True
#使用字符串构造一个列表
l1 = list(s)
print l1
#输出: ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd']
print type(l1)
#输出:
l2 = [1, 2, 3]
print id(l2)
#输出: 37599176
print isinstance(l2, Iterable)
#输出: True
#使用列表构造一个列表
l3 = list(l2)
print l3
#输出: [1, 2, 3]
print id(l3)
#输出: 46114568
print type(l1)
#输出:
l4 = [6, 66, 666]
print id(l4)
#输出: 44370952
l5 = l4
print id(l5)
#输出: 44370952
'''
通过l4, l5与l2,l3的比较, 我们可以发现, 当使用列表构造一个列表时, 确实是重新生成了一个新的列表, 因为它们的内存地址是不同的
而直接使用等号=将一个列表赋值给另一个列表时, 它们的内存地址是相同的, 这就说明并未生成新的列表, 而是将该变量指向了原先的列表在内存中的地址
'''
#后面即将要学习的元组类型
t = (1, 2, 3)
print isinstance(t, Iterable)
#输出: True
l6 = list(t)
print l6
#输出: [1, 2, 3]
print type(l6)
#输出:
#后面即将要学习的字典类型
d = {'a': 1, 'b': 2}
print isinstance(d, Iterable)
#输出: True
l7 = list(d)
print l7
#输出: ['a', 'b']
print type(l7)
#输出:
'''
通过上面的这些可迭代数据类型构造列表我们不难发现它在内部是如何做的
相当于:
l = []
for i in Iterable:
l.append(i)
'''
l8 = []
for k in d:
l8.append(k)
print l8
#输出: ['a', 'b']
print type(l8)
#输出:
1.2 元组的定义
元组跟列表的定义方式也是类似的, 在python中, 定义元组可以使用以下几种方式:
以左圆括号(开头、右圆括号)结尾, 中间的就是元组的内容, 元组中的每一项称之为元素, 元素之间使用逗号,分隔, 需要特别注意的是, 当元组中只有一个元素时, 逗号是不可以省略的
# -*- coding: utf-8 -*-
#以左圆括号(开头、右圆括号)结尾, 中间的就是元组的内容, 元组中的每一项称之为元素, 元素之间使用逗号,分隔
t1 = (1, 2, 3)
print t1
#输出: (1, 2, 3)
print type(t1)
#输出:
#元组中同样可以放各种数据类型
t2 = (1, 'abc', 0.2, 3L, [1, 2])
print t2
#输出: (1, 'abc', 0.2, 3L, [1, 2])
print type(t2)
#输出:
#需要特别注意的是, 当元组中只有一个元素时, 逗号是不可以省略的
t3 = (1,)
print t3
#输出: (1,)
print type(t3)
#输出:
#我们来看一下当元组中只有一个元素并且逗号被省略后的情况
x = (1)
print x
#输出: 1
print type(x)
#输出:
print repr(x)
#输出: 1
#元组的元素也可以是一个元组
t4 = (1, 2, 30)
t5 = (t4, 100)
print t5
#输出: ((1, 2, 30), 100)
print type(t5)
#输出:
将其他可迭代的数据类型通过tuple的构造方法生成一个新的元组对象
# -*- coding: utf-8 -*-
#将其他可迭代的数据类型通过tuple的构造方法生成一个新的元组对象
s = 'Hello World'
t1 = tuple(s)
print t1
#输出: ('H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd')
print type(t1)
#输出:
l1 = [1, 2, 3]
t2 = tuple(l1)
print t2
#输出: (1, 2, 3)
print type(t2)
#输出:
print id(t2)
#输出: 44294432
t3 = tuple(t2)
print t3
#输出: (1, 2, 3)
print id(t3)
#输出: 44294432
print type(t3)
#输出:
'''
与列表不同的是, 当将一个元组作为tuple的构造参数时, 构造出来的新tuple的内存地址与传入的元组内存地址是一样的
在上面列表的测试中, 两者地址是不同的, 这可能是因为元组不可被修改的原因, python作者所做的优化
'''
d = {'a': 1, 'b': 2}
t4 = tuple(d)
print t4
#输出: ('a', 'b')
print type(t4)
#输出:
2. 列表、元组的基本操作
2.1 下标 (索引)
下标 (索引)用于表示某个元素在序列中的位置, 基本上所有的编程语言中, 下标 (索引)都是从0开始的, 由于序列属于线性结构, 即每个元素在逻辑上是相连的, 并且是有序的, 所有可以很方便的使用下标来索引到需要的元素
# -*- coding: utf-8 -*-
'''
所有序列的位置(下标)都是从0开始的
[1, 2, 3, 4]
下标: 0 1 2 3
'''
s = 'Hello World'
#输出字符串中下标为0的元素
print s[0]
#输出: H
l1 = [1, 2, 3]
#输出列表中下标为2的元素
print l1[2]
#输出: 3
t1 = (1,)
#需要注意的是, 在使用下标索引元素时, 所指定的下标必须是在合法范围内的
#如果下标超出了范围就会抛出异常, 像下面这样
#IndexError: tuple index out of range
#print t1[1]
#print t1[-3]
2.2 切片
在介绍字符串类型时已经说过了, 只要是序列都支持切片操作, 而在python中, 字符串、列表、元组都属于序列
# -*- coding: utf-8 -*-
l1 = [1, 2, 3, 4]
t1 = (1, 2, 3, 4)
#从头切到尾, 可以看到返回的新列表内存地址并不相同
l2 = l1[:]
print id(l1)
#输出: 47132584
print id(l2)
#输出: 47132544
print type(l2)
#输出:
#在元组中从头切到尾发现返回的新元组的内存地址与原先元组的内存地址相同
#也就是说仅仅是将变量t2指向了t1指向的内存地址
t2 = t1[:]
print id(t1)
#输出: 41300304
print id(t2)
#输出: 41300304
print type(t2)
#输出:
#还有一些其他的切片操作在上一篇介绍字符串类型时已经介绍过了
2.3 元组的基本操作
先来看一下tuple的基本操作, 由于tuple是不可修改的, 所以用于操作tuple的方法很少
print dir(tuple)
'''
输出:
['__add__',
'__class__',
'__contains__',
'__delattr__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__getnewargs__',
'__getslice__',
'__gt__',
'__hash__',
'__init__',
'__iter__',
'__le__',
'__len__',
'__lt__',
'__mul__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__rmul__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'count',
'index']
'''
可以看到除了以__开头和__结尾的方法外, 就只剩下了count和index这两个方法了
方法(函数)
功能
count
统计某个值在元组中出现的次数
index
从元组中找出某个值第一个匹配项的索引位置
2.3.1 count
定义(省略了self): int count(value)
功能: 统计某个值在元组中出现的次数
参数:
(obj) value: 需要统计的值, 可以是任意类型的值
# -*- coding: utf-8 -*-
t1 = (1, 2, 3, 2, 4, 2)
#统计2在元组中的个数
print t1.count(2)
#输出: 3
#统计1在元组中的个数
print t1.count(1)
#输出: 1
#统计100在元组中的个数
print t1.count(100)
#输出: 0
t2 = ((1,), [1, 2, 3], 1, 2, 3, 'Hello World')
print t2[1]
#输出: [1, 2, 3]
print id(t2[1])
#输出: 42768392
find_list = [1, 2, 3]
print id(find_list)
#输出: 42798088
print t2.count([1, 2, 3])
#输出: 1, 可以看到, count只是在按值统计, 它并不会管内存地址是否一致, 只要值一致就可以
2.3.2 index
定义(省略了self): int index(value, start=None, stop=None)
功能: 从元组中找出某个值第一个匹配项的索引位置
参数:
(obj) value: 需要查找的值, 可以是任意类型的值
(int) start: 可以指定范围, start作为范围的起始, 默认为None
(int) stop: 可以指定范围, stop作为范围的起始, 默认为None
# -*- coding: utf-8 -*-
t1 = (1, 2, 3, 2, 4, 2)
#查找2的位置(下标)
print t1.index(2)
#输出: 1, 可以看到存在多个时, 只返回找到的第一个值的下标
#查找2的位置(下标)
print t1.index(4)
#输出: 4
2.4 列表的基本操作
再看一下列表中都有哪些操作方法
print dir(list)
'''
输出:
['__add__',
'__class__',
'__contains__',
'__delattr__',
'__delitem__',
'__delslice__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__getslice__',
'__gt__',
'__hash__',
'__iadd__',
'__imul__',
'__init__',
'__iter__',
'__le__',
'__len__',
'__lt__',
'__mul__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__reversed__',
'__rmul__',
'__setattr__',
'__setitem__',
'__setslice__',
'__sizeof__',
'__str__',
'__subclasshook__',
'append',
'count',
'extend',
'index',
'insert',
'pop',
'remove',
'reverse',
'sort']
'''
列表中的操作方法虽然不多, 但这些方法在平时开发中会经常用到
方法(函数)
功能
append
在列表末尾添加新的值
count
统计某个值在列表中出现的次数
extend
在列表末尾一次性追加另一个序列中的多个值
index
从列表中找出某个值第一个匹配项的索引位置
insert
将新的值插入到列表中指定的位置
pop
移除列表中指定下标的元素, 并返回该元素的值
remove
移除列表中某个值的第一个匹配项
reverse
反转列表中元素
sort
对列表进行排序
2.4.1 append
定义(省略了self): void append(object)
功能: 在列表末尾添加新的值
参数:
(obj) object: 需要添加的新值, 可以是任意类型的值
# -*- coding: utf-8 -*-
l1 = [1, 2]
#末尾追加个1
l1.append(1)
print l1
#输出: [1, 2, 1]
print id(l1)
#输出: 39310840
s = 'Hello World'
#继续末尾追加个字符串
l1.append(s)
print l1
#输出: [1, 2, 1, 'Hello World']
print id(l1)
#输出: 39310840
l2 = [100, 101, 102]
l1.append(l2)
print l1
#输出: [1, 2, 1, 'Hello World', [100, 101, 102]]
print id(l1)
#输出: 39310840
''''
可以看到, 当追加的数据类型也是序列时, append方法并不会将两个序列的内容做合并, 而是将该序列看作是一个元素追加到末尾的
如果想完成列表与其他序列的内容合并, 可以使用下面即将介绍的extend方法
'''
2.4.2 count
定义(省略了self): int count(value)
功能: 统计某个值在列表中出现的次数
参数:
(obj) value: 需要统计的值, 可以是任意类型的值
该方法与元组中的count方法用法一模一样
2.4.3 extend
定义(省略了self): void extend(iterable)
功能: 在列表末尾一次性追加另一个序列中的多个值
参数:
(obj) iterable: 该参数必须是一个可迭代的类型, 否则将抛出异常
# -*- coding: utf-8 -*-
'''
extend与append方法类似, 都向原列表默认追加内容
不同的是, append可以追加任意类型的内容,当追加的类型为可迭代类型时, 会将该类型当作一个整体追加到末尾
而extend只能追加可迭代数据类型, 如果追加的参数非可迭代数据类型将抛出异常
通常使用extend方法来合并多个列表的数据, 像下面这样
'''
l1 = [1, 2]
l2 = [10, 11]
l1.extend(l2)
print l1
#输出: [1, 2, 10, 11]
'''
可以发现, 输出的结果不再是将l2列表作为一个整体追加到末尾了, 而是将该列表中的元素依次追加到末尾
其内部实现应该像这样:
for i in l2:
l1.append(i)
'''
2.4.4 index
定义(省略了self): int index(value, start=None, stop=None)
功能: 从列表中找出某个值第一个匹配项的索引位置
参数:
(obj) value: 需要查找的值, 可以是任意类型的值
(int) start: 可以指定范围, start作为范围的起始, 默认为None
(int) stop: 可以指定范围, stop作为范围的起始, 默认为None
该方法与元组中的index方法用法一模一样
2.4.5 insert
定义(省略了self): void insert(index, p_object)
功能: 将新的值插入到列表中指定的位置
参数:
(int) index: 指定插入到列表中的位置(下标)
(obj) p_object: 需要插入的新值, 可以是任意类型的值
# -*- coding: utf-8 -*-
l1 = [1, 2, 3]
#在开头插入100
l1.insert(0, 100)
print l1
#输出: [100, 1, 2, 3]
#在第二个元素前插入字符串, 默认下标从0开始
l1.insert(1, 'Hello World')
print l1
#输出: [100, 'Hello World', 1, 2, 3]
#我们来看下, 如果给的下标位置超出了范围会怎么样
l1.insert(200, (1,))
print l1
#输出: [100, 'Hello World', 1, 2, 3, (1,)]
#可以看到, 并不会报错, 如果索引是正无穷方向, 将会追加到末尾去
#再看一下索引是负数的情况
l1.insert(-100, 'xxxx')
print l1
#输出: ['xxxx', 100, 'Hello World', 1, 2, 3, (1,)]
#同理, 如果索引是负无穷方向, 将会追加到开头
2.4.6 pop
定义(省略了self): obj pop(index=None)
功能: 移除列表中指定下标的元素, 并返回该元素的值
参数:
(int) index: 需要移除的元素下标, 默认为None, 移除最后一个元素
# -*- coding: utf-8 -*-
l1 = [1, 2, 3, 4]
#默认移除最后一个元素
value = l1.pop()
print value
#输出: 4
print l1
#输出: [1, 2, 3]
#移除最头部的元素
value = l1.pop(0)
print value
#输出: 1
print l1
#输出: [2, 3]
l2 = [1, 2, (1, 2)]
value = l2.pop()
print value
#输出: (1, 2)
print l2
#输出: [1, 2]
l3 = [1, 2, 3]
#当pop的下标(索引)不存在时, 将抛出异常
#IndexError: pop index out of range
#value = l3.pop(100)
#value = l3.pop(-10)
'''
使用列表可以快速实现数据结构中的栈和队列
用到的就是列表中的insert和pop这两个方法
'''
2.4.7 remove
定义(省略了self): void remove(value)
功能: 移除列表中某个值的第一个匹配项
参数:
(obj) value: 需要从列表中移除的值, 可以是任意类型的值
# -*- coding: utf-8 -*-
l1 = [1, 2, 3, 4, 2, 4, 2, 'Hello', (1, 2)]
#移除值为3的元素
l1.remove(3)
print l1
#输出: [1, 2, 4, 2, 4, 2, 'Hello', (1, 2)]
#移除值为2的元素(可以看到有多个为2的元素)
l1.remove(2)
print l1
#输出: [1, 4, 2, 4, 2, 'Hello', (1, 2)]
#可以看到, 当移除的值有个多匹配项时, 只会移除第一个匹配项
l1.remove((1, 2))
print l1
#输出: [1, 4, 2, 4, 2, 'Hello']
#当移除的值不存在时也将抛出异常
#ValueError: list.remove(x): x not in list
#l1.remove(1000)
2.4.7 reverse
定义(省略了self): void reverse()
功能: 反转列表中元素
参数: 无
l1 = [1, 2, 3, 4, 2, 4, 2, 'Hello', (1, 2)]
print l1
#输出: [1, 2, 3, 4, 2, 4, 2, 'Hello', (1, 2)]
print id(l1)
#输出: 46538664
#反转列表
l1.reverse()
print l1
#输出: [(1, 2), 'Hello', 2, 4, 2, 4, 3, 2, 1]
print id(l1)
#输出: 46538664
需要注意的时, 列表的反转操作是直接在当前列表中操作的, 而不是返回反转后的列表
2.4.7 sort
定义(省略了self): void sort(cmp=None, key=None, reverse=False)
功能: 对列表进行排序
参数:
(function) cmp: 该参数接收一个函数作为比较函数(该函数接收2个参数), 用于定义列表的元素怎样比较, 对于整数这种内置类型的比较方法很直观, 但是对于自定义类型的比较, 就要自己定义比较函数了. 函数返回 0 就是两个数相等, 返回负数就是第一个参数小于第二个参数, 返回正数就是第一个参数大于第二个参数
(function) key: 该参数指定的函数(该函数接收一个参数)将作用于列表中的每一个元素上并根据key函数返回的结果进行排序
(bool) reverse: 按升序还是降序进行排序, 默认为False, 为升序排序(从小到大), True为降序排序(从大到小)
# -*- coding: utf-8 -*-
l1 = [1, 2, 3, 100, 4, 5, 8, 77, 77]
print l1
#输出: [1, 2, 3, 100, 4, 5, 8, 77, 77]
#使用默认, 升序排序
l1.sort()
print l1
#输出: [1, 2, 3, 4, 5, 8, 77, 77, 100]
#使用降序排序
l1.sort(reverse=True)
print l1
#输出: [100, 77, 77, 8, 5, 4, 3, 2, 1]
#除了使用列表的内置sort方法可以排序, 也可以使用python中的内置方法sorted方法进行排序
#该方法的定义与list中sort方法的定义一样, 但该方法不会再原始的列表中进行排序, 而是返回一个新的排好序的列表
#def sorted(iterable, cmp=None, key=None, reverse=False)
l2 = sorted(l1)
print l1
#输出: [100, 77, 77, 8, 5, 4, 3, 2, 1]
print l2
#输出: [1, 2, 3, 4, 5, 8, 77, 77, 100]
#来试一下key参数
l3 = [-2, -7, 0, 2, -22, -18, 100]
#按绝对值大小排序
#该参数指定的函数将作用于列表中的每一个元素上并根据key函数返回的结果进行排序
l3.sort(key=abs)
print l3
#输出: [0, -2, 2, -7, -18, -22, 100]