聊聊Python中的闭包和装饰器

1. 闭包

首先我们明确一下函数的引用,如下所示:

def test1():
    print("--- in test1 func----")

# 调用函数
test1()

# 引用函数
ret = test1

print(id(ret))
print(id(test1))

#通过引用调用函数
ret()

运行结果:

--- in test1 func----
140212571149040
140212571149040
--- in test1 func----

以y=kx+b为例,请计算一条线上的某个点,即给x值计算出y值。下面以这个例子引出闭包的概念。

方法1

# 第1种
k = 1
b = 2
y = k*x+b
# 缺点:如果需要多次计算,那么就的写多次y = k*x+b这样的式子

方法2

# 第2种
def line_2(k, b, x):
    print(k*x+b)

line_2(1, 2, 0)
line_2(1, 2, 1)
line_2(1, 2, 2)
# 缺点:如果想要计算多次这条线上的y值,那么每次都需要传递k,b的值,麻烦

方法3

# 第3种: 全局变量
k = 1
b = 2
def line_3(x):
    print(k*x+b)

line_3(0)
line_3(1)
line_3(2)
k = 11
b = 22
line_3(0)
line_3(1)
line_3(2)
# 缺点:如果要计算多条线上的y值,那么需要每次对全局变量进行修改,代码会增多,麻烦

这里引申一下,关于全局变量,要是直接读取,不修改的话,是不用加global的。

而且所谓的修改,指的是地址变了,假如我指向的是一个列表,指向没变,列表中的内容改变了,也不用加global的。

我们看一下下面的例子:

a = 100
b = ‘chenchi‘
c = [1, 2, 3]

def func():
    print(‘a的值:{},a的地址:{}‘.format(a, id(a)))
    print(‘b的值:{},b的地址:{}‘.format(b, id(b)))
    print(‘c的值:{},c的地址:{}‘.format(c, id(c)))

def func2():
    global a
    global b
    a += 1
    b += ‘ccc‘
    # 这里c不用声明global,c的地址没有发生变化
    c.append(4)

if __name__ == ‘__main__‘:
    func()
    func2()
    func()

运行结果:

列表的地址没变,不用加global。

方法4

# 第4种:缺省参数
def line_4(x, k=1, b=2):
    print(k*x+b)

line_4(0)
line_4(1)
line_4(2)

line_4(0, k=11, b=22)
line_4(1, k=11, b=22)
line_4(2, k=11, b=22)
# 优点:比全局变量的方式好在:k, b是函数line_4的一部分 而不是全局变量,因为全局变量可以任意的被其他函数所修改
# 缺点:如果要计算多条线上的y值,那么需要在调用的时候进行传递参数,麻烦

方法5

# 第5种:实例对象
class Line5(object):
    def __init__(self, k, b):
        self.k = k
        self.b = b

    def __call__(self, x):
        print(self.k * x + self.b)

line_5_1 = Line5(1, 2)
# 对象.方法()
# 对象()
line_5_1(0)
line_5_1(1)
line_5_1(2)
line_5_2 = Line5(11, 22)
line_5_2(0)
line_5_2(1)
line_5_2(2)
# 缺点:为了计算多条线上的y值,所以需要保存多个k, b的值,因此用了很多个实例对象, 浪费资源

关于__call__()的用法:

方法6

采用闭包的方式,什么是闭包呢?

在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。

如下所示:

# 第6种:闭包
def line_6(k, b):
    def create_y(x):
        print(k*x+b)
    return create_y

line_6_1 = line_6(1, 2)
line_6_1(0)
line_6_1(1)
line_6_1(2)
line_6_2 = line_6(11, 22)
line_6_2(0)
line_6_2(1)
line_6_2(2)

闭包,红色部分return的不能加(),不加()是函数引用,否则就应该是函数的返回值。

在上面的例子中,函数line_6与变量k,b构成闭包,闭包具有提高代码可复用性的作用。

由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存,但是相对于实例对象来说,已经好多了。

思考:函数、匿名函数、闭包、对象 当做实参时 有什么区别?
1. 匿名函数能够完成基本的简单功能,传递是这个函数的引用 只有功能。
2. 普通函数能够完成较为复杂的功能,传递是这个函数的引用 只有功能。
3. 闭包能够将较为复杂的功能,传递是这个闭包中的函数以及数据,因此传递是功能+数据。
4. 对象能够完成最为复杂的功能,传递是很多数据+很多功能,因此传递是功能+数据。

修改外部函数中的变量

x = 300
def outer():
    x = 200
    def inner():
        nonlocal x
        # global x
        print(‘---1---x=%d‘ % x)
        x = 100
        print(‘---1---x=%d‘ % x)
    return inner

t1 = outer()
t1()

注意,这是python3,输出结果如下:

如果把nonlocal改成global,第一个x就变成300了。

2. 装饰器

原文地址:https://www.cnblogs.com/DarrenChan/p/10346935.html

