Python数据类型(三)数据结构类型—list、tuple、range、set、frozenset、dict

数据结构类型是指用来存储数据、文字、字符、二进制、类对象,进一步方便操作查找存储内容的结构。数据类型分为了序列类型集合类型映射类型

序列类型是Python数据类型的内置基本数据类型,有三种基本序列类型:listtuplerange。同样,序列类型分为通用序列类型、可变类型和不可变类型。

集合类型是指由具有唯一性的hashable对象锁组成的无序多项集。无序多项集与序列类型不同,作为无序的集合并不支持记录元素的位置和插入顺序,相应地集合也不支持索引、切片或其他序列类的操作。目前有两种集合类型:setfrozenset

映射类型会将hashable值映射到任意对象,映射属于可变对象,目前只有一种标准映射类型对象:dic

序列类型-list,tupe,range

序列类型有通用序列,可变序列和不可变序列。序列支持一系列的操作。

通用序列操作支持

通用序列支持的操作可变序列和不可变序列都支持,下面是通用序列的支持操作:

备注:表格中s和t是具有相同类型的序列,n, i, jk 是整数,x 是任何满足 s 所规定的类型和值限制的任意对象,序列的操作同样具有优先级顺序,表格是按照优先级降序排列(not in 和 in 优先级相同)

运算 释义
s.count(x) x在s中出现的总次数
s.index(x,i,j) x在s中首次出现项的索引号(索引号在i或其后且在j之前)
max(s) s的最大项
min(s) s的最小项
len(s) s的长度
s[i:j:k] s从i到j步长为k的切片
s[i:j] s从i到j的切片
s[i] s的第i项
sn 或n\s 相当于s与自身进行n次拼接
s+t s与t两个序列进行拼接
x not in s 如果x不存在于s项中,则返回True,如果存在于s中,则返回False
x in s 如果x存在于s中,则返回True,如果不存在于s中,则返回True

想同类型的序列支持比较,tuple和list的比较是通过比较对应元素的顺序,若比较结果相同,则每个元素比较结构都必须相等,并且两个序列长度必须相同。

可变序列操作支持

通用序列的操作在可变序列中适用,但可变序列也有部分自己的支持操作,可变序列的操作如下表所示:

备注:s是可变序列类型的实例,t是任意可迭代对象, x 是符合对 s 所规定类型与值限制的任何对象

操作 释义
s[i] =x 将s的第i项替换为x
s[i:j] = t 将s从i到j的切片替换为可迭代对象t的内容
del s[i:j] 删除s从i到j的切片 等同于s[i:j] = []
s[i:j:k] =t 将s从i到j步长为k的切片替换为可迭代对象t的内容
del s[i:j:k] 删除s从i到j步长为k的切片 等于s[i:j:k] = []
s.append(x) 将x添加到序列的末尾
s.clear() 将s中的所有项移除 等同于del s[:]
s.copy() 创建s的浅拷贝
s.extend(t)或 s+=t t 的内容扩展 s 两个序列的合并
s*=n shiyongs的内容重复n次来对其进行更新
s.insert(i,x) 在由 i 给出的索引位置将 x 插入 s (等同于 s[i:i] = [x])
s.pop(i) 提取在i位置上的序列项,并从s中移除该项
s.remove(x) 若x在s中存在,则将s[i] (s[i]==x)的第一个元素移除
s.reverse() 将列表中的元素逆序

list - 列表

​ 列表是可变序列,通常用于存放同类项目的集合(其中精确的相似程度将根据应用而变化)。

list构建 以及list方法

