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

Python闭包可能会在面试或者是工作中经常碰到,而提到Python的延迟绑定,肯定就离不开闭包的理解,今天总结下 关于闭包的概念以及一个延迟绑定的面试题。

Python闭包

1、什么是闭包,闭包必须满足以下3个条件:

必须是一个嵌套的函数。

闭包必须返回嵌套函数。

嵌套函数必须引用一个外部的非全局的局部自由变量。

举个栗子

# 嵌套函数但不是闭包
def nested():
  def nst():
    print('i am nested func %s' % nested.__name__)
  nst()

# 闭包函数
def closure():
  var = 'hello world' # 非全局局部变量

  def cloe():
    print(var) # 引用var

  return cloe # 返回内部函数

cl = closure()
cl()

2、闭包优点

避免使用全局变量

可以提供部分数据的隐藏

可以提供更优雅的面向对象实现

优点1,2 就不说了,很容易理解,关于第三个,例如当在一个类中实现的方法很少时,或者仅有一个方法时,就可以选择使用闭包。

举个栗子

# 用类实现一个加法的类是这样
class _Add(object):
  def __init__(self, a, b):
    self.a = a
    self.b = b

  def add(self):
    return self.a + self.b

# 用闭包实现
def _Add(a):
  def add(b):
    return a + b

  return add 

ad = _Add(1) # 是不是很像类的实例化
print(ad(1)) # out:2
print(ad(2)) # out:3
print(ad(3)) # out:4

闭包的概念差不多就是这样了。

Python 延迟绑定

结合一个题目来说明:

def multipliers():
  return [lambda x : i*x for i in range(4)]

print [m(2) for m in multipliers()] 

output:
# [6, 6, 6, 6]

其实这个题目,可能目的是想输出:[0, 2, 4, 6],如何改进才能输出这个结果呢?

def multipliers():
  # 添加了一个默认参数i=i
  return [lambda x, i=i: i*x for i in range(4)]

print [m(2) for m in multipliers()] 

output:
# [0, 2, 4, 6]

multipliers就是一个闭包函数了

def multipliers():
  return [lambda x : i*x for i in range(4)]
 # multipliers内嵌套一个匿名函数
 # 该匿名函数引用外部非全局变量 i
 # 返回该嵌套函数
print [m(2) for m in multipliers()]

下面来解释为什么输出结果是[6,6,6,6]。

运行代码,代码从第6行开始运行,解释器碰到了一个列表解析,循环取multipliers()函数中的值,而multipliers()函数返回的是一个列表对象,这个列表中有4个元素,每个元素都是一个匿名函数(实际上说是4个匿名函数也不完全准确,其实是4个匿名函数计算后的值,因为后面for i 的循环不光循环了4次,同时提还提供了i的变量引用,等待4次循环结束后,i指向一个值i=3,这个时候,匿名函数才开始引用i=3,计算结果。所以就会出现[6,6,6,6],因为匿名函数中的i并不是立即引用后面循环中的i值的,而是在运行嵌套函数的时候,才会查找i的值,这个特性也就是延迟绑定)

# 为了便于理解,你可以想象下multipliers内部是这样的(这个是伪代码,并不是准确的):

def multipliers():
  return [lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x]

因为Python解释器,遇到lambda(类似于def),只是定义了一个匿名函数对象,并保存在内存中,只有等到调用这个匿名函数的时候,才会运行内部的表达式,而for i in range(4) 是另外一个表达式,需等待这个表达式运行结束后,才会开始运行lambda 函数,此时的i 指向3,x指向2

那我们来看下,添加了一个i=i,到底发生了什么?

def multipliers():
  # 添加了一个默认参数i=i
  return [lambda x, i=i: i*x for i in range(4)]

添加了一个i=i后,就给匿名函数,添加了一个默认参数,而python函数中的默认参数,是在python 解释器遇到def(i=i)或lambda 关键字时,就必须初始化默认参数,此时for i in range(4),每循环一次,匿名函数的默认参数i,就需要找一次i的引用,i=0时,第一个匿名函数的默认参数值就是0,i=1时,第二个匿名函数的默认参数值就是1,以此类推。