时间: 2024-08-28 00:29:24

聊聊Python中的闭包和装饰器的相关文章

21.python中的闭包和装饰器

python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 以下说明主要针对 python2.7,其他版本可能存在差异. 也许直接看定义并不太能明白,下面我们先来看一下什么叫做内部函数: def wai_hanshu(canshu_1): def nei_hanshu(canshu_2): # 我在函数内部有定义了一个函数 return canshu_1*canshu_2 return

python中的闭包和装饰器

闭包函数介绍 什么是闭包 维基百科中关于闭包的概念: 在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包.闭包可以用来在一个函数与一组 "私有" 变量之间创建关联关系.在给定函数被多次调用的过程中,这些私有变量能够保持其持久性. 对上面这段话总结一下,即python中的闭包需要满足3个条件:1) 内嵌函数,即函数里定义了函数 -- 这对应函数之间的嵌套2) 内嵌函数必须引用定义在外部函数里中的变量(不是全局作用域中的引用)-- 内部

轻松理解python中的闭包和装饰器 (下)

在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函数,例如filter, map, reduce这些函数 可以定义一个函数作为高阶函数例如: def func(x, y, f): return f(x)+f(y) 可以这样调用func(2,-1,abs) 函数返回结果为3 有些时候,我们不需要显式地定义传入的函数,直接传入匿名函数更方便. 在Pyt

python中的无参装饰器和有参装饰器

python中的无参装饰器和有参装饰器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 装饰器特点: 1>.开放封闭原则,即对扩展是开放的,对修改时封闭的: 2>.装饰器本质可以是任意可调用的对象,被装饰的对象也可以是任意可调用对象: 3>.装饰器的功能是在不修改被装饰器对象源代码以及被装饰器对象的调用方式的前提下为其扩展新功能: 4>.装饰器本质是函数,(即装饰其他函数)就是为其他函数添加附加功能. 一.典型装饰器案例 1 #!/usr/bin/env pyth

Python中,关于@property装饰器

1.为什么使用@property装饰器?br/>在类中,当我么不想在外界直接调用到类的属性,或者不想展示属性的真实内容时,可以用到@property.它规定了我们直接用 对象名.属性名 获取对象属性时并不会直接取得对象的属性,而是通过调用@property装饰过的属性函数来给调用者反馈. 2.我们为什么不使用特定的方法来进行上面的操作?原因是因为太繁琐.例: class Person: def __init__(self, username, password) -> None: self.u

Python基础day-7[闭包,装饰器]

闭包: 首先说下闭包是什么? 闭包就是在函数内部定义的函数,包含对外部作用域的引用,但不包含全局作用域.因为函数的作用域在定义的时候就固定死了,所以闭包函数有自带作用域和延迟计算的特点. 闭包函数定义:如果一个内部函数,包含了对外部作用域的引用,但是不是包含全局作用域.那么这个函数就被认为是闭包函数.闭包函数可以使用".__closure__" 来查看闭包函数的属性.下面我们来看一个示例: def t(): money = 100 def s(): print(money) retur

Python中带参数的装饰器

装饰器本身是用来是为一个函数是实现新的功能,并且不改变原函数的代码以及调用方式. 遇到这样一种问题: 众多函数调用了你写的装饰器,但客户有需求说,我想实现我可以随之控制装饰器是否生效. 那你就不可能在得到命令的时候去原函数头部去做删除和添加装饰器调用的命令.这是就可以用到带参数的装饰器,定义一个开关,调用装饰器的时候,把这个装饰器的开关参数给传递进去,这样当开关打开的时候装饰器生效,关闭的时候则只执行原函数的代码. 举例:开关参数为True的时候执行过程: 1 F = True #step 1

python函数2_闭包和装饰器

变量的范围(局部/全局) 局部变量 在函数内部声明的变量 在函数体外部无法获取 全局变量 在函数外部声明的变量 所有函数都可以访问 在函数内,局部变量和全局变量同名,优先使用局部变量 name = '月月' def fun2(): name = '小月月' name += '会弹吉他' print(name) fun2() 小月月会弹吉他 当在函数体内,尝试更改全局变量会报错 name = '月月' def fun2(): print(name) name += '会弹吉他' fun2() 当需

13、python中的函数(闭包与装饰器)

一.嵌套函数 函数的内部又再定义另一个函数,这个函数就叫嵌套函数,里面含函数就叫内部函数. 示例: 二.返回函数 函数可以接收函数对象作为参数,同理函数也能返回一个函数对象作为返回值. 示例: 返回函数可以用来延迟函数的执行. 三.命名空间与变量作用域 变量作用域指的是变量的存活的范围.命名空间指的是属于一个对象的所有属性(对象)的集合. 示例: A的命名空间是A函数对象里面的所有对象的集合,包括变量a.函数B.变量b:B的命名空间就是属于函数B的所有对象的集合,包括变量b: a的变量作用域就是