认识Python中的闭包:闭包入门到自闭

Python装饰器:从原理到工业级应用实践 装饰器是Python中基于闭包实现的语法糖,通过@符号为函数动态添加功能而不修改原代码。其核心原理是利用函数作为一等对象的特性,通过闭包保持函数状态。在工程实践中,装饰器广泛应用于性能监控、权限校验、缓存优化等场景,能有效实现代码复用和解耦。本文通过计时装饰器、重试机制等典型案例,展示如何利用functools.wraps保持函数元信息,避免常见陷阱。对于Web开发中的接口监控和权限管理,装饰器模式比传统条件判断更符合单一职责原则。在Django/Flask等框架中,类装饰器还能实现批量注册等高级功能。需要 阅读详情

前言

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。

PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取

python免费学习资料、代码以及交流解答点击即可加入


python中什么是闭包?闭包有什么用?为什么要用闭包?今天我们就带着这3个问题来一步一步认识闭包。

闭包和函数紧密联系在一起,介绍闭包前有必要先介绍一些背景知识,诸如嵌套函数、变量的作用域等概念。

1. 作用域

作用域是程序运行时变量可被访问的范围,定义在函数内的变量是局部变量,局部变量的作用范围只能是函数内部范围内,它不能在函数外引用。

定义在模块最外层的变量是全局变量,它是全局范围内可见的,当然在函数里面也可以读取到全局变量的。而在函数外部则不可以访问局部变量。例如:

a = 1 
def foo(): 
   print(a) # 1 
def foo(): 
    print(a) # NameError: name 'num' is not defined 

2. 嵌套函数

函数不仅可以定义在模块的最外层,还可以定义在另外一个函数的内部,像这种定义在函数里面的函数称之为嵌套函数(nested function)。对于嵌套函数,它可以访问到其外层作用域中声明的非局部(non-local)变量,比如代码示例中的变量a可以被嵌套函数printer 正常访问。

def foo(): 
   #foo是外围函数 
   a = 1 
   # printer是嵌套函数 
   def printer(): 
       print(a)
   printer() 
foo() # 1

那么有没有一种可能即使脱离了函数本身的作用范围,局部变量还可以被访问得到呢?

答案就是闭包!

我们将上述函数改成高阶函数(接受函数为参数,或者把函数作为结果返回的函数是高阶函数)的写法。

def foo(): 
   #foo是外围函数 
   a = 1 
   # printer是嵌套函数 
   def printer(): 
       print(a)
   return printer
x = foo() 
x() # 1

这段代码和前面例子的效果完全一样,同样输出 1。不同的地方在于内部函数 printer直接作为返回值返回了。

一般情况下,函数中的局部变量仅在函数的执行期间可用,一旦 foo() 执行过后,我们会认为变量a将不再可用。然而,在这里我们发现 foo 执行完之后,在调用x 的时候a变量的值正常输出了,这就是闭包的作用,闭包使得局部变量在函数外被访问成为可能。

3. 闭包

人们有时会把闭包和匿名函数弄混。这是有历史原因的:在函数内部定义函数 不常见,直到开始使用匿名函数才会这样做。而且,只有涉及嵌套函数时才有闭包问题。因此,很多人是同时知道这两个概念的。

其实,闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的 非全局变量。函数是不是匿名的没有关系,关键是它能访问定义体之外定义的非全局变量。

通俗来讲闭包,顾名思义,就是一个封闭的包裹,里面包裹着自由变量,就像在类里面定义的属性值一样,自由变量的可见范围随同包裹,哪里可以访问到这个包裹,哪里就可以访问到这个自由变量。那这个包裹是绑定在哪的呢?在上文代码追加一句打印:

 def foo():
       # foo是外围函数
       a = 1
       # printer是嵌套函数
       def printer():
           print(a)
       return printer
x = foo()
print(x.__closure__[0].cell_contents) # 1 

