掌握Python中的闭包和装饰器

一、闭包

1.闭包的介绍

当一个函数在调用结束后,函数内定义的变量就会被销毁,但是我们有时候需要保存这个函数内的变量,需要在这个变量的基础上完成一些附加操作,比如:每次都在这个变量的基础上和其他变量进行求和运算,那怎么办呢?

我们可以通过闭包来解决这个需求。

闭包:在函数嵌套的前提下,内部的函数使用了外部函数的变量或者参数,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。

 

2.闭包的构成条件

通过闭包的定义,我们已经可以得知闭包的构成条件:

(1)在函数嵌套的前提下

(2)内部函数使用了外部函数的变量或者参数

(3)外部函数返回了内部函数

 

3.简单的闭包示例代码

# 定义一个外部函数
def func_out(number1):
    # 定义一个内部函数
    def inner(number2):
        #内部函数使用了外部函数的变量(number1)
        ret = number1 + number2
        print(ret)

    # 外部函数返回了内部函数,这里返回的内部函数就是 闭包!
    return inner


# 创建闭包的实例

f = func_out(1)

# 执行闭包

f(2)    // 显示3
f(3)    // 显示4

说明:通过上面的输出结果可以看出来闭包保存了外部函数的变量number1,每次执行闭包函数都是在number1=1的基础上进行计算。

 

4.闭包的作用

闭包可以保存外部函数的变量,不会随着外部函数调用结束而销毁。

注意点:由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。

二、闭包的使用

1.通过案例来描述

需求: 根据配置信息使用闭包实现不同人的对话信息,例如对话:

张三: 到北京了吗?
李四: 已经到了,放心吧。

2. 实现步骤说明

  1. 定义外部函数接收不同的配置信息参数,参数是人名
  2. 定义内部函数接收对话信息参数
  3. 在内部函数里面把配置信息和对话信息进行拼接输出

3.功能代码的实现

# 外部函数

def func_name(name):

    # 内部函数
    def say_info(info):
        print(name + ":" + info)

    return say_info

tom = func_name("TT")

tom("你好!")
tom("好久不见..你在吗")


jerry = func_name("JJ")

jerry("不在,不和你玩!")

运行结果:

TT: 你好!
TT: 好久不见..你在吗?
JJ: 不在, 不和你玩!

说明:闭包还可以提高代码的可重用性,不需要再手动定义额外的功能函数。

三、修改闭包使用的外部变量

1.修改闭包内使用的外部变量的实例:

# 定义一个外部函数
def func_out(num1):

    # 定义一个内部函数
    def func_inner(num2):
        # 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1
        nonlocal num1  # 告诉解释器,此处使用的是 外部变量a
        # 修改外部变量num1
        num1 = 10
        # 内部函数使用了外部函数的变量(num1)
        result = num1 + num2
        print("结果是:", result)

    print(num1)
    func_inner(1)
    print(num1)

    # 外部函数返回了内部函数,这里返回的内部函数就是闭包
    return func_inner


# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)

说明:想要在闭包内修改外部函数的变量需要加nonlocal关键字完成。

好了,闭包的内容大致就说完了。接下来我们来在闭包的基础上,掌握python中的装饰器。

 

五、装饰器

1.装饰器的定义

就是给已经存在的函数增加额外功能的函数,它的本质上就是一个闭包函数。

2.装饰器的功能特点

(1)在不修改已有函数的基础上

(2)不修改已有函数的调用方式

(3)给已有的函数增加额外的功能

3.装饰器的实例代码

# 添加一个登录之前验证的功能

def check(func)
    def inner():
        print("正在安全检测..")

        func()
    return inner


def denglu():

    print("正在登录...")

# 使用装饰器来装饰函数
denglu = check(denglu)

denglu()


执行结果:

正在安全检测...
正在登录...

# 装饰器的基本雏形
# def decorator(fn): # fn:目标函数.
#     def inner():
#         '''执行函数之前'''
#         fn() # 执行被装饰的函数
#         '''执行函数之后'''
#     return inner

说明:外部函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。

4.装饰器的语法糖写法

