Python函数式编程进阶:装饰器和闭包介绍

文章目录

  • Python函数式编程进阶:函数装饰器和闭包介绍
    • 一个简单的装饰器实现和行为表现
    • 装饰器通常会把函数替换成另一个函数
    • Python导入模块时首先就会运行装饰器
    • 闭包
      • __closure__属性可以查看闭包的自由变量
      • 总结
    • nonlocal声明

Python函数式编程进阶:函数装饰器和闭包介绍

一个简单的装饰器实现和行为表现

函数装饰器用于在源代码中“标记”一个函数,增强它的行为。理解他的前提是理解闭包

先来看一个装饰器的例子

def decorator(func):print('Running -->{}'.format(func.__name__))return func@decorator
def add(a,b):return a + bprint(add(1,2))
# 输出
# Running -->add
# 3

对于一个函数target,和其装饰器decorator,在运行target函数时本质上就是运行target = decorator(target)
我们去掉这个装饰符号后运行一下代码

def decorator(func):print('Running -->{}'.format(func.__name__))return func# @decorator
def add(a,b):return a + bprint(decorator(add))
# 输出
# Running -->add
# <function add at 0x00000217C90D7280>

这下我们就发现了使用装饰符的好处了,我们可以增强这个函数的行为,只需要和之前一样直接传参数给这个就可以了

以上只是一个简单的例子,是一个很平凡的装饰器。接下来我们观察一个稍微复杂一点的例子

装饰器通常会把函数替换成另一个函数

def deco(func): def inner():print('Running --> inner()')return inner
@deco
def target():print('Running --> target()')print(target) # <function deco.<locals>.inner at 0x00000132A1427310>
target() # Running --> inner()

此时target函数的行为被替换成了装饰器中定义的函数了,而且我们查看target函数的时候发现其已经变成了装饰器中定义的函数inner的引用

Python导入模块时首先就会运行装饰器

装饰器在被装饰函数定义之后立刻运行

registry = []def register(func):registry.append(func)print('Running register(%s)' % func)return func@register
def func1():print('Running func1()')@register
def func2():print('Running func2()')@register
def func3():print('Running func3()')print(registry)
func1()
func2()
func3()
# Running register(<function func1 at 0x0000021E0A9B7280>)
# Running register(<function func2 at 0x0000021E0A9B7310>)
# Running register(<function func3 at 0x0000021E0A9B73A0>)
# [<function func1 at 0x0000021E0A9B7280>, <function func2 at 0x0000021E0A9B7310>, <function func3 at 0x0000021E0A9B73A0>]  
# Running func1()
# Running func2()
# Running func3()

闭包

如果有提前了解过全局变量和局部变量,那么就可以知道闭包是什么意思了,它其实指的是延伸了作用域的函数
在函数内部又定义了一个新的函数

def make_average():series = []def averager(new_value):series.append(new_value)total = sum(series) / len(series)return totalreturn averageravg = make_average()print(avg(10)) # 10
print(avg(20)) # 15 
print(avg(30)) # 20

观察这个函数,我们发现,除了函数有的输入输出特性之外,这个函数还可以保存历史的值
series是make_average()的局部变量,在averager函数的作用域之外
在定义avg变量时,函数内部已经对series这个变量进行了初始化
在averager这个函数中 series 这个变量叫做自由变量,特指没有在averager这个函数作用域中绑定的函数
可以通过查看__code__(表示编译后的函数定义体)属性来检查函数有哪些局部变量和自由变量

print(avg.__code__.co_varnames) # 局部变量
print(avg.__code__.co_freevars) # 自由变量 
# 输出
# ('new_value', 'total')
# ('series',)

__closure__属性可以查看闭包的自由变量

print(avg.__closure__)  # 自由变量组成的元组
print(avg.__closure__[0]) 
print(avg.__closure__[0].cell_contents) # 查看cell_contents属性
# 输出 
# (<cell at 0x0000016AA4E70B50: list object at 0x0000016AA4E17940>,)
# <cell at 0x0000016AA4E70B50: list object at 0x0000016AA4E17940>
# [10, 20, 30]

总结

闭包是一种函数,它会保存在函数定义时存在的自由变量,这样调用函数时虽然定义作用域不可用了,但仍能使用绑定的变量

nonlocal声明

python中列表是一种可变类型,而数字,字符串时不可变类型