可以发现是在函数对象的__ closure __属性中,__ closure __是一个元祖对象函数负责闭包绑定,即自由变量的绑定。该属性值通常是None,如果这个函数是一个闭包的话,那么它返回的是一个由cell 对象组成的元组对象。cell 对象的cell_contents 属性就是闭包中的自由变量。这解释了为什么局部变量脱离函数之后,还可以在函数之外被访问的原因的,因为它存储在了闭包的 cell_contents中了。

4. 闭包的好处

闭包避免了使用全局变量,此外,闭包允许将函数与其所操作的某些数据(环境)关连起来。这一点与面向对象编程是非常类似的,在面对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。

一般来说,当对象中只有一个方法时,这时使用闭包是更好的选择。来看一个计算均值的例子,假如有个名为avg 的函数,它的作用是计算不断增加的系列值的均值;例如,整个历史中 某个商品的平均收盘价。每天都会增加新价格,因此平均值要考虑至目前为止所有的价格,如下所示:

>>> avg(10) #10.0 
>>> avg(11) #10.5 
>>> avg(12) #11.0 

在以往,我们可以设计一个类:

class Averager():

def __init__(self):
   self.series = []
    
def __call__(self, new_value):
   self.series.append(new_value)
   total = sum(self.series)
   return total/len(self.series)
   
avg = Averager()
avg(10) #10.0
avg(11) #10.5
avg(12) #11.0

这时候我们使用闭包来实现。

def make_averager():
   series = []
   def averager(new_value):
       series.append(new_value)
       total = sum(series)
       return total/len(series)
   return averager

avg = make_averager()
avg(10) #10.0
avg(11) #10.5
avg(12) #11.0

调用make_averager 时,返回一个 averager 函数对象。每次调用 averager 时,它会把参数添加到列表中,然后计算当前平均值。这比用类来实现更优雅,此外装饰器也是基于闭包的一中应用场景。

5. 闭包的坑

看了上述闭包的解释你以为闭包也不过如此?实际使用中往往在不经意间就会掉入陷阱,看看下面的例子:

def create_multipliers():
   return [lambda x: x * i for i in range(5)]
    
for multiplier in create_multipliers():
   print(multiplier(2))
    
# 期望输出0, 2, 4, 6, 8
# 结果是 8, 8, 8, 8, 8

我们期望是输出0, 2, 4, 6, 8。结果却是8, 8, 8, 8, 8。为什么会出现这问题呢?让我们改下代码:

def create_multipliers():
   multipliers = [lambda x: x * i for i in range(5)]
   print([m.__closure__[0].cell_contents for m in multipliers])
        
create_multipliers()  # [4, 4, 4, 4, 4] 

可以看到函数绑定的i值都成了4即循环后最终i的取值,这是因为Python的闭包是延迟绑定 ,这意味着闭包中用到的变量的值,是在内部函数被调用时查询得到的。

正确的使用方式是将i的值利用参数的方式进行传递:


def create_multipliers():
   return [lambda x,i=i: x * i for i in range(5)]
 
s = create_multipliers()
for multiplier in s:
   print(multiplier(2))  # 0, 2, 4, 6, 8

我们利用默认参数来传递i,同闭包一样默认参数是绑定在__ defaults __属性上。

print([f.__defaults__ for f in s]) # [(0,), (1,), (2,), (3,), (4,)] 
Python 作用域与 UnboundLocalError 排查改法 Python 的变量作用域与名称解析是每个开发者都会遇到的基础概念。LEGB 规则决定了名字的读取顺序,而编译期符号表会扫描整个函数体,只要出现赋值、循环目标、with as、except as、import、del 等绑定操作,该名字就会被判定为局部变量。运行时读取局部变量使用 LOAD_FAST,直接访问当前帧槽位,不会回退到全局命名空间。这种设计提升了局部变量访问速度,也让函数行为更可预测。但在函数内修改全局计数、闭包内累加状态、条件分支或循环体未执行、try 块中赋值被异常打断时,就会触发 Unbo 阅读详情

相关推荐

Python作用域与LEGB规则详解

