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之函数与变量
本节内容 函数介绍及其作用 函数的定义与调用 函数的参数说明 全局变量与局部变量 值传递和引用传递 一.函数的介绍及其作用 编程语言中的函数与数学中的函数是有区别的:数学中的函数有参数(输入),就会有 ...
随机推荐
- JavaScript数据结构与算法(八) 集合(ECMAScript 6中定义的类似的Set类)
TypeScript方式实现源码 // 特性: // 1. 集合是由一组无序且唯一(即不能重复)的项组成的.这个数据结构使用了与有限集合相同的数学概念,但应用在计算机科学的数据结构中. // 2. 也 ...
- [LeetCode] Number of Atoms 原子的个数
Given a chemical formula (given as a string), return the count of each atom. An atomic element alway ...
- [LeetCode] Next Greater Element I 下一个较大的元素之一
You are given two arrays (without duplicates) nums1 and nums2 where nums1’s elements are subset of n ...
- IOS开发-UIDynamic(物理仿真)简单使用
UIDynamic是从IOS7开始引入的一种新技术,隶属于UIKit框架,我们可以认为是一种物理引擎能模拟和仿真现实生活中的物理现象,比如重力,弹性碰撞等. 可以让开发人员远离物理公式的情况下,实现一 ...
- SpringIOC学习二
Spring的IOC容器通过依赖注入DI(dependency injection)来实现程序之间的依赖关系,达到解耦的方式依赖的方式:a.基于xml文件配置的注入 * 构造函数注入 * ...
- SpringIOC学习一
Spring是一个轻量级的控制反转(IOC)和面向切面(IOP)的容器框架1.控制反转IOC(inversion of controller) IOC是一种概念,是把我们程序中类与类之间的依赖关 ...
- 以太坊如何估计计算gas?
以太坊如何估计估算计算gas?Etherscan上transaction info中有个gas used by txn,结果跟remix给的结果以及geth中getTransactionReceipt ...
- 用DotTrace 来分析.NET-Core程序
1. 前言 看园子里面讲dotTrace 的文章不多,最近也有这方面的需要,于是去搜索了一下,.NET 性能分析方面的工具.目的呢,主要是想发现我的代码中,哪些代码占用了最多时间,来进行优化.主要 ...
- 【网络流】【BZOJ1006】【SCOI2007】蜥蜴
学弟@lher在周末训练赛中出的题目的原题(这个人拿省选题来当作提高组模拟,太丧了...) 题意简析:看题目:) 解题思路:题目显然是最大流. 首先拆点将点权变为边权,然后按照题意对于所有有跳板的点向 ...
- [BZOJ]1045 糖果传递(HAOI2008)
放一道数学题. Description 有n个小朋友坐成一圈,每人有ai个糖果.每人只能给左右两人传递糖果.每人每次传递一个糖果代价为1. Input 第一行一个正整数n<=1000000,表示 ...