Python 面试题(1)
Posted June 2, 2021 by clannadzsy ‐ 2 min read
运行环境:以下示例在 CPython 3.12.14 验证,仅使用标准库。每个完整代码块可独立保存为 demo.py 并执行 python demo.py。第一题的重复结果和第二题的共享列表都是要观察的行为,不是语法错误。
闭包
def multipliers(): return [lambda x: i * x for i in range(4)] print([m(2) for m in multipliers()])正确答案 [6, 6, 6, 6]
错误答案
[0, 2, 4, 6]原因是闭包的后期绑定(late binding):这些函数捕获的是同一个外层局部变量
i,并在调用时读取它。列表推导式结束后i为3,所以之后对四个函数分别传入2,都会得到3 * 2 = 6。这也适用于用def定义的闭包,参见 Python 官方 FAQ。# 创建一个闭包,通过使用默认参数立即绑定它的参数 def multipliers(): return [lambda x, i=i: i * x for i in range(4)] print([m(2) for m in multipliers()]) # [0, 2, 4, 6]或者使用
functools.partial提前绑定乘法的第一个参数:from functools import partial from operator import mul def multipliers(): return [partial(mul, i) for i in range(4)] print([m(2) for m in multipliers()]) # [0, 2, 4, 6]默认参数值是可变的
def extendList(val, list_val=[]): list_val.append(val) return list_val list1 = extendList(10) list2 = extendList(123, []) list3 = extendList('a') print("list1 = %s" % list1) print("list2 = %s" % list2) print("list3 = %s" % list3) # 输出结果 # list1 = [10, 'a'] # list2 = [123] # list3 = [10, 'a']默认参数表达式在函数定义时求值一次,而不是每次调用时重新求值。没有显式传入列表的
list1和list3引用同一个默认列表;list2使用显式传入的新列表。使用None作为默认值,再在函数体内创建列表,可以让省略列表参数的各次调用相互独立,参见官方函数定义说明。# 修改如下 def extendList(val, list_val=None): if list_val is None: list_val = [] list_val.append(val) return list_val list1 = extendList(10) list2 = extendList(123, []) list3 = extendList('a') print("list1 = %s" % list1) print("list2 = %s" % list2) print("list3 = %s" % list3) # 输出结果 # list1 = [10] # list2 = [123] # list3 = ['a']