在编程语言中,作用域决定了变量的可见性和生命周期。Python采用独特的LEGB(Local局部→Enclosing闭包→Global全局→Built-in内置)作用域规则,这种设计既实现了代码封装性,又支持闭包等高级特性。理解作用域机制对编写可维护代码至关重要,特别是在处理全局变量修改、闭包陷阱等问题时。通过locals()和globals()函数可以探查不同作用域的变量,而nonlocal和global关键字则用于跨作用域变量修改。在实际开发中,合理运用作用域规则能有效避免变量污染、内存泄漏等问题,同时

weixin_33178839的博客 165

lambda表达式与for循环的搭配用法

lambda表达式与for循环的搭配用法 开篇-遇到的问题 给出一个值 x ,分别计算 x 与 0~5 之间各个数的和 # 给出一个值 x ,分别计算 x 与 0~5 之间各个数的和 res=[lambda x,i=i:x+i for i in range (6)] print(res) #得到返回 x+i 函数的五个地址 for f in res: #循环遍历每个地址 print(f(2)) #对每个遍历到的函数 传递参数 x=2 #2 3 4 5 6 7 问题一:l

韩的博客 2万+

Python链表实战:何时必须手写而非用list

链表是一种基础线性数据结构,其核心在于通过指针链接实现动态内存布局与确定性O(1)插入/删除行为。相比Python列表(底层为动态数组),链表在头部高频写入、流式处理、内存受限等场景具备不可替代的性能优势——尤其当操作集中在链首且需规避数组扩容抖动与内存拷贝时。它天然适配FIFO消息缓冲、实时日志、嵌入式缓存等‘只进不出、顺序消费’类应用。本文聚焦链表的本质行为契约、三种变体选型逻辑、Python特有陷阱(引用环、迭代器崩溃、闭包泄漏)及工业级增强(节点池、线程安全、序列化),结合真实生产案例揭示:链表不是

weixin_30660027的博客 319

python--杂识--理解[lambda x: x*i for i in range(4)]