如果有多个函数都需要添加登录验证的功能,每次都需要编写func = check(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。

Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰

实例代码:

# 添加一个登录之前验证的功能

def check(func)
    def inner():
        print("正在安全检测..")

        func()
    return inner

# 使用语法糖方式来装饰函数
@check
def denglu():

    print("正在登录...")




denglu()


执行结果:

正在安全检测...
正在登录...

说明:(1)@check 等价于 denglu = check(denglu)    (2)装饰器的执行时间是在加载模块的时候立即执行

 

六、装饰器的使用

1.装饰器的使用场景

(1)函数执行时间的统计

(2)输出日志信息

2.装饰器实现对已有函数执行时间的统计

# 首先导入一个time模块统计时间
import time

# 装饰器函数
def get_tim(fn):
    def inner():
        start = time.time()
        fn()
        end = time.time()
        print("函数的执行时间是:%f" % end-start)
    return inner


@get_time
def hello():
    s = 0
    for i in range(100000):
        s += 1
    print(s)

hello()

 

七、通用装饰器的使用

1.装饰带有参数的函数
 

# 添加输出日志的功能

def logging(fn):
    def inner(n1,n2):
        print("正在计算..")
        fn(n1,n2)

    return inner


# 使用装饰器装饰函数
@logging

def sum(a,b)
    
    ret = a + b
    print(ret)


sum(10,20)

运行结果:
正在计算...
30

2.装饰带有返回值的函数

# 添加输出日志的功能

def logging(fn):
    def inner(n1,n2):
        print("正在计算..")
        # 原函数带有返回值,所以将返回值返回给rs
        rs = fn(n1,n2)
        # 再将rs返回给原函数
        return rs
    return inner


# 使用装饰器装饰函数

@logging
def sum(a,b)
    
    ret = a + b
    # 带返回值的原函数
    return ret

# 输出返回值
print(sum(10,20))  

运行结果:
正在计算...
30

3.装饰带有不定长参数的函数

# 添加输出日志的功能

def logging(fn):
    def inner(*args,**kwargs):
        print("正在计算..")
        # 原函数带有返回值,所以将返回值返回给rs
        rs = fn(*args,**kwargs)
        # 再将rs返回给原函数
        return rs
    return inner


# 使用装饰器装饰函数

@logging
def sum(*args,**kwargs)
    ret = 0
    # 求args不定长参数中的和
    for i in args:
        ret += i
    # 求kwargs不定长函数中的和
    for j in kwargs.values():
        ret += j
          
    # 带返回值的原函数
    return ret

# 输出返回值
print(sum(10,20,a=10,b=20))  

运行结果:
正在计算...
60

4.通用装饰器

其实通用装饰器就是定义了一个带返回值的带不定长参数的闭包函数,然后这个装饰器就可以被多个函数所使用新增功能。

实例代码:

# 添加输出日志的功能

def logging(fn):
    def inner(*args,**kwargs):
        print("正在计算..")
        # 原函数带有返回值,所以将返回值返回给rs
        rs = fn(*args,**kwargs)
        # 再将rs返回给原函数
        return rs
    return inner


# 使用装饰器装饰函数

@logging
def sum(*args,**kwargs)
    ret = 0
    # 求args不定长参数中的和
    for i in args:
        ret += i
    # 求kwargs不定长函数中的和
    for j in kwargs.values():
        ret += j
          
    # 带返回值的原函数
    return ret

@logging
def sub(a1,b1)
    
    print(a1-a2)


# 第一个原函数输出返回值
print(sum(10,20,a=10,b=20))  

# 第二个原函数
sub(10,20)

运行结果:
正在计算...
60
-10

 

八、多个装饰器的使用

1.多个装饰器就是一个原函数同时使用多个装饰器,但是它的使用顺序是有区别的。

 

def make_div(func):
    """对被装饰的函数的返回值 div标签"""
    def inner():
        return "<div>" + func() + "</div>"
    return inner


def make_p(func):
    """对被装饰的函数的返回值 p标签"""
    def inner():
        return "<p>" + func() + "</p>"
    return inner


# 装饰过程: content = make_div(make_p(content))

@make_div
@make_p
def content():
    return "好久不见..小伙子"

result = content()

print(result)

运行结果:

<div><p>好久不见...小伙子</p></div>

说明:多个装饰器的装饰过程就是:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,它是一个由内到外的装饰过程。

九、带有参数的装饰器(这个意思是装饰器可以传参)

 

1.带有参数的装饰器介绍

带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数

语法格式:@装饰器(参数,...)

2.实例代码(其实就是在装饰器的外面再套一个函数,让最外面的函数接收参数,返回值的值是装饰器函数)

# 添加输入日志的功能
# 最外部函数,用来接收参数
def logging(flag):
    def zhuangshi(fn):
        def inner(*args,**kwargs):
            # 接收传来的参数进行判断
            if flag == "+":
                print("正在进行加法运算..")
            elif flag =="-":
                print("正在进行减法运算..")

            ret = fn(*args,**kwargs)
            return ret

        return inner
    # 返回装饰器函数
    return zhuangshi

# 通过装饰器来传参
@logging("+")
def sum(a,b)
    return a + b

# 通过装饰器来传参
@logging("-")
def sub(a,b)
    return a - b


print(sum(10,20))

print(sub(20,10))


运行结果:

正在进行加法运算..
30
正在进行减法运算..
10

 

十、类装饰器的介绍

1.类装饰器的介绍

装饰器还有一种特殊的用法就是类装饰器,通过定义一个类来装饰函数。

2.类装饰器实例代码

# 定义一个类
class Check(object):
    
    # 相当于外部函数
    def __init__(self,fn):
        self.__fn = fn
    
    # 相当于内部函数
    # 实现call方法,表示对象是一个可调用对象,可以像调用函数一样去调用。
    def __call__(self,*args,**kwargs):
       # 添加装饰功能
        print("正在进行安全验证...")
        self.__fn()


@Check
def denglu():
    print("正在登录...")


denglu()

运行结果:

正在进行安全验证...
正在登录...

说明:

  • 想要让类的实例对象能够像函数一样进行调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable)
  • 类装饰器装饰函数功能在call方法里面进行添加

 

