【从零学习python 】31.深入理解Python中的高阶函数和闭包

文章目录

  • 高阶函数
    • 定义一个变量指向函数
    • 高阶函数
      • 函数做为另一个函数的参数
      • 函数作为另一个函数的返回值
    • 闭包
      • 函数嵌套
      • 什么是闭包
      • 修改外部变量的值
        • 原因分析
        • 解决方案
    • 进阶案例

高阶函数

在Python中,函数其实也是一种数据类型。

def test():
    return 'hello world'

print(type(test))  # 

函数对应的数据类型是 function,可以把它当做是一种复杂的数据类型。

既然同样都是一种数据类型,我们就可以把它当做数字或者字符串来处理。

定义一个变量指向函数

在Python中,我们还可以定义一个变量,让它来指向一个函数,相当于给函数起了一个别名。

def test():
    return 'hello wrold'

fun = test   # 定义了一个变量fun,让它指向了 test 这个函数
print(fun())   # 使用fun()可以直接调用test这个函数

print(id(fun))  # 1819677672040
print(id(test))  # 1819677672040

注意:在定义一个变量表示一个函数时,函数后面不能加括号!加括号表示的是调用这个函数。

def test():
    return 'hello world'

result = test()   # 这种写法是调用test函数,并把函数的返回值赋值给result变量
print(result())   # 这里会报错  TypeError: 'str' object is not callable

fun = test   # 这种写法是给test函数起了一个别名,注意,这里的test后面不能加()
fun()        # 可以使用别名调用这个函数

高阶函数

既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,同样,我们还可以把一个函数当做另一个函数的返回值。这种函数的使用方式我们称之为高阶函数。

函数做为另一个函数的参数

def test(age, action):
    if age < 18:
        print('您还没满十八岁,请退出')
    action()   # 把参数action直接当做一个函数来调用

def smoke():
    print('我已经年满十八岁了,我想抽烟')

my_action = smoke  # 定义一个变量my_action,让它指向smoke函数
test(21, my_action)  # 将my_action传给test函数作为它的参数

test(21, smoke)  # 还可以不再定义一个新的变量,直接传入函数名

函数作为另一个函数的返回值

def test():
    print('我是test函数里输入的内容')

def demo():
    print('我是demo里输入的内容')
    return test  # test 函数作为demo函数的返回值

result = demo()  # 我是demo里输入的内容  调用 demo 函数,把demo函数的返回值赋值给 result
print(type(result)) #   result 的类型是一个函数

result() # 我是demo里输入的内容    我是test函数里输入的内容   既然result是一个函数,那么就可以直接使用() 调用这个函数

demo()()  # 我是demo里输入的内容    我是test函数里输入的内容

闭包

函数只是一段可执行代码,编译后就“固化”了,每个函数在内存中只有一份实例,得到函数的入口点便可以执行函数了。函数还可以嵌套定义,即在一个函数内部可以定义另一个函数,有了嵌套函数这种结构,便会产生闭包问题。

函数嵌套

在函数里面还可以定义函数,可以嵌套多层,执行需要被调用。

def outer():
    print('outer----hello')
    def inner():  # inner这个函数是在outer函数内部定义的
        print('inner----hello')
    inner()  # inner函数只在outer函数内部可见

outer()
# inner()  这里会报错,在outer函数外部无法访问到inner函数

什么是闭包

闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数块+引用环境)。

def outer(n):
    num = n
    def inner():
        return num+1
    return inner

print(outer(3)())  # 4
print(outer(5)())  # 5

在这段程序中,函数 inner 是函数 outer 的内嵌函数,并且 inner 函数是outer函数的返回值。我们注意到一个问题:内嵌函数 inner 中引用到外层函数中的局部变量num,Python解释器会这么处理这个问题呢?先让我们来看看这段代码的运行结果,当我们调用分别由不同的参数调用 outer 函数得到的函数时,得到的结果是隔离的(相互不影响),也就是说每次调用outer函数后都将生成并保存一个新的局部变量num,这里outer函数返回的就是闭包。如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

修改外部变量的值

闭包里默认不能修改外部变量。

def outer(n):
    num = n
    def inner():
        num = num + 1
        return num
    return inner

print(outer(1)())

上述代码运行时会报错!

UnboundLocalError: local variable 'num' referenced before assignment

原因分析

