Python中的容器
Python中,可包含其他对象的对象,称之为容器(Collections)。参见https://docs.python.org/zh-cn/3/reference/datamodel.html#objects-values-and-types
容器是一种数据结构——结构数据类型。
Python标准内建容器:列表(list)、元组(tuple)、字典(dict)、集合(set)。通过单独或是组合使用它们,可以高效的完成很多事情。
列表( list)
列表 ,是一种复合 数据类型,可将不同值组合在一起。用方括号标注,逗号分隔的一组值。列表 可以包含不同类型的元素,但一般情况下,各个元素的类型相同。列表中的值称为元素(element) ,有时也被称为项(item)。
一个列表中的元素不需要是相同的数据类型。下面的列表包含一个字符串、一个浮点数、一个整数和另一个列表:
['spam ', 2.0, 5, [10, 20]]
一个列表在另一个列表中,称为嵌套(nested )列表。
一个不包含元素的列表被称为空列表;可以用空的方括号[]表示。
列表(list)是以固定顺序保存对象的容器。如下图所示:
列表中的元素是有序的——可以通过索引访问其中的元素。
列表是可迭代的(iterable)对象。【如果可以使用循环访问对象中的每一个元素,那么该对象是可迭代的,被称为可迭代对象】可迭代对象中的每一个元素都有一个索引(index),即表示元素在可迭代对象中位置的数字。列表中第一个元素的索引是0,而不是 1。Python 还支持使用负索引(negative index ):可用来从右向左查找可迭代对象中元素的索引(必须是一个负数)。使用索引-1 可以查找可迭代对象中的最后一个元素。
列表是可变的(mutable)——这意味着可以修改增删其元素。
下面是示例:
可以用多种方式构建列表:
☆使用一对方括号来表示空列表:[]
☆使用方括号,其中的项以逗号分隔:[a], [a, b, c]
☆使用列表推导式:[x for x in iterable]
☆使用类型构造器(type constructor)【也称为内置函数(Built-in Functions)】:list() 或 list(iterable)
构造一个列表,其中的项与 iterable (可迭代对象,能够逐一返回其成员项的对象)中的项具有相同的的值与顺序。 iterable 可以是序列、支持迭代的容器或其它可迭代对象。 如果 iterable 已经是一个列表,将创建并返回其副本,类似于 iterable[:]。 例如,list('abc') 返回 ['a', 'b', 'c'] 而 list( (1, 2, 3) ) 返回 [1, 2, 3]。 如果没有给出参数,构造器将创建一个空列表 []。
其它许多操作也会产生列表,包括 sorted() 内置函数。
列表实现了所有 一般(common:通用) 和 可变(mutable ) 序列的操作。 列表还额外提供了以下方法:sort(*, key=None, reverse=False)
https://docs.python.org/zh-cn/3.9/library/functions.html#func-list
https://docs.python.org/zh-cn/3/library/stdtypes.html#lists
https://docs.python.org/zh-cn/3/tutorial/introduction.html#lists
https://docs.python.org/zh-cn/3/tutorial/datastructures.html#more-on-lists
in 运算符在列表中的使用,参见下图:
加号运算符+ 拼接多个列表:
乘号运算符* 以给定次数的重复一个列表:
切片(slice) 运算符的使用:
切片操作符[n:m]返回从第n个元素到第m个元素的片段,包括第一个元素,但是不包括最后一个元素。如果你省略第一个索引,切片将从列表头开始。如果你省略第二个索引,切片将会到列 尾结束。所以如果你两者都省略,切片就是整个列表的一个拷贝。
切片运算符放在赋值语句的左边时,可以一次更新多个元素:
列表的特性:
有序的集合;
通过偏移来索引,从而读取数据;
支持嵌套;
可变的类型;
查找和插入元素的时间随元素增多而变慢,时间复杂度 O (n)。
列表的常用方法有:
append() ---- 向列表尾部追加元素
insert() ---- 向指定的位置追加元素
sort()---- 排序(一般用来排序数字)
index() ----查找元素第一次在列表中出现的位置,如果没有这个元素,则抛出异常
reverse()----- 将列表元素顺序翻转
remove()----- 通过元素来移除元素,注意,如果元素不存在,则抛出异常
count() ----- 统计元素个数
clear()---- 清除元素
copy() ---- 浅拷贝对象,是在堆内存中进行对象拷贝的
extend---- 合并列表
pop()----- 删除最后元素,并返回这个元素
例、合并:
ls1=list([1,2,3,4,5,6])
ls2=list([7,8,9,0])
ls1.extend(ls2)
print(ls1)
例、排序:
ls=[6,8,5,1,4,3]
ls.sort()
print(ls)
元组(tuple)
元组是不可变(immutable:不能修改)序列,由多个用逗号隔开的值组成,输出时,元组由圆括号标注,输入时,圆括号可有可无,不过经常是必须的(如果元组是更大的表达式的一部分)。
列表和元组的主要不同在于,列表是可以修改的,而元组不可以。这意味着列表适用于需要中途添加元素的情形,而元组适用于出于某种考虑需要禁止修改序列的情形。
可以用多种方式构建元组:
☆使用一对圆括号来表示空元组:()
☆使用一个后缀的逗号来表示单元组:a, 或 (a,)
☆使用以逗号分隔的多个项:a, b, c or (a, b, c)
☆使用类型构造器(type constructor):tuple() 或 tuple(iterable)
构造一个元组,其中的项与 iterable (可迭代对象,能够逐一返回其成员项的对象)中的项具有相同的值与顺序。 iterable 可以是序列、支持迭代的容器或其他可迭代对象。 例如,tuple('abc') 返回 ('a', 'b', 'c') 而 tuple( [1, 2, 3] ) 返回 (1, 2, 3)。 如果没有给出参数,构造器将创建一个空元组 ()。参见下图:
如果 iterable 已经是一个元组,会不加改变地将其返回,如tuple(('a', 'b', 'c'))返回('a', 'b', 'c')。
特别提示,元组中只包含一个元素时,需要在元素后面添加逗号来消除歧义,如:
t = (50,)
缺少逗号是50这个数!这是因为括号()既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义,这种情况下,Python按小括号进行处理,计算结果自然是50。参见下图:
请注意,决定生成元组的其实是逗号而不是圆括号。 圆括号只是可选的,生成空元组或需要避免语法歧义的情况除外。 例如,f(a, b, c) 是在调用函数时附带三个参数,而 f((a, b, c)) 则是在调用函数时附带一个三元组。
元组由多个用逗号隔开的值组成:
输出时,元组都要由圆括号标注,这样才能正确地解释嵌套元组。输入时,圆括号可有可无,不过经常是必须的(如果元组是更大的表达式的一部分)。不允许为元组中的单个元素赋值,当然,可以创建含列表等可变对象的元组。
https://docs.python.org/zh-cn/3.9/library/functions.html#func-tuple
https://docs.python.org/zh-cn/3/library/stdtypes.html#tuples
https://docs.python.org/zh-cn/3/tutorial/datastructures.html#tuples-and-sequences
下面是元组和列表的几个区别。
1、元组是不可变的
一旦创建元组,就不能更改元组中的值。
最重要的区别:元组是不可变的,而列表是可变的。
2、元组可以作为字典的 key
不能将列表当作字典的 key,而元组可以(因为元组是不可变的)。
Python的元组与列表类似,不同之处在于元组的元素不能修改。
元组使用小括号,列表使用方括号。
元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。
元组中只包含一个元素时,需要在元素后面添加逗号
元组元素不可修改,只可合并或其他操作:
tup1 = (12, 34.56)
tup2 = ('abc', 'xyz')
#修改元组元素操作是非法的,如:
#tup1[0] = 100
#可以创建一个新的元组
tup3 = tup1 + tup2
print(tup3) #输出:(12, 34.56, 'abc', 'xyz')
请正确理解tuple不可变的含义,要避免对如下情况的误解:
t这个tuple定义的时候有3个元素,分别是'a','b'和一个list。不是说tuple一旦定义后就不可变了吗?怎么后来又变了?
解释如下:
先看看定义的时候tuple包含的3个元素:
当把list的元素'A'和'B'修改为'X'和'Y'后,tuple变为:
表面上看,tuple的元素确实变了,但其实变的不是tuple的元素,而是list的元素。tuple一开始指向的list并没有改成别的list,所以,tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向'a',就不能改成指向'b',指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!
理解了“指向不变”后,要创建一个内容也不变的tuple怎么做?那就必须保证tuple的每一个元素本身也不能变。
字典(dict)
可以把字典理解为 键值对 的集合(用于保存由 键 ( key )到 值 ( value )的映射关系),但字典的键必须是唯一的。花括号 {} 用于创建空字典。另一种初始化字典的方式是,在花括号里输入逗号分隔的键值对,这也是字典的输出方式。如下图所示:
与列表和元组不同,字典中存储的对象是无序的。字典的价值在于键与值之间的关联。
将一个对象链接至另一个对象,也被称为映射(mapping )。字典是可变的,因此可以向字典中新增、删除键值对或修改某个键的值。
参见下图:
字典的主要用途是通过关键字存储、提取值。用 del 可以删除键值对。用已存在的关键字存储值,与该关键字关联的旧值会被取代。通过不存在的键提取值,则会报错。
对字典执行 list(d) 操作,返回该字典中所有键的列表,按插入次序排列(如需排序,请使用 sorted(d))。检查字典里是否存在某个键,使用关键字 in。
字典可用多种方式来创建:
☆使用花括号内以逗号分隔 键: 值 对,包含于花括号之内的方式:
{'jack': 4098, 'sjoerd': 4127}
{4098: 'jack', 4127: 'sjoerd'}
参见下图:
☆使用字典推导式,如:
{x: x ** 2 for x in range(10)}
参见下图:
☆使用类型构造器(type constructor):dict()、dict([('foo', 100), ('bar', 200)])、dict(foo=100, bar=200)
如果没有给出位置参数,将创建一个空字典。 如果给出一个位置参数并且其属于映射对象,将创建一个具有与映射对象相同键值对的字典。 否则的话,位置参数必须为一个 iterable 对象。 该可迭代对象中的每一项本身必须为一个刚好包含两个元素的可迭代对象。 每一项中的第一个对象将成为新字典的一个键,第二个对象将成为其对应的值。 如果一个键出现一次以上,该键的最后一个值将成为其在新字典中对应的值。
参见下图:
再给出几例:
通过向类型构造函数传递关键字参数来创建字典。
D = dict(name='Bob', age=45, job=('mgr', 'dev'))
通过将键/值元组对传递给类型构造函数来创建字典。
D = dict(zip('abc', [1, 2, 3]))
与前一行的效果相同:接受任何键和值的iterable(可迭代的)对象。
D = dict([['a', 1], ['b', 2], ['c', 3]])
参见下图:
如果给出了关键字参数,则关键字参数及其值会被加入到基于位置参数创建的字典。 如果要加入的键已存在,来自关键字参数的值将替代来自位置参数的值。
以下示例,它们都是等价的:
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})
f = dict({'one': 1, 'three': 3}, two=2)
返回的字典均等于 {"one": 1, "two": 2, "three": 3}:
它们都是等价的,即:a == b == c == d == e == f
https://docs.python.org/zh-cn/3.9/library/functions.html#func-dict
https://docs.python.org/zh-cn/3/library/stdtypes.html#dict
https://docs.python.org/zh-cn/3/tutorial/datastructures.html#dictionaries
下面是字典的几个特点。
1、查找速度快
无论 dict 有 10 个元素还是 10 万个元素,查找速度都一样。而 list 的查找速度随着元素增加而逐渐下降。不过 dict 的查找速度快不是没有代价的,dict 的缺点是占用内存大,还会浪费很多内容,list 正好相反,占用内存小,但是查找速度慢。由于 dict 是按 key 查找,所以,在一个 dict 中,key 不能重复。
2、存储的 key-value 序对没有顺序
这一点和 list 不一样。
当我们打印下面这个 dict 会得到:
d = {
'Adam': 95,
'Lisa': 85,
'Bart': 59
}
print(d)
不同的机器打印的顺序都可能不同,这说明 dict 内部是无序的,不能用 dict 存储有序的集合。
3、作为 key 的元素必须不可变
Python 的基本类型如字符串、整数、浮点数都是不可变的,都可以作为 key。但是 list 是可变的,就不能作为 key。不可变这个限制仅作用于 key,value 是否可变是无所谓的:
{
'123': [1, 2, 3], # key 是 str,value是list
123: '123', # key 是 int,value 是 str
('a', 'b'): True # key 是 tuple,并且tuple的每个元素都是不可变对象,value是 boolean
}
最常用的 key 还是字符串,因为用起来最方便。
字典中的元素是以键值对的形式组成的。也就是说,一个元素都有其定义与之对应。其存在形式例如:
d = {"name": "刘琛", "age": 23, "gender": "男"}
d2 = dict()
通过key来访问对应的值
例如:d[name]。返回name对应的值,如果没有,抛出异常
字典的常见方法有:
clear ----清空
copy----拷贝
get---- 和字典对象[key]类似,获取键对应值,注意,如果没有该键,返回None
keys -----返回所有的键
values ---- 返回所有的值
setdefault ----- 设置默认值
items----- 返回一个键值对
pop(key)----通过key删除键值对
popitem----- 移除一个键值对,移除的规则是LIFO(last in first out)
字典的遍历相对于其他容器比较特殊。因为在它应该输出两部分。即key和key对应的值,用for循环对字典进行遍历有三种方法:
d = {"name": "刘琛", "age": 23, "gender": "男"}
#法一
for k in d.keys():
print(k, d.get(k))
#法二
for k in d:
print(k, d[k])
#法三
for k,v in d.items():
print(k, v)
集合(set)
集合(set)存储的元素也是没有顺序的;集合不能包含重复的元素——会自动去掉重复的元素; set是可变的(但frozenset是不可变)。
目前有两种内置集合类型,set 和 frozenset。 set是可变的——其内容可以使用 add() 和 remove() 这样的方法来改变。 由于是可变类型,它没有哈希值,且不能被用作字典的键或其他集合的元素。 frozenset是不可变并且为可哈希( hashable) --- 其内容在被创建后不能再改变;因此它可以被用作字典的键或其他集合的元素。
除了可以使用 set 构造器,非空的 set (不是 frozenset) 还可以通过将以逗号分隔的元素列表包含于花括号之内来创建,例如: {'jack', 'sjoerd'}。
集合是由不重复元素组成的无序容器。基本用法包括成员检测、消除重复元素。集合对象支持合集、交集、差集、对称差分等数学运算。
创建集合用花括号或 set() 函数。注意,创建空集合只能用 set(),不能用 {},{} 创建的是空字典。
集合可用多种方式来创建:
☆使用花括号内以逗号分隔元素的方式:{'jack', 'sjoerd'}
☆使用集合推导式:{c for c in 'abracadabra' if c not in 'abc'}
参见下图:
☆使用类型构造器(type constructor):set()、 set('foobar')、 set(['a', 'b', 'foo'])、frozenset(range(-4, 5))
返回一个新的 set 或 frozenset 对象,其元素来自于 iterable(可迭代对象,能够逐一返回其成员项的对象)。 集合的元素必须为可哈希(hashable)。 要表示由集合对象构成的集合,所有的内层集合必须为 frozenset 对象。 如果未指定 iterable,则将返回一个新的空集合。参见下图:
以下是一些简单的示例:
https://docs.python.org/zh-cn/3.9/library/functions.html#func-set
https://docs.python.org/zh-cn/3.9/library/functions.html#func-frozenset
https://docs.python.org/zh-cn/3/library/stdtypes.html#set-types-set-frozenset
https://docs.python.org/zh-cn/3/tutorial/datastructures.html#sets
dict 的作用是建立一组 key 和一组 value 的映射关系,dict 的 key 是不能重复的。有的时候,我们只想要 dict 的 key,不关心 key 对应的 value,目的就是保证这个集合的元素不会重复,这时,set 就派上用场了。set 持有一系列元素,这一点和 list 很像,但是 set 的元素没有重复,而且是无序的,这点和 dict 的 key 很像。
set 的特点:
set 的内部结构和 dict 很像,唯一区别是不存储 value,因此,判断一个元素是否在 set 中速度很快。
set 存储的元素和 dict 的 key 类似,必须是不变对象,因此,任何可变对象是不能放入 set 中的。
set 存储的元素也是没有顺序的。
set 不能包含重复的元素(set 会自动去掉重复的元素)。
集合的创建方式为:
s=set()
要注意的是如果()+{ 集合中的元素}来创建集合,只有()和{ }都存在才是集合。如果只有{ }则是一个字典。
且集合中的元素必须是唯一的,不可重复。
集合的常见方法有:
clear ----清空集合
remove ----替换元素
copy ----浅拷贝
add ----增加元素
difference----- 差集
intersection---- 交集
union ----并集
update----- 更新集合,合并集合
discard---- 移除元素,但是如果不存在,则不做任何操作
python容器的有序性和可变性总结
容器名称 | 有序性 | 可变性 |
列表(list) | 有序 | 可变 |
元组(tuple) | 有序 | 不可变 |
字典(dict) | 无序 | 可变 |
集合(set) | 无序 | 可变 |
冻集合(frozenset) | 无序 | 不可变 |
【有序性:是指可以通过其位置来索引某一元素。可变性:是指是否可以对容器中的元素进行增删改的操作。】
python容器的新增,删除,修改,查询的异同小结
容器 | 新增 | 删除 | 修改 | 查询 |
列表 | append方法 | del 方法 | 通过索引进行修改 | 通过索引查询 |
元组 | 无 | 无 | 无 | 通过索引查询 |
字典 | 通过关键字赋值新增 | del 方法 | 通过关键字赋值进行修改 | 通过关键字查询 |
集合 | add 方法 | del 方法 | 无修改方法 | 无 |
说明:
☆同样是新增,为什么列表的方法名是append,而集合的新增方法名是add?
列表新增,除了append方法,还有insert方法,append默认在列表尾部追加,insert要指定插入的索引位置,这两个方法名都比较含蓄的体现出了列表的有序特点。
集合里没有索引的概念,也就没有尾部的概念,新增方法名用add比较合适,和顺序无关,就是增加一个数据。
☆为什么集合没有修改和查询的方法?
从使用场景来分析,集合的主要作用是为了去重,不存在修改的操作,至于查询操作,没有索引,也就不能通过索引来查询数据,也不像字典那样一个key对应一个value,因此,也无法像字典那样通过key去查询value,唯一的近似查询的操作是in 这个成员操作符判断某个数据是否在集合中。
☆字典和集合的创建都用{}, 我们可以将集合看成是一个特殊的字典,集合里key和value是相同的,只是集合里隐去了value部分,只保留了key的部分。
字典和集合的key都是不能重复的,因为key存在的意义就是唯一的与一个value建立起映射关系,两个相同的key,不论是映射到相同的value还是不同的value,都没有存在的意义。
附、python类的构造方法【也称为构造函数(constructor)】的调用示例
# 定义类
class Demo:
# 手动为类添加构造方法
def __init__(self, name, age, gender):
print('构造方法被执行了。', name, age, gender)
# 定义类属性
# 定义 info() 方法
def info(self,):
print('info() 方法被执行了。')
Demo('李萌', 16, '女') # 创建 Demo 类的对象,将会隐式调用构造方法并传入参数值
demo = Demo('阿杰', 18, '男') # 创建 Demo 类的对象,将会隐式调用构造方法并传入参数值
运行结果: