python lambda结合列表推导式?
11 个回答
一个列表,列表里的每一个元素是个lambda function,就是如此。
至于那个i,因为是个变量,在lambda求值时它是9,也就是说构造lambda的时候i是几并不重要,lambda里只是用了这么个引用;求值时i是多少它就是多少。
能理解下面这段代码为什么输出1而不是0吗:
i = 0
def foo():
print(i)
i = 1
func()
如果能理解上面的,那么接下来这样:
data = range(10)
funcs = []
for i in data:
l = lambda x: i * x
funcs.append(l)
def m(x):
return i * x
funcs.append(m)
i = 21
for func in funcs:
print(func(2))
全都是42。
Python的闭包(lambda和function等)会保存变量的名字(i)和scoping(global)。当你调用的时候才会去找具体的对象。所以你给i赋不同的值,在那之后的调用都会去取新的object(21)。
In [1]: import dis
In [2]: i = 0
In [3]: def foo(x):
...: return i * x
...:
In [4]: dis.dis(foo)
2 0 LOAD_GLOBAL 0 (i)
3 LOAD_FAST 0 (x)
6 BINARY_MULTIPLY
7 RETURN_VALUE
In [5]: bar = lambda x: i * x
In [6]: dis.dis(bar)
1 0 LOAD_GLOBAL 0 (i)
3 LOAD_FAST 0 (x)
6 BINARY_MULTIPLY
7 RETURN_VALUE
重点在LOAD_GLOBAL. 调用的时候是去找global的i对应的object。
那么,怎么让Python保存object而不是保存名字和scoping呢?
Assignment.
创建一个local的变量,对其赋值,Python会记住那个时候的object,然后LOAD_GLOBAL变成LOAD_FAST,然后lambda被调用的时候就不会再去找global的i了。
In [7]: def baz(x, i=i):
...: return i * x
...:
In [8]: dis.dis(baz)
2 0 LOAD_FAST 1 (i)
3 LOAD_FAST 0 (x)
6 BINARY_MULTIPLY
7 RETURN_VALUE
这上面i=i实际上创建了一个local的i,然后把global的i赋值给它。后面在return的时候就会用local的i。
除了LOAD_GLOBAL,还有啥LOAD_DEREF, LOAD_CLOSURE的。唉头疼,不懂,不说了。
OK,修改一下你的code:
data = range(10)
funcs = [lambda x, y=i: y * x for i in data]
i = 12345
for func in funcs:
print(func(2))
不过这样一来func就变成接受两个参数的lambda了。。。
回到你的例子,为啥有这么奇怪的现象:
当任何一个lambda在被调用的时候,python会去找i当前对应的object,然后返回那个i*x;而不是你想像中的去找lambda定义的时候的那个object。
你要明白几件事情:
1. for i in data实际上等于
iterator = iter(data)
i = next(iterator)
# do something
i = next(iterator)
# do something
...
# raise StopIteration
2. Python的def、lambda等等都是statement,Python会执行一些特殊的code。这是为什么i=i能奏效的原因。
3. name和value(object)的区别
Ned Batchelder这个talk你可以看一下
Facts and myths about Python names and values
4. scoping
python变量的scoping其实很直接:
4.1. Python只有三种scoping:global,module,class,function。
这是三种吗,会数数吗同学?
。。。module scoping其实就是global,所以实际上只有module,class,function三种scoping。
不像C++,加个左括号就是一层scoping。
4.2. 有assignment(等号=)的变量都是local,没有assignment的都不是local
除此之外,
python 2有一个global declaration可以指定某些变量是global的(即使有assignment也是global)
python 3有一个nonlocal declaration可以指定某些变量不是local的(即使有assignment也不是local)
多说一句,function,lambda的参数实际上有一个隐式的assignment。