深入浅出讲解python闭包

一、定义

在 Python 中,当一个函数内部定义的函数引用了外部函数的局部变量时,就形成了一个闭包。这个内部函数可以访问并修改外部函数的局部变量,而这些局部变量的状态会一直被保存在闭包中,即使外部函数已经执行完毕。

这种机制使得闭包可以实现一些特殊的功能,例如记忆化(Memoization)和实现私有变量等。闭包可以在函数内部保存一些状态,并且这些状态对外部是不可见的,从而实现了一定程度上的信息隐藏和封装。

def outer_function():
    x = 10
    def inner_function():
        nonlocal x  # 声明使用外部函数的局部变量 x
        x += 5
        return x
    return inner_function
​
closure = outer_function()
print(closure())  # 输出结果为 15
print(closure())  # 输出结果为 20

在这个示例中,inner_function 就是一个闭包,它引用了外部函数 outer_function 中的局部变量 x。每次调用 closure() 都会修改并返回 x 的值,而这个状态是被保存在闭包中的。


二、通过一个例子全面剖析一下闭包这个概念。

需求:实现银行系统的余额变化。

方式一:全局变量+函数

balance = 1000
def deposit(amount):
    global balance
    balance += amount
    print(f"成功存入 {amount} 元,当前余额为 {balance} 元")
​
def withdraw(amount):
    global balance
    if amount <= balance:
        balance -= amount
        print(f"成功取出 {amount} 元,当前余额为 {balance} 元")
    else:
        print("余额不足,取款失败")
​
def check_balance():
    print(f"当前余额为 {balance} 元")
​
# 存款和取款操作
deposit(500)  # 存入 500 元
withdraw(200)  # 取出 200 元
balance=10
deposit(500)  # 存入 500 元
check_balance()  # 查看余额

结果

成功存入 500 元,当前余额为 1500 元

成功取出 200 元,当前余额为 1300 元

成功存入 500 元,当前余额为 510 元

当前余额为 510 元

缺点:全局变量不安全,可以被随意访问和修改。

使用全局变量的方式虽然可以实现功能,但存在一些潜在问题:

  1. 可变性:全局变量的值是可变的,任何函数都可以直接修改它,这增加了程序出错的可能性,尤其在大型程序中更容易出现问题。

  2. 可见性:全局变量对整个程序都是可见的,这意味着任何部分都可以修改它,从而导致程序行为难以预测。

  3. 扩展性:如果需要管理多个账户,全局变量的方式就显得力不从心,因为很难将多个账户的信息独立地封装起来。


方案二:类

class BankAccount:
    def __init__(self, initial_balance):
        self.balance = initial_balance
​
    def deposit(self, amount):
        self.balance += amount
        print(f"成功存入 {amount} 元,当前余额为 {self.balance} 元")
​
    def withdraw(self, amount):
        if amount <= self.balance:
            self.balance -= amount
            print(f"成功取出 {amount} 元,当前余额为 {self.balance} 元")
        else:
            print("余额不足,取款失败")
​
    def check_balance(self):
        print(f"当前余额为 {self.balance} 元")
​
# 创建账户
account = BankAccount(1000)
account.balance=550
# 存款和取款操作
account.deposit(500)  # 存入 500 元
account.withdraw(200)  # 取出 200 元
account.check_balance()  # 查看余额

结果

成功存入 500 元,当前余额为 1050 元

成功取出 200 元,当前余额为 850 元

当前余额为 850 元

缺点:共有属性也能被对象访问修改,不安全。


方案三:类+私有属性

class BankAccount:
    def __init__(self, initial_balance):
        self.__balance = initial_balance
​
    def deposit(self, amount):
        self.__balance += amount
        print(f"成功存入 {amount} 元,当前余额为 {self.__balance} 元")
​
    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
            print(f"成功取出 {amount} 元,当前余额为 {self.__balance} 元")
        else:
            print("余额不足,取款失败")
​
    def check_balance(self):
        print(f"当前余额为 {self.__balance} 元")
​
account = BankAccount(1000)
account.deposit(500)
​
account.withdraw(200)
account.check_balance()

结果:

成功存入 500 元,当前余额为 1500 元

成功取出 200 元,当前余额为 1300 元

当前余额为 1300 元

问题得到解决。


方案四:闭包

def create_account(initial_balance):
    balance = initial_balance
    def deposit(amount):
        nonlocal balance
        balance += amount
        print(f"成功存入 {amount} 元,当前余额为 {balance} 元")
​
    def withdraw(amount):
        nonlocal balance
        if amount <= balance:
            balance -= amount
            print(f"成功取出 {amount} 元,当前余额为 {balance} 元")
        else:
            print("余额不足,取款失败")
​
    def check_balance():
        print(f"当前余额为 {balance} 元")
​
    return deposit, withdraw, check_balance