def make_average():count = 0total = 0def averager(new_value):count += 1total += new_valuereturn total / countreturn averager
avg = make_average()
print(avg(1))

以上代码中,count += 1 其实就是 count = count + 1
也就是说我们在对这个变量进行赋值,意味着它会变成这个函数里头的一个局部变量(函数会隐式的创建count变量),所以在这个函数的作用域中,没有定义count变量,会报错
列表是可变的对象,因为我们没有给它重新赋值而是调用了append之类的方法添加元素
像数字字符串元组这种的都是不可变类型,只能读取不能更新
python3中引入的nonlocal声明就是把变量标记为自由变量

def make_average():count = 0total = 0def averager(new_value):nonlocal count , totalcount += 1total += new_valuereturn total / countreturn averager
avg = make_average()
print(avg(1)) # 1
print(avg(3)) # 2 
print(avg(5)) # 3

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

贴片和直插型IRM红外遥控接收头引脚定义和规格参数及使用注意事项

红外遥控接收头使用注意事项 引脚定义存在不同 红外遥控接收头大量使用在家用电器的遥控中&#xff0c;属于价廉物美的一种光电接收器件&#xff0c;批量价格约0.3元左右。 多数遥控接收头的引脚定义是OUT,GND,VCC&#xff0c;另有引脚定义不同为OUT,VCC,GND&#xff0c;记住…

Django 创建项目及应用

1&#xff0c;安装 Django pip install Django3.1.5 2&#xff0c;创建 Django项目 django-admin startproject myshop 3&#xff0c;创建 Django应用 python manage.py startapp app1 4&#xff0c;启动 Django项目 python .\manage.py runserver 到这里项目及应用创建…

Delphi主窗体实现透明

设置以下属性即可实现透明窗体显示文字&#xff1a; 设置窗体属性&#xff1a; BorderStyle 属性设置为 bsNone Color 属性设置为 clWhite&#xff08;白色&#xff1a;窗体背景色&#xff09; TransparentColor 属…

空间转录组基础数据解读+学习方法

详情请参考这个视频&#xff1a;空间转录组&#xff08;spatial transcriptome&#xff09;数据分析基础教程_哔哩哔哩_bilibili 1.首先是filtered_feature_bc_matrix文件 两个里面的内容本质一样&#xff0c;都是空间转录组 表达矩阵的信息 2.具体的所有东西可以在10x的网站…

React(五)UseEffect、UseRef

(一)useEffect useEffect – React 中文文档 useEffect hook用于模拟以前的class组件的生命周期&#xff0c;但比原本的生命周期有着更强大的功能 1.类组件的生命周期 在类组件编程时&#xff0c;网络请求&#xff0c;订阅等操作都是在生命周期中完成 import React, { Co…

图书推荐:ChatGPT专业知识信息课程

《ChatGPT专业知识信息课程》&#xff08;ChatGPT-Expertise Informative Course&#xff09; 是一本由Dwayne Anderson撰写的电子书&#xff0c;提供了关于ChatGPT的丰富知识。该书涵盖了与ChatGPT相关的各种主题&#xff0c;如其与OpenAI的关系、ChatGPT与GPT-3之间的混淆、C…

【蓝牙概述】

蓝牙无线技术是一种短距离通信系统&#xff0c;旨在取代连接便携式和/或固定电子设备的电缆。蓝牙无线技术的主要特点是稳健性、低功耗和低成本。该规范的许多功能都是可选的&#xff0c;从而允许产品差异化。 蓝牙无线技术系统有两种形式&#xff1a;基本速率 (BR) 和低功耗 …

浅谈旧项目如何添加新依赖

Spring项目创建之后&#xff0c;还想添加新的依赖&#xff08;如Spring框架内置的依赖&#xff09;&#xff0c;可以安装插件&#xff1a; 装完该插件之后&#xff0c;就可以在pom.xml文件里&#xff0c;右键选择 Generate即可出现下述界面&#xff1a; 点击ok即可添加新的…

jpeg压缩算法学习(1)——离散余弦变换

离散余弦变换是jpeg压缩算法的关键步骤 思想 离散余弦变换的基本原理是&#xff1a;每一组离散的数据都可以由一组不同频率的余弦波来表示。 应用于图片上就是&#xff1a;将像素值转换为不同频率的余弦函数的系数&#xff08;权重&#xff09; 像素值——>权重 一维离…