# 为了便于理解,你可以想象下multipliers内部是这样的(这个是伪代码只是为了理解):

def multipliers():
  return [lambda x,i=0: i*x, lambda x,i=1: i*x, lambda x,i=2: i*x, lambda x,i=3:i*x i=3]
# x的引用是2 所以output的结果就是:[0,2,4,6]

当然你的i=i,也可以改成a=i。

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

Python的延迟绑定其实就是只有当运行嵌套函数的时候,才会引用外部变量i,不运行的时候,并不是会去找i的值,这个就是第一个函数,为什么输出的结果是[6,6,6,6]的原因。

以上就是自己对于Python闭包和延迟绑定的理解。

这篇对Python闭包与延迟绑定的方法详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Python绑定方法与非绑定方法详解

    本文实例为大家分享了Python绑定方法与非绑定方法,供大家参考,具体内容如下 定义: 绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入): 1. 绑定到类的方法:用classmethod装饰器装饰的方法. 为类量身定制 类.boud_method(),自动将类当作第一个参数传入 (其实对象也可调用,但仍将类当作第一个参数传入) 2. 绑定到对象的方法:没有被任何装饰器装饰的方法. 为对象量身定制 对象.boud_method(),自动将对象当作第一个参数传入 (属于类的函数,类可以

  • 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中绑定与未绑定的类方法用法分析

    本文实例讲述了Python中绑定与未绑定的类方法.分享给大家供大家参考,具体如下: 像函数一样,Python中的类方法也是一种对象.由于既可以通过实例也可以通过类来访问方法,所以在Python里有两种风格: 未绑定的类方法:没有self 通过类来引用方法返回一个未绑定方法对象.要调用它,你必须显示地提供一个实例作为第一个参数. 绑定的实例方法:有self 通过实例访问方法返回一个绑定的方法对象.Python自动地给方法绑定一个实例,所以我们调用它时不用再传一个实例参数. 两种方法都是对象,它们可

  • python获取微信小程序手机号并绑定遇到的坑

    最近在做小程序开发,在其中也遇到了很多的坑,获取小程序的手机号并绑定就遇到了一个很傻的坑. 流程介绍 官方流程图 小程序使用方法 需要将 <button> 组件 open-type 的值设置为 getPhoneNumber,当用户点击并同意之后,可以通过 bindgetphonenumber 事件回调获取到微信服务器返回的加密数据, 然后在第三方服务端结合 session_key 以及 app_id 进行解密获取手机号. <button open-type="getPhoneN

  • python利用MethodType绑定方法到类示例代码

    前言 本文主要给大家介绍了关于python用MethodType绑定方法到类的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 对python中MethodType不熟悉的朋友们可以先看看这篇文章 问题引出 先看下面一段代码: from types import MethodType def set_age(self,age): self.age=age class Stu(object): pass Stu.set_age=MethodType(set_age,Stu)

  • python类的方法属性与方法属性的动态绑定代码详解

    动态语言与静态语言有很多不同,最大的特性之一就是可以实现动态的对类和实例进行修改,在Python中,我们创建了一个类后可以对实例和类绑定心的方法或者属性,实现动态绑定. 最近在学习python,纯粹是自己的兴趣爱好,然而并没有系统地看python编程书籍,觉得上面描述过于繁琐,在网站找了一些学习的网站,发现廖雪峰老师的网站上面的学习资源很不错,而且言简意赅,提取了一些python中的重要的语法和案例.重要的是可以在线测试python的运行代码,缺点就是没有系统的看python的书籍,不能及时的将

  • 详解python tkinter教程-事件绑定

    一个Tkinter主要跑在mainloop进程里.Events可能来自多个地方,比如按键,鼠标,或是系统事件. Tkinter提供了丰富的方法来处理这些事件.对于每一个控件Widget,你都可以为其绑定方法function. widget.bind(event,handler) 如果相应的event发生了,就会调用handler处理事件.举个例子: 捕获鼠标点击事件: from Tkinter import * root = Tk() def callback(event): print "cl

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

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

  • python爬虫之BeautifulSoup 使用select方法详解

    本文介绍了python爬虫之BeautifulSoup 使用select方法详解 ,分享给大家.具体如下: <html><head><title>The Dormouse's story</title></head> <body> <p class="title" name="dromouse"><b>The Dormouse's story</b></

  • 对python pandas 画移动平均线的方法详解

    数据文件 66001_.txt 内容格式: date,jz0,jz1,jz2,jz3,jz4,jz5 2012-12-28,0.9326,0.8835,1.0289,1.0027,1.1067,1.0023 2012-12-31,0.9435,0.8945,1.0435,1.0031,1.1229,1.0027 2013-01-04,0.9403,0.8898,1.0385,1.0032,1.1183,1.0030 ... ... pd_roll_mean1.py # -*- coding: u

  • 对python dataframe逻辑取值的方法详解

    我遇到的一个小需求,就是希望通过判断pandas dataframe中一列的值在两个条件范围(比如下面代码中所描述的逻辑,取小于u-3ε和大于u+3ε的值),然后取出dataframe中的所有符合条件的值,这个需求的解决与普通的iloc.loc.ix的方式不同,所以我想分享一下,希望可以帮到遇到这个困难的朋友们,下面是我的实例代码: doc[~((doc.iloc[:,141:142]<(mean_value-3*std_value))&(doc.iloc[:,141:142]>(me

  • 对Python获取屏幕截图的4种方法详解

    Python获取电脑截图有多种方式,具体如下: PIL中的ImageGrab模块 windows API PyQt pyautogui PIL中的ImageGrab模块 import time import numpy as np from PIL import ImageGrab img = ImageGrab.grab(bbox=(100, 161, 1141, 610)) img = np.array(img.getdata(), np.uint8).reshape(img.size[1]

  • 对Python的多进程锁的使用方法详解

    很多时候,我们需要在多个进程中同时写一个文件,如果不加锁机制,就会导致写文件错乱 这个时候,我们可以使用multiprocessing.Lock() 我一开始是这样使用的: import multiprocessing lock = multiprocessing.Lock() class MatchProcess(multiprocessing.Process): def __init__(self, threadId, mfile, lock): multiprocessing.Proces

  • 对Python之gzip文件读写的方法详解

    gzip文件读写的时候需要用到Python的gzip模块. 具体使用如下: # -*- coding: utf-8 -*- import gzip # 写文件 f_out = gzip.open("xxx.gz", "wb") # 读文件 # f_in = gzip.open("xxx.gz", "rb") for line in open("yyy.txt", "rb"): f_out

  • 对Python定时任务的启动和停止方法详解

    在python中我们可以使用APScheduler进行定时任务. APScheduler的具体编码这里就不介绍了.主要说下在终端中启动和停止任务. 一.运行计划任务的python脚本 如果我们在终端中直接执行的话,关闭终端窗口,Python任务就会中断,Python进程会被杀死,程序将停止运行.可以使用如下命令运行python脚本, python apschedulerscript.py & 这样执行后及时关闭终端窗口,程序依旧运行. 二.停止计划任务的Python脚本 如何停止呢,可使用如下方

  • 对python pandas读取剪贴板内容的方法详解

    我使用的Python3.5,32版本win764位系统,pandas0.19版本,使用df=pd.read_clipboard()的时候读不到数据,百度查找解决方法,找到了一个比较靠谱的 打开site-packages\pandas\io\clipboard.py 在 text = clipboard_get() 后面一行 加入这句: text = text.decode('UTF-8') 保存,然后就可以使用了 df=pd.read_clipboard() #变成正常的了 下次可以在其他地方复

  • python pandas修改列属性的方法详解

    使用astype如下: df[[column]] = df[[column]].astype(type) type即int.float等类型. 示例: import pandas as pd data = pd.DataFrame([[1, "2"], [2, "2"]]) data.columns = ["one", "two"] print(data) # 当前类型 print("----\n修改前类型:&quo

随机推荐