参考链接: Python闭包

这里根据我们平常见到的一些Python常见面试题来跟大家说一下关于Python闭包的相关问题!

题目:

1.什么是闭包?闭包的定义?

2.手写一个简单的闭包

3.变量作用域规则与 nonlocal 关键字?

4.闭包的应用

答案要点:

1.首先,我们要了解变量作用域

# 示例一

def test1(a):

print(a)

print(b) # 当函数执行到这一步时会报错。

# NameError: global name 'b' is not defined

test1(1)

# 示例二

b = 6

def test2(a):

print(a)

print(b)

test2(1) # 1 6

# 示例三

b = 6

def test3(a):

print(a)

print(b) # 当函数执行到这一步仍然会报错

# UnboundLocalError: local variable 'b' referenced before assignment

b = 9 # 比示例二多了一行赋值

test3(1)

学过其他语言,比如 Java ,对示例三的结果会比较惊讶,在 Java 中类似的情况,不会报错,会引用外部的全局变量,而如果在内部重新赋值后,再次使用则会用局部变量的值。而在 Python 中情况则不一样,它在编译函数时,发现对 b 有赋值的操作,它判定 b 是一个局部变量,所以在打印 b 时,它会去查询局部变量b,发现并没有赋值,所以会抛出异常。

引用《流畅的Python》中对此的解释:

这不是缺陷,而是设计选择:Python 不要求声明变量,但是假定在函数定义体中赋值的变量是局部变量。这比 JavaScript 的行为要好多了,JavaScript 也不要求声明变量,但是如果忘记把变量声明为局部变量(使用var),可能会在不知情的情况下获取全局部变量。

上段话第一次看可能会有点不明白,其实简单来说,Python 就是这样设计的,它认为在函数体中,如果对变量有赋值操作,则证明这个变量是一个局部变量,并且它只会从局部变量中去读取数据。这样设计可以避免我们在不知道的情况下,获取到全局变量的值,从而导致一些错误数据的出现。

至于解决方法,就是使用 global 关键字,来说明我们使用的是 全局变量 。示例如下:

b = 6

def test4(a):

print(a)

global b # 1

print(b) # 6

b = 9

print(b) # 9

test4(1)

2.闭包的定义:简单来说,闭包的概念就是当我们在函数内定义一个函数时,这个内部函数使用了外部函数的临时变量,且外部函数的返回值是内部函数的引用时,我们称之为闭包。有点绕

代码如下:

# 一个简单的实现计算平均值的代码

def get_avg():

scores = [] # 外部临时变量

def inner_count_avg(val): # 内部函数,用于计算平均值

scores.append(val) # 使用外部函数的临时变量

return sum(scores) / len(scores) # 返回计算出的平均值

return inner_count_avg # 外部函数返回内部函数引用

avg = get_avg()

print(avg(10)) # 10

print(avg(11)) # 10.5

...

3.nonlocal 关键字。上面的代码,有一个小缺陷,有很多重复的计算,当我们传入一个新的值想要得到新的平均值时,其他前一次的总和是可以通过外部临时变量存储的。于是我们很自然的想到下面的代码:

# 一个简单的实现计算平均值的代码改进版一

def get_avg():

scores = 0 # 将外部临时变量由 list 改为一个 整型数值

count = 0 # 同时新增一个变量,记录个数

def inner_count_avg(val): # 内部函数,用于计算平均值

scores += val # 使用外部函数的临时变量

count += 1

return scores / count # 返回计算出的平均值

return inner_count_avg # 外部函数返回内部函数引用

avg = get_avg()

print(avg(10)) # 报错

...

这里报错的原因,请看第 1 点:变量的作用规则。因为 scores += val ,其实就是 scores = scores + val,有了赋值操作,则认为 scores 是局部变量了。而我们也没办法使用 global 关键字,因为此时 scores 和 count 是定义在 get_ave 函数内的,它们俩也是一个局部变量。而为什么我们使用 list 时,没有出现这个问题呢?也是很好理解的,因为我们使用的是 list.append() 方法,它没有赋值操作。你可以简单认为,可变对象(即我们可以通过调用自身一些方法去做增删改操作且变量地址值不变)不存在此问题,而不可变对象则会有。

在 Python 3 中引入了一个关键词 nonlocal 解决了这一个问题:

# 一个简单的实现计算平均值的代码改进版二

def get_avg():

scores = 0 # 将外部临时变量由 list 改为一个 整型数值

count = 0 # 同时新增一个变量,记录个数

def inner_count_avg(val): # 内部函数,用于计算平均值

nonlocal count, scores

scores += val # 使用外部函数的临时变量

count += 1

return scores / count # 返回计算出的平均值

return inner_count_avg # 外部函数返回内部函数引用

avg = get_avg()

print(avg(10)) # 报错

你也许会说,那在 Python 2 的环境下应该怎么解决呢?emm~其实呢 也是有办法的,思路就是将不可变对象变为可变对象即可。具体代码如下:

# -*- coding:utf-8 -*-

class Score:

pass

def get_avg():

s = Score() # 使用类对象

s.scores = 0.0 # 注意 Python 2 中整数除法是舍弃小数的,所以要定义为浮点数

s.count = 0

def inner_get_avg(val):

s.count += 1

s.scores += val

return s.scores / s.count

return inner_get_avg

avg = get_avg()

print(avg(10)) # 10.0

print(avg(11)) # 10.5

4.闭包的应用:首先是装饰器,装饰器就是通过修改被装饰函数,来达到增加新功能的作用。当我们在内部函数去修改被装饰函数时,大部分情况都会使用到闭包。简单示例:

def decorator(func): # 外部函数的局部变量 func

def wrapper(*args, **kwargs): # 接受被包装函数传入过来的参数

return func(*args, **kwargs) # 使用外部函数的局部变量 func

return wrapper

@decorator

def basic_func(name):

print 'my name is', name

# 等价于

decorator_func(func)

另外一个应用由之前求平均值的示例也可以看出来,可以在重复计算时提高效率。其次还有一个比较重要的应用场景,就是利用“惰性求值”这一特性,这一点在 Django 的 QuerySet 里有体现。当我们利用 ORM 去做 SQL 查询时,很多时候会根据不同的判断条件,去加 filter,加 filter 的时候并没有真正做查询,在最终获取结果的时候才真正执行了查询。

这里也是根据我们平常见到的一些Python常见面试题来跟大家讲的Python闭包,有补充的伙伴非常欢迎留言补充哈!也希望大家能够互相学习,取长补短,进步呀!

[转载] 根据Python常见面试题来谈谈 Python 闭包相关推荐

  1. 【python常见面试题】之python 中对list去重的多种方法

    在python相关职位的面试过程中,会对列表list的去重进行考察.(注意有时会要求保证去重的顺序性) 1.直观方法 1 li=[1,2,3,4,5,1,2,3] 2 new_li=[] 3 for ...

  2. Python常见面试题:TCP 协议中的三次握手与四次挥手相关概念详解

    今天来聊聊Python常见面试题中面试频率特别高的一个题目:TCP 协议中的三次握手与四次挥手. 涉及到的知识点有: 1.TCP.UDP 协议的区别 2.TCP 头部结构 3.三次握手与四次挥手过程详 ...

  3. python作用域排序_11道Python常见面试题,80%的人不会

    原标题:11道Python常见面试题,80%的人不会 1.final作用域的代码一定会被执行吗? 正常的情况下,finally作用域的代码一定会被执行的,不管是否发生异常.哪怕是调用了sys.exit ...

  4. 分享 Python 常见面试题及答案(下)

    程序IT圈 www.cxyquan.com 优秀程序猿技术公众号 之前分享了一篇很全的Java的面试题 分享 Java 常见面试题及答案(上) 分享 Java 常见面试题及答案(下) 由于篇幅过长,这 ...

  5. 测试工程师python常见面试题_测试人员python面试题

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 这条路会很曲折,你也会一度认为是不是自己选错了,但只要坚持,就算最后没有成功,但 ...

  6. 315道Python常见面试题

    第一部分,Python基础篇 为什么学习Python? 通过什么途径学习的Python? Python和Java.PHP.C.C#.C++等其他语言的对比? 简述解释型和编译型编程语言? Python ...

  7. Python 常见面试题

    1.Python 中的列表和元组有什么区别? 列表 list 是可变的,即可以编辑,同时运行比元组慢 元组 tuple 是不可变(元组是无法编辑的列表) 2.Python 的主要功能是什么? Pyth ...

  8. gil 简述_求职面试常见问题:Python常见面试题全解析附答案

    Python是个非常受欢迎的编程语言,随着近些年机器学习.云计算等技术的发展,Python的职位需求越来越高.我收集了110道Python面试官经常问的问题,供大家参考学习.篇幅问题,分为2次推送,下 ...

  9. python常见面试题基础部分

    基础 编程题 1.优化一下下面的程序result = []for x in range(10):result.append(x ** 2)print(result) 答案: result = [ (x ...

最新文章

  1. intouch负值显示0_excel 应用中计算结果显示为负数,使负数显示为0应如何操作?...
  2. VS2008 各种杂七杂八技巧
  3. nginx利用referer指令实现防盗链配置
  4. openoffice转换过程中遇到繁体字文档转换失败的问题
  5. 计算机网络项目——最小网元设计(阶段三)
  6. Silverlight 游戏开发小技巧:技能冷却效果1(Cooldown)
  7. java 元祖_在java中对元组列表进行排序的有效方法
  8. oracle同sql中isnull无法,SQL中的ISNULL函数使用介绍
  9. vue json对象转数组
  10. hash素数表(备用)
  11. 生成专题2 | 图像生成评价指标FID
  12. Captain Flint and Crew Recruitment
  13. css改变水平线的颜色
  14. chrome的视频播放加速功能也太好用了。
  15. 宣传册打印选择哪种纸张
  16. OpenCV中关于各种颜色的定义以及RGB值
  17. 震撼心灵、洗礼灵魂--【经典的大师参禅的禅语】
  18. 郑州73中学计算机老师,关于“郑州市中学信息技术优质课评比”的通知
  19. 树莓派+摄像头实现对移动物体的检测
  20. 【数据结构】_树与二叉树

热门文章

  1. msclass 文字滚动_MSClass (通用不间断滚动JS封装类)
  2. ubuntu下c 调用java_ubuntu下使用JNI Java调用C++的例子
  3. 循环遍历java属性_java中循环遍历实体类的属性和数据类型以及属性值
  4. JavaScript数组中新增元素(2)
  5. OO’s Sequence
  6. 取文字_玉镯取不出来了怎么办?教你6种最有效的方法
  7. swagger -- 前后端分离的API接口
  8. bzoj 1082: [SCOI2005]栅栏(二分+DFS)
  9. Wannafly模拟赛2: A. Contest(Cdq分治)
  10. 2017百度之星初赛:A-1001. 小C的倍数问题