Python延迟绑定问题原理及解决方案

延迟绑定出现在闭包问题中。下面我们看一个闭包的例子:

def (n):
  def mul(x):
    return n*x
  return mul
double = gen_mul(2)
doubled_value = double(6) 

可以看出满足闭包的几点:

  • 有内部函数
  • 内部函数引用了外部函数中的自由变量
  • 内部函数被返回

闭包的优点:

  • 可以避免使用全局变量
  • 可以持久化变量,达到静态变量的作用

闭包的缺点:

  • 可能会消耗大量的内存
  • 可能会导致内存泄漏

当然缺点可以通过人为避免。

现在我们来看看另一个会引出延迟绑定的例子:

def multipliers():
  return [lambda x : i * x for i in range(4)]
print([m(2) for m in multipliers()]) # [6,6,6,6]

上边的例子会输出[6,6,6,6],而不是我们预期的[0,2,4,6]。

这就是延迟绑定导致的结果。具体过程我们可以来分析下:
执行第三行时,会先执行multipliers函数,然后执行函数中的列表解析式。在每一次迭代的时候都会生成一个匿名函数(这里只是定义)作为元素。然后回到第三行,遍历返回的列表中的匿名函数,传入参数2并执行。此时函数类似于这样:

def noname(x):
return i * x

我们知道Python查找变量的作用域链的顺序依次为LEGB:

局部变量(L)->外部函数中的局部变量(E)->全局变量(G)->内置变量(B)

非常重要的一点我们需要知道:Python的作用域在编译时就已经形成了,而不是在运行时,函数的作用域与其被调用的位置无关。

那么在本例中,上面的noname函数体中的i从何而来呢?当然首先会到multipliers函数的局部变量中去寻找。此时i的值已经为3,所以出现这种让人”费解”的现象。

那么现在我们既然已经知道了原因,那么要怎样解决呢?

我们可以将迭代的i值直接注入到匿名函数的函数体中,这里给出两种方法:

通过为参数设置默认值,这是因为在编译时就会计算确定默认值:

def multipliers_ch1():
return [lambda m,x=i : m * x for i in range(4)]

通过内置函数partial:

from functools import partial
def multipliers_ch2():
  return [partial(lambda m,x : m * x,i) for i in range(4)]

利用生成器的延迟计算:

def multipliers_ch3():
  for m in range(4):
    yield lambda x: m * x

partial及生成器的内容会在以后分享。

运行结果

print([m(2) for m in multipliers_ch1()]) # [0,2,4,6]
print([m(2) for m in multipliers_ch2()]) # [0,2,4,6]
print([m(2) for m in multipliers_ch3()]) # [0,2,4,6]

注:

自由变量:指未在本地作用域中绑定的变量,我们可通过访问函数的code属性进行查看:

fun.code.co_freevars

