Contents

Python递归的坑、for变量与list comprehension

前言

今天偶然看到了这么一个视频,讲到Python中递归函数的一个坑,但我发现里面涉及到之前完全不清楚的Python机制,它们看起来很基础,以至于我都有点羞愧了,在这里都记录一下。

For loop不会引入新的作用域

1
2
3
4
5
6
7
8
'''
for循环不会引入作用域,而for创建的变量是直接在外部作用域生效的,
所以这里循环结束后还可以引用a,且a的值是列表最后一个元素
'''
for a in [1,2,3]:
	pass
print(a)
# 输出 3

List comprehension会引入新的作用域

1
2
3
4
5
6
'''
如果用list comprehension,由于它相当于list(),是在一个函数中声明a,所以不会污染外部作用域
'''
b = [a for a in range(3)]
print(a)
# NameError: name 'a' is not defined

def a()递归函数中调用a不一定是自己

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
'''
在递归函数中,调用echo时,python会去外部作用域找echo,而不是直接调用原本的函数,
这里我们用a暂存原echo函数后,修改echo的具体函数,发现它第二次递归时调用的就是新的函数b
'''
def echo(i, limit):
	if i == limit:
		return
	print("echo")
	echo(i + 1, limit)

a = echo
b = lambda x,y : print(x, y)
echo = b
a(1,3)
# echo
# 1, 3

原视频的例子

这个例子融合了上面三个知识点,看看你是否可以正确说出三个print的结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
functions = []
for n in range(8):
    def f():
        return f.index
    f.index = n
    functions.append(f)

print([f() for f in functions])
print([f.index for f in functions])
lst = []
for f in functions:
    lst.append(f())
print(lst)
'''
[7, 7, 7, 7, 7, 7, 7, 7] f()里面调用的f都指向最后一次的def f(),它的.index = 7
[0, 1, 2, 3, 4, 5, 6, 7] 直接调用f.index则不会有问题
[0, 1, 2, 3, 4, 5, 6, 7] for循环每次重新修改了外部作用域的f,所以.index又正确了
'''

Reference

【python】我调用的我不是我?你绝对不知道的python递归陷阱_哔哩哔哩_bilibili