容器#
Python 有 4 种基本容器
List |
Tuple |
Dict |
Set |
|
---|---|---|---|---|
有索引 |
✓ |
✓ |
✓ |
× |
顺序 |
✓ |
✓ |
✓ |
× |
可变 |
✓ |
× |
✓ |
✓ |
可重复 |
✓ |
✓ |
× |
× |
1. 列表、元组#
列表
[]
有序动态数组
可变且长度可修改。如:一个人的年龄、体重、身高。
元组
()
有序静态数组
不可变。如:一个人的生日、出生地。
缓存于运行时环境,每次使用时无须访问内核去分配内存。
1.1. 切片#
切片的基本格式为 [start:end:step]
。但应尽量避免使用 step
,尤其是字符串类型为 utf-8 字节码的时候(对 Unicode 运行良好)。
倒序排序的快捷操作为
[::-1]
。
w = "寿司"
x = w.encode("utf-8")
print(w.encode("utf-8")[::-1])
# b'\xb8\x8f\xe5\xbf\xaf\xe5'
print(y.decode("utf-8"))
# 'utf-8' codec can't decode byte 0xb8 in position 0: invalid start byte
1.2. 条件切片#
间隔取值
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
n = 3
lst[n - 1::n]
# [3, 6, 9]
也可以使用 slice(start:stop:step)
函数
a = [0, 1, 2, 3, 4, 5]
LASTTHREE = slice(-3, None)
a[LASTTHREE]
# [3, 4, 5]
1.3. 解包#
对于不定长的列表,可用星号表达式进行解包,其中, *
可出现在任何位置,其接收值永远为列表。
x, *y, z = 0, 1, 2, 3, 4, 5
x # 0
y # [1, 2, 3, 4]
z #5
2. 字典、集合#
字典
{key: value}
无序键值对
key
不能重复,且不可修改
集合
()
无序动态数组
可变且长度可修改
数学表达 |
Python 表达 |
方法 |
---|---|---|
S ∩ Z |
s & z |
|
S ∪ Z |
s ∥ z |
|
S \ Z |
s - z |
|
S Δ Z |
s ^ z |
|
S ⊂ Z |
s < z |
|
S ⊆ Z |
s <= z |
|
S ⊃ Z |
s > z |
|
S ⊇ Z |
s >= z |
|
2.1. 查询#
从 Python 3.7 版本开始,字典将保留插入顺序。
在字典中查询缺失
value
,优先选择get()
方法,dict[key]
在key
不存在时会返回错误。
votes = {
"baguette": ["Bob", "Alice"],
"ciabatta": ["Coco", "Deb"],
}
key ="brioche"
who ="Elmer"
names = votes.get(key)
if names is None:
votes[key] = names = []
names.append(who)
print(votes)
2.2. 缺省#
虽然使用 get()
方法更好,但对于某些用例,setdefault()
似乎是最简单的选择。
from collections import defaultdict
visits = {
"Mexico": {"Tulum", "Puerto Vallarta"},
"Japan": {"Hakone"},
}
visits.setdefault("France", set()).add("Arles")
# Short
if (japan := visits.get("Japan")) is None:
# Long
visits["Japan"] = japan = set()
japan.add("Kyoto")
print(visits)
3. 字符串#
3.1. 替换、删除#
# replace
s1 = 'aaaaaaaaa'
s1.replace('a', 'b') #'bbbbbbbbb'
s1.replace('a', 'b', 3) #'bbbaaaaaa'
# delete
t = '-----hello====='
t.lstrip('-')
# 'hello====='
t.strip('-=')
# 'hello'
3.2. 拼接#
# 合并
list_of_strings = ["Hello", "my", "friend"]
# BAD:
my_string = ""
for i in list_of_strings:
my_string += i + " "
# GOOD:
list_of_strings = ["Hello", "my", "friend"]
my_string = " ".join(list_of_strings)
4. 特殊容器#
4.1. 数组#
若需要一个只包含数字的列表,则 array.array
比 list 更高效。数组支持所有跟可变序列有关的操作,包括 .pop
、.insert
和 .extend
。另外,数组还提供从文件读取和存入文件的更快的方法,如 .frombytes
和 .tofile
。
Python 数组跟 C 语言数组一样精简。
4.2. 具名元组#
collections.namedtuple()
用来构建一个带字段名的元组和一个有名字的类。这个带名字的类对调试程序有很大帮助。
用 namedtuple
构建的类的实例所消耗的内存跟元组是一样的,因为字段名都被存在对应的类里面。这个实例跟普通的对象实例比起来也要小一些,因为 Python 不会用 __dict__
来存放这些实例的属性。
from collections import namedtuple
City = namedtuple('City', 'name country population coordinates')
tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
tokyo # City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))
tokyo.population # 36.933
tokyo.coordinates # (35.689722, 139.691667)
tokyo[1] # 'JP'
4.3. 有序字典#
要控制字典中元素的顺序,可以使用 collections
模块中的 OrderedDict
类。当对字典做迭代时,它会严格按照元素初始添加的顺序进行。
City._fields # ('name', 'country', 'population', 'coordinates')
LatLong = namedtuple('LatLong', 'lat long')
delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
delhi = City._make(delhi_data)
delhi._asdict()
# OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))])
4.4. 双向队列#
collections.deque
是一个线程安全、可以快速从两端添加或者删除元素的数据类型。若想要有一种数据类型来存放最近用到的几个元素,这是一个很好的选择。
若不指定队列的大小,也就得到了一个无界限的队列列。
从队列两端添加或弹出元素的复杂度都是 O(1),列表的复杂度为 O(N)。
from collections import deque
dq = deque(range(10), maxlen=10)
dq
# deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
dq.rotate(3)
dq
# deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
dq.rotate(-4)
dq
# deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
dq.appendleft(-1)
dq
# deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
dq.extend([11, 22, 33])
dq
# deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
dq.extendleft([10, 20, 30, 40])
dq
# deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)
5. 可变与不可变#
比较字符串或整数是否相等时,应该使用 ==
,而不是 is
。
5.1. 元组的相对不可变#
元组不可变,但元组内的可变类型可变
t1 = (1, 2, [30, 40])
t2 = (1, 2, [30, 40])
print(id(t1[-1]))
#
t1[-1].append(99)
print(t1)
# (1, 2, [30, 40, 99])
print(id(t1[-1]))
#
5.2. 深浅复制#
浅拷贝(shallowCopy)
增加了一个指针指向已存在的内存地址,复制了最外层容器,副本中的元素是源容器中元素的引用
若所有元素都是不可变的,则这样没有问题,还能节省内存
若有可变的元素,可能就会导致意想不到的问题
深拷贝(deepCopy)
增加一个指针,并且申请一个新的内存,使新增加的指针指向新的内存
释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误
l1 = [3, [55, 44], (7, 8, 9)]
l2 = l1
print(l2[0] is l1[0]) # True
print(l2 is l1) # True
from copy import deepcopy
a = [10, 20]
b = [a, 30]
a.append(b)
c = deepcopy(a)
print(a[0] is c[0]) # True
print(a is c) # False
6. 其他#
6.1. 一些陷阱#
# 字符串
'Monty Python'.rstrip(' Python') # 'M'
'Monty Python'.removesuffix(' Python') # 'Monty'
# 字典
d = {}
d[(1, 2)] = 3 # ok
d[[1, 2]] = 4 # error