LEGB: 可看该部分解释

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Python性能提升之延迟初始化

    所谓类属性的延迟计算就是将类的属性定义成一个property,只在访问的时候才会计算,而且一旦被访问后,结果将会被缓存起来,不用每次都计算.构造一个延迟计算属性的主要目的是为了提升性能 property 在切入正题之前,我们了解下property的用法,property可以将属性的访问转变成方法的调用. class Circle(object): def __init__(self, radius): self.radius = radius @property def area(self):

  • 详解Python 多线程 Timer定时器/延迟执行、Event事件

    Timer继承子Thread类,是Thread的子类,也是线程类,具有线程的能力和特征.这个类用来定义多久执行一个函数. 它的实例是能够延迟执行目标函数的线程,在真正执行目标函数之前,都可以cancel它. Timer源码: class Timer(Thread): def __init__(self, interval, function, args=None, kwargs=None): Thread.__init__(self) self.interval = interval self.

  • python3文件复制、延迟文件复制任务的实现方法

    使用python版本3.6.1 工作中测试客户端传输报文速率,写了以下两个脚本. 第一个,简单的复制文件并重命名. 第二个,在循环中增加延时的功能. 使用场景将文件复制并重命名(重命名方式在文件末尾加生成的随机数) #!/usr/bin/python3 #coding=GB2312 import os import os.path import random import shutil count = 0 #源文件夹 src="E:\\file\\CEB411Message__201711151

  • Python检测网络延迟的代码

    本文讲述了Python检测网络延迟的代码.分享给大家供大家参考,具体如下: #!/usr/bin/env python # coding: utf-8 # coding: cp950 ''''' Create Date: 2012-11-06 Version: 1.0 Description: Detection host survival Author: Victor QQ: 1409175531 ''' ''''' Please run the script with root ''' im

  • Python日志无延迟实时写入的示例

    我在用python生成日志时,发现无论怎么flush(),文件内容总是不能实时写入,导致程序意外中断时一无所获. 以下是查到的解决方案(亲测可行): open 函数中有一个bufferin的参数,默认是-1,如果设置为0是,就是无缓冲模式. 但是用二进制模式打开这个文件,并且把要写入的信息转换byte -like如下. with open("test.txt",'wb',buffering=0) as f: #wb是写模式加二进制模式 f.write(b"hello!&quo

  • Python方法的延迟加载的示例代码

    数据挖掘的过程中,数据进行处理是一重要的环节,我们往往会将其封装成一个方法,而有的时候这一个方法可能会被反复调用,每一次都对数据进行处理这将是一个很耗时耗资源的操纵,那么有没有办法将计算后的结果 缓存 起来达到 调用一次,处处运行 的效果,经过一番研究在 lazy_object_proxy/utils.py 中发现了一段代码,详见lazy_object_proxy. class cached_property(object): def__init__(self, func): self.func

  • Python中的延迟绑定原理详解

    直接看下面例子 my_ld = [lambda x:x*i for i in range(3)] my_list = [ld(2) for ld in my_ld] print(my_list) 本想是想通过以上代码,输出[0, 2, 4]的,但结果却是[4, 4, 4] 下面说下本人对这个结果的理解: 因为Python解释器,遇到lambda(或者def),只是定义了一个匿名函数对象,并保存在内存中,只有等到调用这个匿名函数的时候,才会执行函数内部的代码(x*i).所以匿名函数中的i并不是立即

  • Python类属性的延迟计算

    所谓类属性的延迟计算就是将类的属性定义成一个property,只在访问的时候才会计算,而且一旦被访问后,结果将会被缓存起来,不用每次都计算. 优点 构造一个延迟计算属性的主要目的是为了提升性能 实现 class LazyProperty(object): def __init__(self, func): self.func = func def __get__(self, instance, owner): if instance is None: return self else: valu

  • Python延迟绑定问题原理及解决方案

    延迟绑定出现在闭包问题中.下面我们看一个闭包的例子: def (n): def mul(x): return n*x return mul double = gen_mul(2) doubled_value = double(6) 可以看出满足闭包的几点: 有内部函数 内部函数引用了外部函数中的自由变量 内部函数被返回 闭包的优点: 可以避免使用全局变量 可以持久化变量,达到静态变量的作用 闭包的缺点: 可能会消耗大量的内存 可能会导致内存泄漏 当然缺点可以通过人为避免. 现在我们来看看另一个会

  • 对Python闭包与延迟绑定的方法详解

    Python闭包可能会在面试或者是工作中经常碰到,而提到Python的延迟绑定,肯定就离不开闭包的理解,今天总结下 关于闭包的概念以及一个延迟绑定的面试题. Python闭包 1.什么是闭包,闭包必须满足以下3个条件: 必须是一个嵌套的函数. 闭包必须返回嵌套函数. 嵌套函数必须引用一个外部的非全局的局部自由变量. 举个栗子 # 嵌套函数但不是闭包 def nested(): def nst(): print('i am nested func %s' % nested.__name__) ns

  • Python函数中闭包和延迟绑定详情

    闭包必须满足以下3个条件: 必须有一个内嵌函数 内嵌函数必须应用外部函数的变量 外部函数的返回值必须是内嵌函数 关于请看下面代码: def multipliers(): return [lambda x : i*x for i in range(4)] print ([m(2) for m in multipliers()] ) """ [6, 6, 6, 6] """ 为什么输出结果为[6, 6, 6, 6],这段代码相当于 def multi

  • Python构建网页爬虫原理分析

    既然本篇文章说到的是Python构建网页爬虫原理分析,那么小编先给大家看一下Python中关于爬虫的精选文章: python实现简单爬虫功能的示例 python爬虫实战之最简单的网页爬虫教程 网络爬虫是当今最常用的系统之一.最流行的例子是 Google 使用爬虫从所有网站收集信息.除了搜索引擎之外,新闻网站还需要爬虫来聚合数据源.看来,只要你想聚合大量的信息,你可以考虑使用爬虫. 建立一个网络爬虫有很多因素,特别是当你想扩展系统时.这就是为什么这已经成为最流行的系统设计面试问题之一.在这篇文章中

  • MySQL主从延迟现象及原理分析详解

    一.现象 凌晨对线上一张表添加索引,表数据量太大(1亿+数据,数据量50G以上),造成主从延迟几个小时,各个依赖从库的系统无法查询数据,最终影响业务. 现在就梳理下主从延迟的原理. 二.原理 根据 MySQL 官方文档 MySQL Replication Implementation Details 中的描述,MySQL 主从复制依赖于三个线程:master一个线程(Binlog dump thread),slave两个线程(I/O thread和SQL thread).主从复制流程如下图: m

  • Python异步爬虫实现原理与知识总结

    一.背景 默认情况下,用get请求时,会出现阻塞,需要很多时间来等待,对于有很多请求url时,速度就很慢.因为需要一个url请求的完成,才能让下一个url继续访问.一种很自然的想法就是用异步机制来提高爬虫速度.通过构建线程池或者进程池完成异步爬虫,即使用多线程或者多进程来处理多个请求(在别的进程或者线程阻塞时). import time #串形 def getPage(url): print("开始爬取网站",url) time.sleep(2)#阻塞 print("爬取完成

  • python 装饰器(Decorators)原理说明及操作代码

    目录 1 必要的2个核心操作 1.1 核心操作1, 函数内部可以定义函数 1.2 核心操作2 函数可以作为对象被输入输出 1.2.1 核心操作2的前置条件,函数是对象 1.2.2函数作为输入 1.2.3 函数作为输出 2 尝试构造装饰器 3装饰器定义的简写 本文目的是由浅入深地介绍python装饰器原理 装饰器(Decorators)是 Python 的一个重要部分 其功能是,在不修改原函数(类)定义代码的情况下,增加新的功能 为了理解和实现装饰器,我们先引入2个核心操作: 1 必要的2个核心操

  • Python OpenCV 图像矫正的原理实现

    目录 题目描述 基本思路 核心代码 题目描述 目录hw1下的图像是一些胶片的照片,请将其进行度量矫正. 推荐流程:采用Canny算子,检测边缘点:采用Hough直线检测,根据边缘点检测胶片边缘对应的4条直线:4条直线在图像平面中的交点为胶片图像的4个顶点.根据4个顶点与真实世界中胶片的位置(假设胶片图像长宽比为4:3),得到两个平面之间的单应变换矩阵,并根据单应变换矩阵实现图像矫正. 基本思路 使用Canny算子,检测边缘点:以边缘点作为输入,采用Hough直线检测,检测出最多点共线的四条直线,

  • PHP静态延迟绑定和普通静态效率的对比

    PHP静态延迟绑定和普通静态效率的对比 只是一个简单的小实验,对比了下 延迟绑定 和 非延迟的效率 延迟绑定主要就是使用 static 关键字来替代原来的 self ,但功能非常强大了 实验代码: class A { protected static $cc1 = array('a1', 'b', 'c', 'd'); protected static $cc2 = array('a2', 'b', 'c', 'd'); protected static $cc3 = array('a3', '

随机推荐