【Python函数式编程】——闭包

简介: 一个函数定义中引入了函数定义以外的变量,并且该函数可以在其定义以外被执行,这样的一个函数称为闭包。

Python函数式编程——闭包

在这里插入图片描述

1.什么是闭包

  什么是闭包:一个函数定义中引入了函数定义以外的变量,并且该函数可以在其定义以外被执行,这样的一个函数称为闭包。

闭包的三个条件,缺一不可

"""
1)必须有一个内嵌函数(函数里定义的函数)——这对应函数之间的嵌套 

2)内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量

3)外部函数必须返回内嵌函数——必须返回那个内部函数
"""

闭包代码实现:

# -*- coding: utf-8 -*-
# @File  : 闭包.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 15:58


# 定义一个函数
def fun_a(num_a):
    # 在函数内部再定义⼀个函数
    # 并且这个内部函数⽤到了外部的变量,这个函数以及⽤到外部函数的变量及参数叫 闭包
    def fun_b(num_b):
        print('内嵌函数fun_b的参数是:%s,外部函数fun_a的参数是:%s' % (num_b, num_a))
        return num_a + num_b
    # 这里返回的就是闭包的结果
    return fun_b


# 给fun_a函数赋值,这个10就是传参给fun_a
ret = fun_a(10)

# 注意这里的10其实是赋值给fun_b
print(ret(10))

# 注意这里的90其实是赋值给fun_b
print(ret(90))

在这里插入图片描述

  此时,内部函数对外部函数作⽤域⾥变量的引⽤(⾮全局变量),则称内部函数为闭包。

2.闭包修改外部变量

python交互环境idle

>>> def counter(start = 0):
    count = [start]
    def incr():
        count[0] += 1
        return count[0]
    return incr

>>> c1 = counter(5)
>>> print(c1())
6
>>> print(c1())
7
>>> print(c2())
51
>>> print(c2())
52
>>>
  当一个函数在本地作用域找不到变量申明时会向外层函数寻找,这在函数闭包中很常见但是在本地作用域中使用的变量后,还想对此变量进行更改就会报错。

看一段代码:

# -*- coding: utf-8 -*-
# @File  : 闭包修改外部变量.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 16:30


# 闭包修改外部变量的值
def test1():
    c = 1
    # c不是局部变量,是介于局部变量和全局变量之间的一种变量,用 nonlocal标识

    def add1():

        print(c)  # 1
        c += 1
        return c  # 2
    return add1


print(test1()())

报错信息:
在这里插入图片描述

此时,如果我在函数内加一行nonlocal c就可解决这个问题

代码:

# -*- coding: utf-8 -*-
# @File  : 闭包修改外部变量.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 16:30


# 闭包修改外部变量的值
def test1():
    c = 1
    # c不是局部变量,是介于局部变量和全局变量之间的一种变量,用 nonlocal标识

    def add1():
        nonlocal c
        print(c)  # 1
        c += 1
        return c  # 2
    return add1


print(test1()())

nonlocal声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量(介于局部变量和全局变量之间的一种变量)。

使用 nonlocal的好处是,在为函数添加状态时不用额外地添加全局变量,因此可以大量地调用此函数并同时记录着多个函数状态,每个函数都是独立、独特的。

3.闭包的应用

闭包实现 y = a*x + b

# -*- coding: utf-8 -*-
# @File  : 闭包的应用.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 16:18


# y = a*x + b
def create_line(a, b):
    def line(x):
        return a * x + b

    return line


line1 = create_line(1, 1)  # a:1 b:1
line2 = create_line(4, 5)  # a:4 b:5

print(line1(5))  # 6
print(line2(5))  # 25
从这段代码中,函数 line与变量 a,b构成闭包。在创建闭包的时候,我们通过 create_line的参数 a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式( y = x + 1y = 4x + 5)。我们只需要变换参数 a,b,就可以获得不同的直线表达函数。

由此,我们可以看到,闭包也具有提⾼代码可复⽤性的作⽤。

如果没有闭包,我们需要每次创建函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。

1.闭包优化了变量,原来需要类对象完成的⼯作,闭包也可以完成
2.由于闭包引⽤了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

4.闭包的陷阱

  函数内部函数,引用外部函数参数或值,进行内部函数运算执行,并不是完全返回一个函数,也有可能是一个在外部函数的值,我们还需要知道返回的函数不会立刻执行,而是直到调用了函数才会执行。

看代码:

# -*- coding: utf-8 -*-
# @File  : 闭包的陷阱.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 17:09


def fun_a():
    fun_list = []
    for i in range(1, 4):

        def fun_b():
            return i * i

        fun_list.append(fun_b)
    return fun_list