在python里,只要看到了赋值语句,就会认为赋值语句的左边是一个局部变量。num = num + 1 这段代码里,num=的左边,python解析器会认为我们要修改inner函数里num这个局部变量,而这个变量使用之前是未声明的,所以会报错。

解决方案

我们分析过,报错的原因在于当我们在闭包内修改外部变量时,会被python解析器误会为内部函数的局部变量。所以,解决方案就在于,我们需要想办法,让解析器知道我们不是要修改局部变量,而是要修改外部变量。

解决方法:使用 nonlocal 关键字

def outer(n):
    num = n
    def inner():
        nonlocal num  # 修改前使用nonlocal关键字对 num 变量进行说明
        num = num + 1
        return num
    return inner

print(outer(2)())

进阶案例

【Python】Python 实现猜单词游戏——挑战你的智力和运气!

【python】Python tkinter库实现重量单位转换器的GUI程序

【python】使用Selenium获取(2023博客之星)的参赛文章

【python】使用Selenium和Chrome WebDriver来获取 【腾讯云 Cloud Studio 实战训练营】中的文章信息

使用腾讯云 Cloud studio 实现调度百度AI实现文字识别

【玩转Python系列【小白必看】Python多线程爬虫:下载表情包网站的图片

【玩转Python系列】【小白必看】使用Python爬取双色球历史数据并可视化分析

【玩转python系列】【小白必看】使用Python爬虫技术获取代理IP并保存到文件中

【小白必看】Python图片合成示例之使用PIL库实现多张图片按行列合成

【小白必看】Python爬虫实战之批量下载女神图片并保存到本地

【小白必看】Python词云生成器详细解析及代码实现

【小白必看】Python爬取NBA球员数据示例

【小白必看】使用Python爬取喜马拉雅音频并保存的示例代码

【小白必看】使用Python批量下载英雄联盟皮肤图片的技术实现

【小白必看】Python爬虫数据处理与可视化

【小白必看】轻松获取王者荣耀英雄皮肤图片的Python爬虫程序

【小白必看】利用Python生成个性化名单Word文档

【小白必看】Python爬虫实战:获取阴阳师网站图片并自动保存

小白必看系列之图书管理系统-登录和注册功能示例代码

小白实战100案例: 完整简单的双色球彩票中奖判断程序,适合小白入门

使用 geopandas 和 shapely(.shp) 进行地理空间数据处理和可视化

使用selenium爬取猫眼电影榜单数据

图像增强算法Retinex原理与实现详解

爬虫入门指南(8): 编写天气数据爬虫程序,实现可视化分析

爬虫入门指南(7):使用Selenium和BeautifulSoup爬取豆瓣电影Top250实例讲解【爬虫小白必看】

爬虫入门指南(6):反爬虫与高级技巧:IP代理、User-Agent伪装、Cookie绕过登录验证及验证码识别工具

爬虫入门指南(5): 分布式爬虫与并发控制 【提高爬取效率与请求合理性控制的实现方法】

爬虫入门指南(4): 使用Selenium和API爬取动态网页的最佳方法

爬虫入门指南(3):Python网络请求及常见反爬虫策略应对方法

爬虫入门指南(2):如何使用正则表达式进行数据提取和处理

爬虫入门指南(1):学习爬虫的基础知识和技巧

深度学习模型在图像识别中的应用:CIFAR-10数据集实践与准确率分析

Python面向对象编程基础知识和示例代码

MySQL 数据库操作指南:学习如何使用 Python 进行增删改查操作

Python文件操作指南:编码、读取、写入和异常处理

使用Python和Selenium自动化爬取 #【端午特别征文】 探索技术极致,未来因你出“粽” # 的投稿文章

Python多线程与多进程教程:全面解析、代码案例与优化技巧

Selenium自动化工具集 - 完整指南和使用教程

Python网络爬虫基础进阶到实战教程

Python入门教程:掌握for循环、while循环、字符串操作、文件读写与异常处理等基础知识

Pandas数据处理与分析教程:从基础到实战

Python 中常用的数据类型及相关操作详解

【2023年最新】提高分类模型指标的六大方案详解

Python编程入门基础及高级技能、Web开发、数据分析和机器学习与人工智能

用4种回归方法绘制预测结果图表:向量回归、随机森林回归、线性回归、K-最近邻回归

你可能感兴趣的:(python从零出发,Python,高阶函数,闭包,函数式编程,从零出发,小白必看)