容器#

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.intersection(it, ...)

S ∪ Z

s ∥ z

s.union(it, ...)

S \ Z

s - z

s.difference(it, ...)

S Δ Z

s ^ z

s.symmetric_difference(it)

S ⊂ Z

s < z

S ⊆ Z

s <= z

s.issubset(it)

S ⊃ Z

s > z

S ⊇ Z

s >= z

s.issuperset(it)

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