Python容器初始化技巧

类比C++ STL容器,记录Python容器初始化的一些技巧和陷阱

初始化一个集合的数组

C++写法

1
vector<set<int>> a(n);

正确的Python写法

1
a = [set() for i in range(n)]

一个错误的Python写法

1
a = [set()] * n

如果是常数值,这样写没问题,但是set()是对象,这会导致a[0] a[1] … a[n-1] 存储的都是指向同一个set对象的引用

初始化一个整数到整数集合的映射

C++写法

1
2
3
4
5
map<int, set<int>> b;

for(int i=0;i<10;i++) {
b[i].insert(i); // 在首次访问b[i]时会自动创建一个空的set<int>,所以可以直接调用insert
}

有用的但不舒服Python写法

1
2
3
4
5
b = {}
for i in range(10):
if i not in b: # 首次使用b[i]前要先判断并初始化
b[i] = set()
b[i].add(i)

defaultdict

有用且舒服的Python写法

1
2
3
4
5
from collections import defaultdict

b = defaultdict(set) # 这里指定了dict的值类型
for i in range(10):
b[i].add(i) # 这里就可以像C++一样直接add了

嵌套defaultdict

如果我们想定义一个二重嵌套的defaultdict,类似map<int, map<int>>,不能简单地使用 defaultdict(defaultdict(int)),因为defaultdict需要传递的参数是一个类型T,可以通过T()构造出默认的对象,而defaultdict(int)是对象而不是对象的类型,其实关键是通过T()能得到构造出默认对象就可以,所以我们可以定义一个方法返回defaultdict(int)

1
2
3
def defaultdict_int_creator():
return defaultdict(int)
defaultdict(defaultdict_int_creator)

这样就可以定义一个二重嵌套的defaultdict,更简单地,我们可以把函数改成lambda表达式

1
2
3
4
# 二重嵌套 defaultdict
defaultdict(lambda: defaultdict(int))
# 三重嵌套 defaultdict
defaultdict(lambda: defaultdict(lambda: defaultdict(int)))