f1, f2, f3 = fun_a()
print(f1(), f2(), f3())  
# 9 9 9 
  这里创建了一个 fun_a函数,外部函数的参数 fun_list定义了一个列表,在进行遍历,循环函数 fun_b,引用外部变量i 计算返回结果,加入列表,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了,但是实际结果并不是我们想要的 1, 4, 9,而是 999,这是为什么呢?

  这是因为,返回的函数引用了变量 i ,但不是立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,每一个独立的函数引用的对象是相同的变量,但是返回的值时候,3个函数都返回时,此时值已经完整了运算,并存储,当调用函数,产生值不会达成想要的,返回函数不要引用任何循环变量,或者将来会发生变化的变量,但是如果一定需要呢,如何修改这个函数呢?

我们fun_b()把这里的参数i赋值给x就可以解决

# -*- coding: utf-8 -*-
# @File  : 闭包的陷阱.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 17:09


def fun_a():
    fun_list = []
    for i in range(1, 4):

        def fun_b(x=i):
            return x ** 2

        fun_list.append(fun_b)
    return fun_list


f1, f2, f3 = fun_a()
print(f1(), f2(), f3())  
# 1 4 9
  可以再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变,那我们就可以完成下面的代码:
# -*- coding: UTF-8 -*- # 
def fun_a(): 
    def fun_c(i): 
        def fun_b(): 
            return i * i 
            
        return fun_b 

    fun_list = [] 
    for i in range(1, 4): 
        # f(i)立刻被执行,因此i的当前值被传入f() 
        fun_list.append(fun_c(i)) 
    return fun_list 


f1, f2, f3 = fun_a() 
print(f1(), f2(), f3()) 
# 1 4 9

相关文章
|
1月前
|
Python
Python进阶---闭包和装饰器
Python进阶---闭包和装饰器
30 2
|
1月前
|
Python
Python闭包函数和计时器
本文介绍了闭包函数的概念,它允许内部函数引用外部作用域的变量但无法修改它们。示例展示了如何使用闭包来封装函数。接着,文章讨论了如何在函数调用时添加开始和结束的打印语句,通过传递函数作为参数实现。然后,文章引入装饰器,通过闭包定义了一个`timer`装饰器,用于在函数执行前后打印消息。最后,给出了一个练习,实现了一个计算函数执行时间的装饰器,处理了带有参数的被装饰函数。
33 1
|
1月前
|
分布式计算 Python
Python函数式编程学习笔记
高阶函数是能接收另一个函数作为参数的函数,如Python的map()、reduce()和filter()。map()将传入的函数应用到序列每个元素并返回迭代器,如将整数列表转换为字符串列表。reduce()对序列进行累积计算,例如求和。filter()根据给定函数返回的真值保留或丢弃序列元素,常用于筛选。sorted()函数支持自定义排序,如按绝对值或ASCII值排序。此外,还包括返回函数、匿名函数(lambda)、装饰器(用于动态增强函数功能)和偏函数(partial),用于固定函数部分参数,简化调用。
27 1
|
3天前
|
开发者 Python
Python进阶:深入剖析闭包与装饰器的应用与技巧
Python进阶:深入剖析闭包与装饰器的应用与技巧
|
5天前
|
Python
在Python中,`map()`, `filter()` 和 `reduce()` 是函数式编程中的三个核心高阶函数。
【6月更文挑战第24天】Python的`map()`应用函数到序列元素,返回新序列;`filter()`筛选满足条件的元素,生成新序列;`reduce()`累计操作序列元素,返回单一结果。
16 3
|
17天前
|
Serverless 开发者 Python
Python函数式编程:让你的代码更简洁、更高效!
【6月更文挑战第12天】Python函数式编程引入了数学函数概念,强调无副作用和不可变数据。特点包括高阶函数、Lambda表达式、map、filter和reduce。示例展示了如何使用map进行平方运算,filter筛选条件元素,reduce计算元素总和,体现其简洁高效。虽然不适用于所有情况,但函数式编程能提升代码可读性和可维护性。
|
5天前
|
C++ Python
Python闭包概念该怎么理解
Python闭包概念该怎么理解
|
5天前
|
C语言 Python
python中闭包的详细讲解
python中闭包的详细讲解
|
30天前
|
Python
Python函数式编程自带函数
Python函数式编程自带函数
11 1
|
1月前
|
存储 Java 测试技术
Python中闭包和装饰器使用不当Python中闭包和装饰器使用不当
【5月更文挑战第4天】Python中闭包和装饰器使用不当
17 2