十一、property属性

1.property属性的介绍

property属性就是负责把一个方法当作属性使用,这样可以简化代码的使用。

2.定义property属性有两种方式

(1)装饰器方式

(2)类属性方式

3.类装饰方式实例

class Person(object):
    
    def __init__(self):
        
        self.__age = 0
    
    # 装饰器方式的property,把age方法当作属性使用,表示当获取属性时会之下下面修饰的方法
    @property
    def age(self):
        return self.__age
    
    # 把age方法当作属性使用,表示当设置属性时会执行下面修饰的方法
    @age.setter
    def age(self,new_age)

        if new_age > 150 and new_age < 0:
            print("请输入合法年龄")
        else:
            return self.__age = new_age

 

# 创建对象

oo = Person()

# 获取属性
print(oo.age)

# 设置属性
oo.age = 22

print(oo.age)

# 设置属性
oo.age = 200

运行结果:
0
22
请输入合法年龄

 

说明:

  • @property表示把方法当作属性使用,表示获取属性时候会执行下面修饰的方法
  • @方法明.setter表示把方法当作属性使用,表示党设置属性的时候会执行下面修饰的方法
  • 装饰器方式的property属性修饰的方法名一定要一样

4.类属性方式实例

class Person(object):
    
    def __init__(self):
        
        self.__age = 0
    

    def get_age(self):
        return self.__age
    
    
    
    def set_age(self,new_age)

        if new_age > 150 and new_age < 0:
            print("请输入合法年龄")
        else:
            return self.__age = new_age
    

    # 类属性方式的property属性
    age = property(get_age, set_age)
 

# 创建对象

oo = Person()

# 获取属性
print(oo.age)

# 设置属性
oo.age = 22

print(oo.age)

# 设置属性
oo.age = 200

运行结果:
0
22
请输入合法年龄

说明:

  • property的第一个参数表示获取属性的时候执行的方法
  • propery的第二个参数表示设置属性的时候执行的方法

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值