列表的构建有多种方式

  • 使用一对方括号来表示空列表: []

  • 使用方括号构建,列表中的项以逗号分割:[a,b,c]

  • 使用列表推导式: [x for x in iterable]

  • 使用类型构造器: list() 或list(iterable)。构造器将构造一个列表,其中的项与 iterable 中的项具有相同的的值与顺序。 iterable 可以是序列、支持迭代的容器或其它可迭代对象。 如果 iterable 已经是一个列表,将创建并返回其副本,类似于 iterable[:]。 例如,list('abc') 返回 ['a', 'b', 'c']list( (1, 2, 3) ) 返回 [1, 2, 3]。 如果没有给出参数,构造器将创建一个空列表 []

  • 其它操作,例如sorted()内置函数,分类后会产生一个列表。

    列表作为序列类型,实现了所有通用序列和可变序列的操作,如下:

    | 列表实现的序列操作 | 释 |
    | —————————– | ———————————————————— |
    | list.append(x) | 在列表的末尾添加一个元素。 |
    | list.extend(iterable) | 使用可迭代对象中的所有元素来扩展列表 |
    | list.insert(i,x) | 在给定的位置插入一个元素。第一个参数是要插入的元素的索引,所以 a.insert(0, x) 插入列表头部, a.insert(len(a), x) 等同于 a.append(x) |
    | list.remove(x) | 若x在列表中存在,则将列表中的项的值为x的第一个元素移除 |
    | list.pop(i) | 删除列表中给定位置的元素并返回它。如果没有给定位置,a.pop() 将会删除并返回列表中的最后一个元素。 |
    | list.clear() | 删除列表中所有的元素 |
    | list.index(x, start, end) | 返回x出现的第一个索引位置,索引位置在start和end之间 |
    | list.count(x) | 返回元素 x 在列表中出现的次数 |
    | list.reverse() | 反转列表中的元素 |
    | list.copy() | 返回列表的一个浅拷贝。相当于 a[:] |

    除了以上列表实现的序列操作之外,列表还增加了一个方法:

    list.sort(*, key=None, reverse=False)

    此方法会对列表进行原地排序,只使用 < 来进行各项间比较。 异常不会被屏蔽 —— 如果有任何比较操作失败,整个排序操作将失败(而列表可能会处于被部分修改的状态)。此外,sort()排序是稳定排序。

    sort()接受两个仅限以关键字形式传入的参数:key 指定带有一个参数的函数,用于从每个列表元素中提取比较键 (例如 key=str.lower)。 对应于列表中每一项的键会被计算一次,然后在整个排序过程中使用。 默认值 None 表示直接对列表项排序而不计算一个单独的键值。

    reverse 为一个布尔值。 如果设为 True,则每个列表元素将按反向顺序比较进行排序。

list方法代码示例:

>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
>>> fruits.count('apple')
2
>>> fruits.index('banana')
3
>>> fruits.index('banana', 4)
6
>>>#像 insert ,remove 或者 sort 方法,只修改列表,没有打印出返回值——它们返回默认值 None ,这是Python中所有可变数据结构的设计原则。
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
>>> fruits.append('grape')
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
>>> fruits.sort()
>>> fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
>>> fruits.pop()
'pear'
>>> 

列表作为栈和队列使用

列表的append和pop方法使得列表作为堆栈非常容易,最后一个插入,最先取出,这样完全符合栈的先进后出原则;要添加一个元素到堆栈的顶端,使用 append() 。要从堆栈顶部取出一个元素,使用 pop() ,不用指定索引。

>>> stack = [1,2,3,4]
>>> stack.append(5)
>>> stack.append(6)
>>> stack
[1, 2, 3, 4, 5, 6]
>>> stack.pop()
6
>>> stack
[1, 2, 3, 4, 5]
>>> stack.pop()
5
>>> stack
[1, 2, 3, 4]
>>> 

列表除了上述可以作为队列使用之外,还可以用作队列使用,其中先添加的元素被最先取出就完全符合队列的先进先出原则;然而列表用作这个目的相当低效,因为在列表的末尾添加和弹出元素非常快,但是在列表的开头插入或弹出元素却很慢 (因为所有的其他元素都必须移动一位),但若要实现一个队列,collections类中的deque可以快速的从两段操作。

列表推导式

列表推导式提供了一个更简单的创建列表的方法。常见的用法是把某种操作应用于序列或可迭代对象的每个元素上,然后使用其结果来创建列表,或者通过满足某些特定条件元素来创建子序列。

创建方式有–

普通方法:

>>> squares = []
>>> for x in range(10):
    squares.append(x**2)


>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

上面的普通方法创建方式名为x的变量在for循环后仍然存在,可以利用lambda表达式解决该问题。

lambda表达式方法:

>>> squares = list(map(lambda x:x **2, range(10)))
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

与lambda等效的还有一种更简化的方式。

>>> squares = [x**2 for x in range(10)]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

括号,其中包含一个表达式,后跟一个for子句,然后是零个或多个for或if子句的表达式形式也能创建列表