网络原理——TCP/IP--数据链路层,DNS

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 今天你敲代码了吗 目录 数量链路层目的地址和原地址类型校验和 DNS 数量链路层 主要的协议是以太网协议.一个横跨数据链路层和 物理层的协议,既包含了数据链路层的内容, 也包含了⼀些物理层的内容 我们来了解一…

Docker网络异常问题

遇到此问题 docker: Error response from daemon: failed to create endpoint … on network bridge: failed to add the host (veth9754872) <> sandbox (veth6a875bb) pair interfaces: operation not supported. 先用 modinfo veth 检查 $ modinfo veth如果出错了&…

【前端开发--css学习笔记】CSS超详细的学习笔记。前端开发css学习笔记(非常详细,适合小白入门)

二&#xff0c;CSS学习笔记 1&#xff0c;CSS语法 1-1 CSS 实例 CSS声明总是以分号 ; 结束&#xff0c;声明总以大括号 {} 括起来: <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>菜鸟教程(runoob.com)</title…

为什么改变进制传输系统码长不变

目录 直接上图片 问题分析 传信率与传码率 多进制调制 码长不变的理解 误码率考量 总结 直接上图片 问题分析 在讨论这个问题时&#xff0c;通常是指在保持RB&#xff08;码元传输速率&#xff0c;传码率&#xff0c;符号率&#xff0c;波特率&#xff09;不变的情况下&a…

即时通讯视频会议平台,WorkPlus本地化部署解决方案

随着现代科技的快速发展&#xff0c;传统的会议方式已经不再满足企业和组织的需求。即时通讯视频会议以其便利性和高效性&#xff0c;成为了现代企业沟通和协作的重要工具。通过即时通讯视频会议&#xff0c;企业可以实现无时差的交流和远程协作&#xff0c;增强团队合作和提高…

【JS】实现数组扁平化的7种方式

历史小剧场 换句话说&#xff0c;崇祯上台以后&#xff0c;是很想干事的&#xff0c;但有的事&#xff0c;干了也白干&#xff0c;有的事&#xff0c;干了不如不干&#xff0c;朝廷就是这么个朝廷&#xff0c;大臣就是这帮大臣&#xff0c;没法干。----《明朝那些事儿》 一、递…

第二十六章CSS3基础

1.CSS简介 CSS3是CSS技术的升级版本,是最新的CSS标准。CSS3规范继承了CSS21并进行了很多的增补与修改。 1.CSS3模块 为了提高开发速度,也为了方便各主流浏览器根据需要渐进式支持,CSS3按模块化进行了全新设计,这些模块可以独立发布和实现,也为日后CSS的扩展奠定了基础。…

实现Redis和数据库数据同步问题(JAVA代码实现)

这里我用到了Redis当中的发布订阅模式实现(JAVA代码实现) 先看图示 下面为代码实现 首先将RedisMessageListenerContainer交给Spring管理. Configuration public class redisConfig {AutowiredRedisConnectionFactory redisConnectionFactory;AutowiredQualifier("car…

HALCON-从入门到入门-最常用的算子-二值化

1.废话 图像处理中的二值化是一种将灰度图像转换为只有两种可能值&#xff08;通常是0和255&#xff0c;分别代表黑色和白色&#xff09;的过程。这个过程在数字图像处理中非常常见&#xff0c;因为它可以简化图像数据&#xff0c;突出图像的主要特征&#xff0c;并降低后续处…

【Spring框架全系列】IOC DI案例,setter方法和构造方法注入(详解) + 思维导图

文章目录 一.概念实操Maven父子工程 二. IOC和DI入门案例【重点】1 IOC入门案例【重点】问题导入1.1 门案例思路分析1.2 实现步骤2.1 DI入门案例思路分析2.2 实现步骤2.3 实现代码2.4 图解演示 三、Bean的基础配置问题导入问题导入1 Bean是如何创建的【理解】2 实例化Bean的三种…

二分查找,查找第一个大于目标元素target所对应的下标-2300. 咒语和药水的成功对数

题目链接及描述 2300. 咒语和药水的成功对数 - 力扣&#xff08;LeetCode&#xff09; 题目分析 这道题目作为一个典型的二分查找&#xff0c;题目中所述&#xff0c;找到每一个spells[i]在positions中对应的元素positions[i]使其乘积大于给定元素sucess&#xff0c;并统计每一…