1. 题目: lst = [lambda x: x*i for i in range(4)] res = [m(2) for m in lst] print(res) """ 运行结果: [6, 6, 6, 6] Process finished with exit code 0 """ 理解:题目中得代码相当于以下代码 fun_list = [] for i in range(4): def foo(x): return x*i fun_list.append(fo

Chasing__Dreams的博客 1176

Windows Server上OpenVPN 2.4.8双因子认证实战:账号密码+证书,附一键脚本

本文详细介绍了在Windows Server上部署OpenVPN 2.4.8双因子认证(账号密码+证书)的实战方案,包括架构设计、证书体系规划、环境准备、服务器端配置及客户端自动化部署。通过一键脚本和优化配置,提升企业VPN的安全性和运维效率,特别适合金融、医疗等对安全性要求高的行业。

weixin_29231221的博客 465

高频功率放大器的设计实现

摘 要 随着无线通信技术的高速发展,市场对射频电路的需求越来越大,同时对射频电路的性能要求也越来越高。高频功率放大器是位于无线发射级末端的重要部件,为了弥补信号在无线传输过程中的衰耗,要求发射机具有较大功率的输出,通信距离越远,要求的输出功率越大。在高频范围内,为了获得足够大的高频输出功率,就要采用高频功率放大器。 本次课程设计的高频功率放大器的技术指标包括输出功率、效率、功率增益、由于高频频功率放大器的输出功率比较大,耗能比较多,所以工作效率就显得比较重要。本课程设计的高频功率放大器电路由两极功率放大器

qq_43543515的博客 3万+

[lambda x: x*i for i in range(4)] 详细解析+LEGB规则 闭包原理

转自:http://www.cnblogs.com/shiqi17/p/9608195.html 一、问题描述 fun = [lambda x: x*i for i in range(4)] for item in fun: print(item(1)) 上述式子的输出结果: 预计结果为:0, 2, 4, 6 实际输出为:3, 3, 3, 3 原理:i 在外层作用域 lambd...

Miss_Audrey的博客 1万+

线性回归LinearRegression

对视频中线性回归算法的相关内容进行总结与记录 文章目录一、理论基础二、实例2.1 一元线性回归2.1.1 推导过程2.1.2 代码实现(基于numpy)2.1.3 代码实现(基于sklearn)2.2 多元线性回归2.2.1 代码实现(基于numpy)2.2.2 代码实现(基于sklearn) 一、理论基础 回归 通常是指利用某个函数,尽可能把数据样本点“串”在一起,用于描述输入变量和输出变量间的变化关系 线性回归 特点:用来把数据“串”起来的那个函数是线性的 (一元线性回归(一个自变量) &a.

邱邱邱的博客 1068

Python闭包相关问题

1 >>> def create_multipliers(): 2 ... return [lambda x: i*x for i in range(5)] 3 ... 4 >>> for multiplier in create_multipliers(): 5 ... print multiplier(2) 6 ... 7...

weixin_30627381的博客 113

我的宠物狗学编程:从零开始的代码之旅

在这个示例中,我们定义了一个名为"interact"的函数,它通过输入函数获取用户的输入,并根据输入执行相应的代码块。在编程中,我们使用条件语句来根据不同的条件执行不同的代码块。对于我的狗狗,我将创建一个名为"feed"的函数来表示它的进食行为,并根据不同的条件输出不同的信息。在这个示例中,我们定义了一个名为"feed"的函数,并使用一个参数"food"来表示食物。对于我的狗狗,我将创建一个名为"dog_name"的变量来存储它的名字。对于我的狗狗,我将创建一个名为"bark"的函数来表示它的叫声。

2301_79331387的博客 333

华为od 面试八股文_JAVA语言_01_含答案

华为od 面试八股文_JAVA语言_01_含答案

MISAYAONE的博客 4311

Python系列之闭包

什么是闭包 闭包(closure)是函数式编程的重要的语法结构。函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式)。在面向过程编程中,我们见到过函数(function);在面向对象编程中,我们见过对象(object)。函数和对象的根本目的是以某种逻辑方式组织代码,并提高代码的可重复使用性(reusability)。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。2...

songrenqing 335

数据分析案例-基于红米和华为手机的用户评论分析

在智能手机市场日益繁荣的今天,红米与华为手机作为国产手机品牌的佼佼者,凭借其各自独特的品牌魅力和技术实力,赢得了广泛的用户群体和高度关注。随着技术的不断进步和消费者需求的日益多样化,用户对手机的期望已不仅限于基本的通讯功能,更涵盖了性能、拍照、续航、用户体验等多个方面。因此,深入探究红米与华为手机在用户长期使用过程中的实际表现,尤其是用户评论中所反映出的真实反馈,对于理解市场需求、优化产品设计及提升用户体验具有重要意义。本实验的背景正是基于这样的市场环境和技术发展趋势。我们旨在通过收集并分析红米与华为手机的

m0_64336780的博客 1万+

python中的闭包有什么用_认识Python中的闭包:闭包入门到自闭

本文首发于:行者AIpython中什么是闭包?闭包有什么用?为什么要用闭包?今天我们就带着这3个问题来一步一步认识闭包。闭包和函数紧密联系在一起,介绍闭包前有必要先介绍一些背景知识,诸如嵌套函数、变量的作用域等概念。1. 作用域作用域是程序运行时变量可被访问的范围,定义在函数内的变量是局部变量,局部变量的作用范围只能是函数内部范围内,它不能在函数外引用。定义在模块最外层的变量是全局变量,它是全局范...

weixin_29197835的博客 475

Stable Diffusion WebUI 安装前准备

大家好,我是水滴~~在安装 stable-diffusion-webui 前需要做一些准备工作,本篇文章主要介绍需要的电脑配置、魔法、安装 Python、安装 Git 等。

水滴的博客 2100

【用unity实现100个游戏之2】复刻实现经典2d平台跳跃游戏《蔚蓝 Celeste》(附工程源码)

蔚蓝》是一款备受好评的独立游戏,由游戏开发工作室开发并由发行。这款游戏于2016年首次发布,并在玩家和评论界收获了广泛赞誉。蔚蓝以其独特的艺术风格和引人入胜的故事而著称。游戏以深海为背景,玩家将扮演一个能够探索水下世界的神秘生物。游戏通过惊人的视觉表现和动态的海洋生态系统为玩家带来了令人难忘的游戏体验。无论是广阔的水下景观还是充满生机的海洋生物,蔚蓝都让玩家感受到深海的神秘和美丽。蔚蓝在玩法上注重探索和解谜。玩家可以在海底环境中自由游动,发现隐藏的地点和秘密,与各种生物互动,并解决环境谜题以继续前进。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 4167

从入门到自闭之Python闭包

闭包 定义:在嵌套函数内,使用(非本层变量)非全局变量就是闭包 闭包必须是内层函数对外层函数的变量(非全局变量)的引用 函数执行完毕后,函数体内的空间自行销毁 def func(): a=1 def foo(): print(a) print(foo.__closure__) #判断是不是闭包,返回None就不是闭包 func() 作用: 保护数据的安全性 装饰...

209

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

从入门到自闭之Python序列化

序列化(背) json :将数据类型转换成字符串(序列化),将字符串装换成原数据类型(反序列),支持dict,list,tuple等,序列后都变成了列表 dumps,loads ------ 用于网络传输 json.dumps:将数据类型转换成字符串 json.loads:将字符串转换成原数据类型 dump,load ------ 用于文件传输 json.dump:一个load...

213

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

python闭包学习笔记补充及其复习

闭包学习闭包的定义闭包实际使用闭包的应用 自闭式学习 我太菜了QAQ 闭包的定义 1.在函数里面定义一个变量,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包 def test(number): def test_in(number_in): print("in test_in 函数,number_in is %d"number_in) return numb...

m0_38116154的博客 206

python-day13(正式学习)

闭包函数 闭包 闭包:闭是封闭(函数内部函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用)。闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。 额。。。这里提示一下闭包!=自闭 为函数传参的两种方式 使用参数的方式 def func(x): print(x) func(1) func(1) func(1) 1 1 1 包给函数 def outter...

Wjx510010104的博客 134

Python匿名函数变量绑定陷阱与解决方案:从原理到实战

Python匿名函数变量绑定陷阱与解决方案:从原理到实战

KE17RS的博客 925

Python装饰器:从原理到实战应用

装饰器是Python中一种强大的高阶函数技术,通过@语法糖实现函数行为的动态扩展。其核心原理基于闭包机制,允许在不修改原函数代码的情况下添加新功能。在工程实践中,装饰器常用于实现权限验证、性能监控、缓存优化等横切关注点。标准库中的functools.wraps解决了装饰器导致的元信息丢失问题,而带参数装饰器则提供了更灵活的配置方式。在Web开发领域,Flask的路由系统和Django的权限控制都深度依赖装饰器实现。掌握装饰器技术能显著提升代码复用率和可维护性,是Python开发者进阶的必备技能。

weixin_32308101的博客 198

ESTree 已弃用节点规范全解:SpiderMonkey 遗留扩展与 E4X 的 AST 定义

本篇指南完整解读 ESTree 仓库中的 [deprecated.md](https://link.gitcode.com/i/2f5564e99d1622915bb8403f74884654) 规范文档。该文档定义了曾由 Mozilla SpiderMonkey 引擎解析器支持、但现已废弃的 ESTree API 扩展节点,涵盖表达式闭包、`let` 语句/表达式、数组推导式与生成器表达式、锐利

gitblog_00810的博客 89

Python装饰器:原理、应用与性能优化

装饰器是Python中基于高阶函数和闭包实现的语法糖,它遵循开闭原则,允许在不修改原函数代码的情况下动态扩展功能。从技术原理看,装饰器本质上是接收函数对象作为参数的可调用对象,通过@语法糖实现优雅的函数包装。在工程实践中,装饰器广泛应用于日志记录、权限控制、性能监控等横切关注点,同时也在Web框架(如Flask路由)、缓存优化(如lru_cache)等场景发挥关键作用。理解装饰器的执行时序、元信息保留技巧以及带参数装饰器的实现方式,是掌握这一Python高级特性的关键。

weixin_33507732的博客 81
上一篇: 用Python爬取王冰冰vlog弹幕并制作词云
下一篇: Python基础----函数基础
酸菜鱼编程
博客等级 码龄6年 155粉丝 63原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值