​
# 创建账户
deposit, withdraw, check_balance = create_account(1000)
​
# 存款和取款操作
deposit(500)  # 存入 500 元
print(deposit)
withdraw(200)  # 取出 200 元
check_balance()  # 查看余额
​
# 创建账户
deposit1, withdraw1, check_balance1 = create_account(10000)
# 存款和取款操作
deposit1(500)  # 存入 500 元
print(deposit1)
withdraw1(200)  # 取出 200 元
check_balance1()  # 查看余额

结果

成功存入 500 元,当前余额为 1500 元

<function create_account.<locals>.deposit at 0x0000020DCC711990>

成功取出 200 元,当前余额为 1300 元

当前余额为 1300 元

成功存入 500 元,当前余额为 10500 元

<function create_account.<locals>.deposit at 0x0000020DCC711A20>

成功取出 200 元,当前余额为 10300 元

当前余额为 10300 元

完美解决了问题


三、辨析

闭包和类是两种不同的概念,它们在编程中有着不同的用途和特点。

闭包(Closure)是指可以在其词法作用域之外执行的函数,但仍然保持对其作用域内变量的引用。换句话说,闭包是函数及其相关的引用环境的组合。闭包可以用来封装状态、实现私有变量等功能。在 Python 中,当一个函数内部定义的函数引用了外部函数的局部变量时,就形成了一个闭包。

类(Class)则是面向对象编程中的重要概念,它用来描述具有相似属性和行为的对象的模板。类由属性(成员变量)和方法(成员函数)组成,可以通过实例化来创建对象,并且支持继承、多态等面向对象的特性。类的主要作用是封装数据和操作数据的方法,以及实现代码复用和抽象。

下面是闭包和类的一些区别:

  1. 封装方式不同:闭包是一种函数式编程的封装方式,通过函数和其引用环境来封装状态和行为;类是一种面向对象编程的封装方式,通过属性和方法来封装数据和操作。

  2. 状态的保存方式不同:闭包通过引用环境来保存状态,而类通过实例变量和类变量来保存状态。

  3. 范围不同:闭包通常用于封装一些局部状态,提供函数式编程的功能;类则通常用于描述对象的行为和属性,提供面向对象编程的特性。

总的来说,闭包和类都是用于封装和抽象的工具,但其应用场景和实现方式有所不同。在实际编程中,可以根据具体的需求和问题选择合适的工具来实现相应的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/167844.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在python中分别利用numpy,tensorflow,pytorch实现数据的增加维度(升维),减少维度(降维)

文章目录 前言一、使用numpy实现升维度&#xff0c;降维度二、使用TensorFlow实现升维度&#xff0c;降维度三、使用PyTorch实现升维度&#xff0c;降维度总结 前言 我们明确一下升维和降维的概念&#xff1a; 升维&#xff08;Dimensionality Augmentation&#xff09;&…

非关系型数据库Redis(缓存型数据库)

关系型数据库和非关系型数据库的区别 关系型数据库 是一个结构化的数据库&#xff0c;记录方式是行&#xff08;记录对象属性&#xff09;和列&#xff08;声明对象&#xff09; 表与表之间是有关联的&#xff0c;使用sql语句来对指定的表、库进行增删改查 在创建表的时候&…

通达信的ebk文件

我们在通达信软件中 调出 “自定义板块设置” 这个菜单&#xff0c;点击“导出”&#xff0c;会提示你存储 “自选股.EBK”&#xff0c;其实就是对自定义板块里的目录进行备份的一种方式&#xff0c; 当我们打开 这个文件&#xff0c;你会发现其实就是存储了 股票代码&#xff…

CleanMyMac X2024免费测试版好不好用?值不值得下载

如果你是一位Mac用户&#xff0c;你可能会遇到一些问题&#xff0c;比如Mac运行缓慢、磁盘空间不足、应用程序难以管理等。这些问题会影响你的Mac的性能和体验&#xff0c;让你感到沮丧和无奈。那么&#xff0c;有没有一款软件可以帮助你解决这些问题呢&#xff1f;答案是肯定的…

【AI】行业消息精选和分析(23-11-20)

技术发展 &#x1f3a8; LCM即时绘画&#xff0c;体验所见所得&#xff1a; - LCM LoRA支持即时绘图生成&#xff0c;体验直观。 - 在线体验地址提供直接访问。 - 清华大学SimianLuo开发&#xff0c;加速稳定扩散模型运行。 &#x1f48a; VM Pill&#xff1a;可吞咽装置追踪生…

Pyside6/PyQt6如何添加右键菜单,源码示例

文章目录 📖 介绍 📖🏡 环境 🏡📒 源码分享 📒🎈 添加图标📖 介绍 📖 在UI开发中经常会使用到右键菜单,本文记录了一个添加右键菜单的示例,可以举一反三,仅供参考! 🏡 环境 🏡 本文演示环境如下 Windows11Python3.11.5PySide6📒 源码分享 📒 下面…

clickhouse 业务日志告警

一、需求 对入库到clickhouse的业务日志进行告警&#xff0c;达阀值后发送企业微信告警。 方法一、 fluent-bit–>clickhouse(http)<–shell脚本,每隔一分钟获取分析结果 --> 把结果保存到/dev/shm/目录下 <-- node_exporter读取指标入库到prometheus<-- rules…

