Skip to content

Python

语法

  • 类的 __init__() 方法只能返回 None;显式返回其他值会触发 TypeError
  • with EXPR as VAR: 会先调用上下文管理器的 __enter__(),并把它的返回值绑定到 VAR;离开代码块时调用 __exit__()VAR 在代码块结束后仍然存在,其类型由 __enter__() 的返回值决定。
  • print(OBJ) 优先使用 OBJ.__str__() 的结果;如果类型没有定义 __str__(),则回退到 __repr__()

函数默认参数

默认参数在函数定义时求值一次,而不是在每次调用时重新求值。因此,不要把列表、字典、集合等可变对象直接用作默认值:

def add_item(item, items=[]):
    items.append(item)
    return items


print(add_item(1), add_item(2))
# [1, 2] [1, 2]

两次调用复用了同一个默认列表。print() 的参数会先从左到右完成求值;两个返回值又指向同一个列表,所以真正打印时它们都已经是 [1, 2]

需要为每次调用创建新列表时,应使用 None 作为哨兵值:

def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

闭包与延迟绑定

闭包中的自由变量通常在函数被调用时查找,而不是在函数创建时复制其当前值,这称为延迟绑定(late binding):

funcs = [lambda: i for i in range(3)]

print(len(funcs))
# 3
print([func() for func in funcs])
# [2, 2, 2]

列表推导式创建了三个不同的函数对象,但它们都引用列表推导式作用域中的同一个 i。推导式结束后,该变量的最终值为 2,所以稍后调用三个函数时都得到 2。在 Python 3 中,这个 i 属于列表推导式自己的作用域,不会泄漏到外层作用域。

如果需要保存每轮循环当时的值,可以利用默认参数在函数定义时求值的特性:

funcs = [lambda value=i: value for i in range(3)]

print([func() for func in funcs])
# [0, 1, 2]

列表推导式与生成器表达式

把列表推导式外层的方括号换成圆括号,就得到生成器表达式:

# 列表推导式:立即创建三个函数并保存在列表中
funcs_list = [lambda value=i: value for i in range(3)]

# 生成器表达式:迭代时才逐个创建函数
funcs_generator = (lambda value=i: value for i in range(3))

print([func() for func in funcs_generator])
# [0, 1, 2]

列表会立即计算全部元素,可以按索引访问和重复遍历。生成器按需产生元素,通常只可消费一次,也不支持按索引访问;它适合不需要一次性把所有结果保存在内存中的场景。

Certifi

certifi 是一个 Python 库,旨在为 Python 项目提供 Mozilla 维护的受信任根证书合集(CA Bundle),确保 HTTPS 网络请求的安全。如果使用 mitmproxy debug,可能需要 export SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem 来信任相关证书。