>>> [(x,y) for x in [1,2,3] for y in [3,1,4] if x!= y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

其等价于

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

列表推导式可以使用复杂的表达式和嵌套函数

>>> from math import pi
>>> [str(round(pi, i)) for i in range(1,6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']

嵌套列表推导式

列表推导式中的初始表达式可以是任何表达式,包括另一个列表推导式。

如下是3X4矩阵

>>> matrix = [[1,2,3,4],    
              [5,6,7,8],    
              [9,10,11,12],]

要想交换其行和列,写法如下:

>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

实际应用中,使用内置函数组成复杂的流程语句,zip()函数能高效的处理此种情况

>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

tuple ——元组

元组是不可变序列,通常用于存储异构数据的多项集(例如由enumerate()内置函数所产生的二元组),元组也被用于需要同构数据的不可变序列的情况(例如允许存储到setdict的实例)。

tuple元组构建

元组的构建有多种方式:

  • 使用一对圆括号来表示空元组: ()
  • 使用一个后缀逗号表示单元组: a,(a,)
  • 使用以逗号分隔的多个项:a, b, c or (a, b, c)
  • 使用内置的tuple():tuple()tuple(iterable)。tuple()构造器会构造一个元组,元组中的项与iterable中的项具有相同的值与顺序。 iterable 可以是序列、支持迭代的容器或其他可迭代对象。 如果 iterable 已经是一个元组,会不加改变地将其返回。 例如,tuple('abc') 返回 ('a', 'b', 'c')tuple( [1, 2, 3] ) 返回 (1, 2, 3)。 如果没有给出参数,构造器将创建一个空元组 ()

元组实现了通用序列的支持操作,所有通用序列支持的操作同样适合于元组,除了更改序列操作,元组作为不可变序列,是不支持修改其中元素的。

>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> u = t, (1,2,3,4,5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> v = ([1,2,3], [3,2,1])
>>> v
([1, 2, 3], [3, 2, 1])
>>> #序列解包
>>> x,y,z = t
>>> x
12345
>>> y
54321
>>> z
'hello!'
>>> 

range类型

range类型表示不可变的数字序列,通常用于在for循环中指定次数。

range的构造需要调用系统模块函数:range(stop) 或range(start, stop, step)

​ range构造器参数必须为整数,(可以是内置的 int 或任何实现了 __index__ 特殊方法的对象)。如果省略了step参数,则默认为1。 如果省略了start参数,默认值为0, 如果step为0则会引发ValueError;如果step为正值,确定range r内容的公式为r[i] = start + step*i,其中i >= 0r[i] < stop;如果step为负值,确定range内容的公式仍然是i >= 0r[i] < stop,但限制条件改为 i >= 0r[i] > stop。range 对象确实支持负索引,但是会将其解读为从正索引所确定的序列的末尾开始索引。range对象的元素绝对值大于 sys.maxsize是被允许的。

>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list (range(1,11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list (range(0, 30, 5))
[0, 5, 10, 15, 20, 25]
>>> list (range(0,10,3))
[0, 3, 6, 9]
>>> list(range(0,-10,-1))
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
>>> list(range(0))
[]
>>> list(range(1,0))
[]

range类型与listtuple相比,优势在于range对象总是占用固定数量的(较小)内存不论其所表示的范围有多大(因为它只保存了 start, stopstep 值,并会根据需要计算具体单项或子范围的值)。range提供包含检测、元素索引查找、切片等特性,并支持负索引 。

>>> r = range(0,20,2)
>>> r
range(0, 20, 2)
>>> 11 in r
False
>>> 10 in r
True
>>> r.index(10)
5
>>> r[5]
10
>>> r[:5]
range(0, 10, 2)
>>> r[-1]
18
>>> 

集合类型——set、frozenset

目前有两种集合类型:setfrozenset

set类型

​ set类型是可变的, 其内容可以使用 add()remove() 这样的方法来改变,因为是可变的,所以没有哈希值,且不能呗用作字典的键或其它集合的元素。

​ set构建除了使用set构造器,非空的set还可以通过将以逗号分隔的元素列表包含于花括号之内来创建。

构造器方法:

set(iterable) :返回一个新的set对象,其元素来自于iterable。如果未指定 iterable,则将返回一个新的空集合。

只适用于set的操作如下:

操作 释义
update(*others) set\ = other \ … 更新集合,添加来自 others 中的所有元素。
intersection_update(*others) set &= other &… 更新集合,只保留其中在所有 others 中也存在的元素。
difference_update(*others) set -= other\ … 更新集合,移除其中也存在于 others 中的元素。
symmetric_differenct_update(other) set ^= other 更新集合,只保留存在于集合的一方而非共同存在的元素。
add(elem) 将元素 elem 添加到集合中。
remove(elem) 从集合中移除元素 elem。 如果 elem 不存在于集合中则会引发 KeyError。
discard(elem) 如果元素 elem 存在于集合中则将其移除。
pop() 从集合中移除并返回任意一个元素。 如果集合为空则会引发 KeyError。
clear() 从集合中移除所有元素。

frozenset类型

frozenset类型是不可变的,并且能hashable,其内容在被创建后不能再改变,因此可以被用作字典的键值或其它集合的元素。

构造方法:

frozenset(iterable): 返回一个新的frozenset对象,其元素来自于iterable。集合的元素必须为 hashable。 要表示由集合对象构成的集合,所有的内层集合必须为 frozenset对象。 如果未指定 iterable,则将返回一个新的空集合。

set和frozenset共同支持的操作

set和frozenset的实例提供以下操作:

操作 释义
len(s) 返回集合s的长度
x in s 检测元素x是否存在于集合s中,若存在,则返回True,否则返回False
x not in s 检测元素x是否存在于集合s中,若存在,则返回False,否则返回True
isdisjoint(other) 如果集合中没有与other共有的元素则返回True,当且仅当两个集合的交集为空集合时,两者为不相交集合
issubset(other) set <= other: 检测是否结合中的每个元素都在other之中;set < other 检测结合是否为other的真子集
issuperset(other) set >= other:检测是否other中的每个元素都在集合之中;set > other:检测集合是否为other的真超集
union(*others) __set \ other \ … : 返回一个新集合,其中包含来自原集合以及 others 指定的所有集合中的元素。
intersection(*others) set & other&… 返回一个新集合,其中包含原集合以及 others 指定的所有集合中共有的元素。
difference(*others) set-other-… 返回一个新集合,其中包含原集合中在 others 指定的其他集合中不存在的元素
symmetric_difference(other) set ^ other 返回一个新集合,其中元素属于原集合或属于other指定的其他集合,但不能同时属于两者。
copy() 返回集合的浅拷贝

set 和 frozenset示例:

>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print (basket)
{'pear', 'banana', 'apple', 'orange'}
>>> 'orange' in basket
True
>>> 'crabgrass' in basket
False
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a
{'r', 'c', 'a', 'd', 'b'}
>>> a - b
{'r', 'b', 'd'}
>>> a | b
{'r', 'c', 'a', 'd', 'b', 'z', 'l', 'm'}
>>> a & b
{'c', 'a'}
>>> a ^ b
{'r', 'd', 'b', 'z', 'l', 'm'}
>>> #集合同样支持推导形式
>>> a = [x for x in 'abracadabra' if x not in 'abc']
>>> a
['r', 'd', 'r']
>>> 

setfrozenset总结:

(1)setfrozenset均支持集合与集合的比较。两个集合当且仅当每个集合中的每个元素均包含于另一个集合之内(即各为对方的子集)时则相等。 一个集合当且仅当其为另一个集合的真子集(即为后者的子集但两者不相等)时则小于另一个集合。 一个集合当且仅当其为另一个集合的真超集(即为后者的超集但两者不相等)时则大于另一个集合。

(2)由于集合仅定义了部分排序(子集关系),因此由集合构成的列表 list.sort()方法的输出并无定义。

(3) 集合的元素,与字典的键类似,必须为hashable

(4) 混合了set实例与frozenset的 二进制位运算将返回与第一个操作数相同的类型。例如: frozenset('ab') |set('bc') 将返回frozenset的实例。

(5) 要创建一个空集合你只能用 set() 而不能用 {},因为后者是创建一个空字典

映射类型 ——dict

dict字典是目前唯一的映射类型,字典的键值可以是任何值,除了非 hashable的值(包含列表、集合、字典或其他可变类型的值)。数字类型用作键时遵循数字比较的一般规则:如果两个数值相等 (例如 11.0) 则两者可以被用来索引同一字典条目。 (但是,由于计算机对于浮点数存储的只是近似值,因此将其用作字典键是不明智的,这点在讲数字类型的时候提及过)

​ 字典可以通过将以逗号分隔的 键: 值 对列表包含于花括号之内来创建,例如: {'jack': 4098, 'sjoerd': 4127}{4098: 'jack', 4127: 'sjoerd'},也可以通过 dict构造器来创建。

dict构建

dict构造器

dict(**kwarg)

dict(mapping, **kwarg)

dict(iterable, **kwarg)

以上构造器方法都返回一个新的字典,基于可选的位置参数和可能为空的关键字参数集来初始化。如果没有给出位置参数,将创建一个空字典。 如果给出一个位置参数并且其属于映射对象,将创建一个具有与映射对象相同键值对的字典。 否则的话,位置参数必须为一个iterable对象象。 该可迭代对象中的每一项本身必须为一个刚好包含两个元素的可迭代对象。 每一项中的第一个对象将成为新字典的一个键,第二个对象将成为其对应的值。 如果一个键出现一次以上,该键的最后一个值将成为其在新字典中对应的值。如果给出了关键字参数,则关键字参数及其值会被加入到基于位置参数创建的字典。 如果要加入的键已存在,来自关键字参数的值将替代来自位置参数的值。

>>> a = dict(one=1, two=2, three=3)
>>> b = {'one':1, 'two':2, 'three':3}
>>> c = dict(zip(['one', 'two', 'three'], [1,2,3]))
>>> d = dict([('two', 2), ('one',1), ('three', 3)])
>>> e = dict({'three':3, 'one':1, 'two':2})
>>> a ==b == c == d ==e
True

dict支持的操作

dict支持的操作如下:

操作 释义
len(d) 返回字典d中的项数
d[key] 返回d中以key为键的项,如果映射中不存在key则会引发keyError
d[key]=value
del d[key]
key in d
key not in d
iter(d)
clear() 移除原字典的浅拷贝
copy() 返回原字典的浅拷贝。
get(key, default) 如果 key 存在于字典中则返回 key 的值,否则返回 default。 如果 default 未给出则默认为 None,因而此方法绝不会引发 KeyError。
items() 返回由字典项 ((键, 值) 对) 组成的一个新视图
keys() 返回由字典键组成的一个新视图。
pop(key, default) 如果 key 存在于字典中则将其移除并返回其值,否则返回 default。 如果 default 未给出且 key 不存在于字典中,则会引发 KeyError。
popitem() 从字典中随机移除一项(key, value)并返回该项的值,如果字典为空,则会引发valueError
setdefault(key, default) 如果字典存在键 key ,返回它的值。如果不存在,插入值为 default 的键 key ,并返回 default 。 default 默认为 None。
update(other) 使用来自 other 的键/值对更新字典,覆盖原有的键。 返回 Noneupdate() 接受另一个字典对象,或者一个包含键/值对(以长度为二的元组或其他可迭代对象表示)的可迭代对象。 如果给出了关键字参数,则会以其所指定的键/值对更新字典: d.update(red=1, blue=2)
values() 返回由字典值组成的一个新视图
fromkeys(seq, value) 此为类方法 属于类方法,会返回一个新字典。 value 默认为 None。会利用seq创建keys,value创建values,返回创建好的新字典

dict代码示例

>>> tel = {'jack':4098, 'space':4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'space': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> del tel['space']
>>> tel['irv']=4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel.keys())
['jack', 'guido', 'irv']
>>> sorted(tel.keys())
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False
>>> # 字典同样支持推导式
>>> {x: x**2 for x in (2,4,6)}
{2: 4, 4: 16, 6: 36}

数据结构类型循环过程优化

1.对于字典,循环过程中,可以用items()方法将关键字和对应的值同时取出

>>> for k, v in tel.items():
    print(k, v)


jack 4098
guido 4127
irv 4127

2.单个序列循环时,可以用enumerate()函数将索引位置和其对应的值同时取出

>>> for i, v in enumerate(['tic', 'tac', 'toe']):
    print (i, v)


0 tic
1 tac
2 toe

3.两个或多个序列中循环时,可以用zip()函数将其内元素一一匹配

>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
    print ('what is your{0}? It is {1}.' .format(q, a))


what is yourname? It is lancelot.
what is yourquest? It is the holy grail.
what is yourfavorite color? It is blue.

4.当逆向循环一个序列时, 先正向定位序列,然后调用reversed()函数

>>> for i in reversed(range(1, 10, 2)):
    print (i)


9
7
5
3
1
>>> 

5.如果要按某个顺序循环一个序列,可以用sorted()函数,它可以在不改动原序列的基础上返回一个新的排好序的序列

>>> for f in sorted(set(basket)):
    print (f)


apple
banana
orange
pear

6.若想在循环时修改列表内容,一般改为创建一个新列表

>>> import math
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = []
>>> for value in raw_data:
    if not math.isnan(value):
        filtered_data.append(value)


>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]
>>>