现货白银MACD实战分析例子

MACD这个技术指标的全称是平滑异同移动平均线&#xff0c;主要表示经过平滑处理后均线的差异程度&#xff0c;一般用来研判现货白银价格变化的方向、强度和趋势。MT4中的MACD指标&#xff0c;主要是由信号线、&#xff08;上升/下跌&#xff09;动能柱、0轴这三部分组成。 MACD…

CPU/GPU实现向量内积

向量内积&#xff08;点乘/点积/数量积&#xff09;&#xff1a;两个向量对应元素相乘之后求和&#xff1a; CPU实现&#xff1a; //cpu 实现一下向量内积#include<stdio.h> template<typedef T> void dot_mul(T *a, T *b, T *c, int n) { double tmp 0;for(i…

WiseGiga NAS远程命令执行漏洞复现 [附POC]

文章目录 WiseGiga NAS RCE漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 WiseGiga NAS RCE漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff…

【latex】公式推导等号对齐

使用aligned进行多行公式对齐&#xff0c;&作为对齐的节点&#xff0c;\作为公式换行 \begin{equation} \begin{aligned}a& bc \\& cd \end{aligned} \end{equation}

不懂找伦敦银趋势?3个方法搞定

趋势是我们的朋友&#xff0c;但是这个朋友却很喜欢跟我们开玩笑&#xff0c;如果我们不留意&#xff0c;根本发觉不了它的存在。怎么找到趋势本体并且和它做个好朋友呢&#xff1f;下面我们就来介绍三个方法。 数波段的高点和低点。我们以当前的市场波动价格为轴&#xff0c;向…

Vue项目中如何获取浏览器唯一标识Fingerprint2-浏览器指纹获取-demo

Fingerprint "fingerprintjs2": "^2.1.4", <template><n-card :segmented"{content: true,footer:true}" footer-style"padding:10px"><template #header>通过设备浏览器信息获取浏览器指纹的插件(官方宣称其识别精…

JavaScript 字符处理

1.删除前几个字符 使用 slice console.log(12345.slice(1))// 23452.首字母大写 var word abcconsole.log(word.charAt(0).toUpperCase() word.slice(1))// Abc3.字符为数字时可直接相乘 console.log(2*3) 4.字符串中是否包含某个子字符串 子串既可以为数字也可为字符串 /…

PlayCover“模拟器”作弊解决方案

当下的游戏市场&#xff0c;移动游戏已占据了主导地位&#xff0c;但移动端游戏碍于屏幕大小影响操作、性能限制导致卡顿等因素&#xff0c;开始逐步支持多端互通。但仍有一些游戏存在移动端与 PC 端不互通、不支持 PC 端或没有 Mac 版本&#xff0c;导致 Mac 设备体验游戏不方…

mysql主从搭建(docker)

一、主从概述 MySQL主从又叫Replication、AB复制。简单讲就是A与B两台机器做主从后&#xff0c;在A上写数据&#xff0c;另外一台B也会跟着写数据&#xff0c;实现数据实时同步。有这样几个关键点&#xff1a; 1&#xff09;MySQL主从是基于binlog&#xff0c;主上需开启binl…

掌握Python中classmethod的妙用,提升代码灵活性与可维护性

概要 在Python编程中&#xff0c;classmethod是一种非常有用的装饰器&#xff0c;它可以将一个方法转换为类方法&#xff0c;使得该方法可以通过类名或实例名直接调用&#xff0c;而不需要传入self参数。通过合理使用classmethod&#xff0c;我们可以提高代码的灵活性、复用性…

京东API接口获取京东平台商品详情数据,SKU,价格参数及其返回值说明

做过淘客开发的一定接触过淘宝API开发。 而做京东联盟软件自然离不开京东联盟API。 京东联盟API目前上线的有很多。 参数说明 通用参数说明 url说明 https://api-gw.onebound.cn/平台/API类型/ 平台&#xff1a;淘宝&#xff0c;京东等&#xff0c; API类型:[item_search,ite…

OpenHarmony Meetup北京站招募令

OpenHarmony Meetup城市巡回北京站火热来袭&#xff01;&#xff01;日期&#xff1a;2023年11月25日14:00地点&#xff1a;中国科学院软件园区五号楼B402与OpenHarmony技术大咖近距离互动&#xff0c;分享技术见解&#xff0c;结交志同道合的朋友&#xff01;活动主题聚焦Open…

重要功能丨支持1688API 接口对接一键跨境铺货及采购,解决跨境卖家货源烦恼!

在跨境电商运营中&#xff0c;不少卖家都会优先选择1688平台产品作为跨境店铺货源。 必不可少的1688商品详情接口 阿里巴巴中国站获得1688商品详情 API 返回值说明 item_get-获得1688商品详情 1688.item_get 公共参数 请求地址: 申请调用KEY测试 名称类型必须描述keyStrin…