Python 3 函数自由变量的大坑
Python中函数是一个对象, 和整数,字符串等对象有很多相似之处,例如可以作为其他函数的参数或返回对象, Python中的函数还可以携带自由变量, 两者无疑极大增进了Python的表达力.
但是Python函数自由变量的内部机制和列表解析或for循环结合使用时却暗藏杀机:
#---CASE 1
fs = map(lambda i:(lambda j: i*j),range(6))
print([f(2) for f in fs]) #---CASE 2
fs = [lambda j:i*j for i in range(6)]
print([f(2) for f in fs]) #---CASE 3
fs = []
for i in range(6):
fs.append(lambda j:i*j)
if i==3:
break
print([f(2) for f in fs]) #---CASE 4
fs = [(lambda i:lambda j:i*j)(i) for i in range(6)]
print([f(2) for f in fs])
结果:
[0, 2, 4, 6, 8, 10]
[10, 10, 10, 10, 10, 10]
[6, 6, 6, 6]
[0, 2, 4, 6, 8, 10]
可以通过下面这个简单的测试来分析Python函数在执行时是如何确定自由变量的值的:
i = 1
def f(j):
return i*j
print(f(2)) # ---> 2 i = 2
print(f(2)) # ---> 4 def g():
i = 3
def f(j):
return i*j
return f
f = g()
print(f(2)) # ---> 6 i = 100
print(f(2)) # ---> 6
可见,当 函数f在*定义时*, Python不会记录自由变量'i'对应什么对象, 只会告诉f, 你有一个自由变量, 它的名字叫 'i'.
接着, 当函数f在*执行时*, Python告诉f:
(1) 空间上: 你需要在你被*定义时*的外层namespace里面去查找i对应的对象, 假设这个namespace为X.
(2) 时间上: 是在你*当前运行时*, X 里面的 i 对应的对象.
上面那个简单测试中的 i = 2 之后, f(2)随之也返回4也能反映了这一点.
CASE 2和3 也是如此, fs里面每个函数对应的自由变量i在*定义时*都是循环变量i, 因此*执行时*都是对应循环结束或跳出时i所指对象.
而 CASE 1和4为什么能如愿发生变化呢? 这是因为函数对应的自由变量i不再是循环变量i, 而是外层lambda函数*执行时*,循环变量i所指对象在其栈上的拷贝, 由于每次调用外层lambda时i所指对象都不相同, 因此每个函数的自由变量也会指向不同的对象.
最后, 列表解析里面的作用域是一个全新的作用域, 而普通的for循环则有所不同. 例如:
#---CASE 2
fs = [lambda j:i*j for i in range(6)]
print([f(2) for f in fs])
i = 4
print([f(2) for f in fs]) #---CASE 3
fs = []
for i in range(6):
fs.append(lambda j:i*j)
print([f(2) for f in fs])
i = 4
print([f(2) for f in fs])
结果是:
[10, 10, 10, 10, 10, 10]
[10, 10, 10, 10, 10, 10]
[10, 10, 10, 10, 10, 10]
[8, 8, 8, 8, 8, 8]
Python 3 函数自由变量的大坑的更多相关文章
- Python之函数进阶
本节内容 上一篇中介绍了Python中函数的定义.函数的调用.函数的参数以及变量的作用域等内容,现在来说下函数的一些高级特性: 递归函数 嵌套函数与闭包 匿名函数 高阶函数 内置函数 总结 一.递归函 ...
- 【转】Python之函数进阶
[转]Python之函数进阶 本节内容 上一篇中介绍了Python中函数的定义.函数的调用.函数的参数以及变量的作用域等内容,现在来说下函数的一些高级特性: 递归函数 嵌套函数与闭包 匿名函数 高阶函 ...
- Python:函数解释(面向过程)
1. 函数概述 在编程的语境下,函数 (function) 是指一个有命名的.执行某个计算的语句序列 (sequence of statements) .函数可以针对某类问题建立了通用解决步骤(算法) ...
- python的函数
函数一词起源于数学,但是在编程中的函数和数学中的有很大不同.编程中的函数式组织好的,可重复使用的,用于实现单一功能或相关联功能的代码块. 我们在学习过程中已经使用过一些python内建的函数,如pri ...
- python strip()函数 介绍
python strip()函数 介绍,需要的朋友可以参考一下 函数原型 声明:s为字符串,rm为要删除的字符序列 s.strip(rm) 删除s字符串中开头.结尾处,位于 rm删除 ...
- python split()函数
Python split()函数 函数原型: split([char][, num])默认用空格分割,参数char为分割字符,num为分割次数,即分割成(num+1)个字符串 1.按某一个字符分割. ...
- Python数学函数
1.Python数学函数 1.abs(x):取绝对值,内建函数 2.math.ceil(x):向上取整,在math模块中 3.cmp(x,y):如果 x < y ,返回-1:如果 x == y ...
- Python回调函数用法实例详解
本文实例讲述了Python回调函数用法.分享给大家供大家参考.具体分析如下: 一.百度百科上对回调函数的解释: 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函 ...
- Python之函数与变量
本节内容 函数介绍及其作用 函数的定义与调用 函数的参数说明 全局变量与局部变量 值传递和引用传递 一.函数的介绍及其作用 编程语言中的函数与数学中的函数是有区别的:数学中的函数有参数(输入),就会有 ...
随机推荐
- [LeetCode] Squirrel Simulation 松鼠模拟
There's a tree, a squirrel, and several nuts. Positions are represented by the cells in a 2D grid. Y ...
- 实验吧_简单的sql注入_1、2、3
简单的sql注入1 看着这个简单的界面,一时间没有特别好的思路,先输入一个1',发生了报错 初步猜测这是一个字符型的注入,他将我们输入的语句直接当成sql语句执行了,按题目的意思后面肯定过滤了很多注入 ...
- [WC 2011]Xor
Description Input 第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目. 接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 ...
- luogu 3413 SAC#1 - 萌数
题目描述 辣鸡蒟蒻SOL是一个傻逼,他居然觉得数很萌! 好在在他眼里,并不是所有数都是萌的.只有满足“存在长度至少为2的回文子串”的数是萌的——也就是说,101是萌的,因为101本身就是一个回文数:1 ...
- [AHOI2005]约数研究
题目描述 科学家们在Samuel星球上的探险得到了丰富的能源储备,这使得空间站中大型计算机“Samuel II”的长时间运算成为了可能.由于在去年一年的辛苦工作取得了不错的成绩,小联被允许用“Samu ...
- ●SPOJ 7258 Lexicographical Substring Search
题链: http://www.spoj.com/problems/SUBLEX/题解: 后缀自动机. 首先,因为相同的子串都被存在了自动机的同一个状态里面,所以这就很自然的避免了重复子串的问题. 然后 ...
- bzoj 4542: [Hnoi2016]大数
Description 小 B 有一个很大的数 S,长度达到了 N 位:这个数可以看成是一个串,它可能有前导 0,例如00009312345 小B还有一个素数P.现在,小 B 提出了 M 个询问,每个 ...
- linux tracepoint用法【转】
转自:https://blog.csdn.net/u014089131/article/details/73907995 在kernel中经常会看到trace_XX形式的函数,但是又找不到它的定义. ...
- Spring之定时器(QuartzJobBean)的实现
需求:做个分配任务的功能模块,在任务截止前的十五分钟进行提醒(发送邮件.短信.系统提醒).每隔五分钟提醒一次,直到任务完成! 想法:刚开始是想着是不是可以做个监听器,监听截止时间.当时间到了开始进 ...
- Linux系统之TroubleShooting(故障排除)(转)
尽管Linux系统非常强大,稳定,但是我们在使用过程当中,如果人为操作不当,仍然会影响系统,甚至可能使得系统无法开机,无法运行服务等等各种问题.那么这篇博文就总结一下一些常见的故障排除方法,但是不可能 ...