Python-25-闭包和装饰器

一、闭包

1. 函数的引用

def demo01():
    print("---demo01---")
test01 = demo01
test01()		# ---demo01---

2. 案例

设计程序计算 y = k * x + b

  • 函数
def demo01(x, k=1, b=1):
    print(k*x+b)
demo01(0)
demo01(1)
demo01(2)
demo01(0, 2, 2)
demo01(1, 2, 2)
demo01(2, 2, 2)
class Demo02(object):
    def __init__(self, k, b):
        self.k = k
        self.b = b
    def __call__(self, x):
        print(self.k * x + self.b)
d1 = Demo02(1, 1)
d1(0)
d1(1)
d1(2)
  • 闭包
def outter_func(k, b):
    def inner_func(x):
        print(k*x+b)
    return inner_func

f = outter_func(1, 2)
f(0)
f(1)
f(2)

函数:一般用来实现较简单的项目
闭包:一般用来实现较复杂的项目
类:一般用来实现十分复杂的项目

3. 闭包的概念

  • 闭包的构成条件

    • 函数的嵌套
    • 内部函数使用了外部函数的变量(包括外部函数的参数)
    • 外部函数返回内部函数
  • 闭包函数的空间中包含了外部函数的变量和内部函数本身

4. 闭包修改外部函数的变量

  • 不可变数据类型的外部函数的变量
def outer_func():
    n = 0

    def inner_func():
        nonlocal n		# 修改可变类型的对象,需要加上 nonlocal 关键字
        n = 1
        print(n)
    print(f'调用inner_func之前:n={n}')
    inner_func()
    print(f'调用inner_func之前:n={n}')

    return inner_func

f = outer_func()

''' 输出结果
调用inner_func之前:n=0
1
调用inner_func之前:n=1
'''
  • 可变数据类型的外部函数的变量
def outer_func():
    n = list()

    def inner_func():
        # nonlocal n		# 可变类型可以不加 nonlocal
        n.append(1)
        print(n)
    print(f'调用inner_func之前:n={n}')
    inner_func()
    print(f'调用inner_func之前:n={n}')

    return inner_func

f = outer_func()

''' 输出结果
调用inner_func之前:n=[]
[1]
调用inner_func之前:n=[1]
'''

二、装饰器

1. 装饰器简介

给已有函数增加额外功能的函数,后者的本质是一个闭包函数,在Python中叫做装饰器。

  • 装饰器的特点
    • 不修改原有函数的代码
    • 不改变原有函数的传参
    • 不改变原有函数的返回值
    • 不改变原有函数的调用方式,即不改变函数的引用名称

写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。

2. 装饰器的使用

# 定义一个装饰器,新增一个功能
def out_func(function):     # 这行中out_func是一个装饰器,function只能传入函数的引用
    def in_func():
        print('新增的功能')
        function()        # 需要调用被装饰的函数
    return in_func

# 创建者写的代码,老代码
# Python中的语法糖,使用装饰器的语法:@装饰器
@out_func           # 相当于 func1 = out_func(func1)
def func1():
    print('函数func1')

# 调用者,旧的调用方法
# func1()

# 新的调用方法
# 使用func1来保存闭包的目的:因为function已经指向了原函数,func1引用就可以指向新的对象
# func1 = out_func(func1)
func1()
  • 随堂练习
# 定义一个计时的装饰器
def count_time(function):
    def in_func():
        # 函数调用前的时间
        t1 = time.time()
        function()
        # 计算函数调用后的时间差
        print(time.time() - t1)
    return in_func


@count_time
def func2():
    for i in range(100000):
        print(i)

@count_time
def func3():
    for i in range(10000):
        print(i)

######## 下面为调用者的代码,不能修改代码
func2()
func3()

3. 原函数需要传入参数

def out_func(function):
    def in_func(a):
        print('新增功能')
        function(a)
    return in_func

@out_func
def func1(x):
    print('func1', x)

func1(22)
  • 练习

原函数计算两数之和;
定义一个装饰器,给求和结果之前打印一名话:“以下为计算结果:”

def out_func(function):
    def in_func(a, b):
        print('以下为计算结果:')
        function(a, b)
    return in_func

@out_func
def sum_(a, b):
    rst = a + b
    print(rst)

sum_(2, 3)

4. 原函数有return如何处理?

def out_func(function):
    def in_func(x, y):
        print('以下为计算的结果:')
        return function(x, y)
    return in_func

@out_func           # func1 = out_func(func1)
def func1(x, y):
    """ 求和函数 """
    rst = x + y
    return rst

print(func1(2, 3))

5. 装饰器的通用参数形式

def out_func(function):
    def in_func(*args, **kwargs):     # 闭包参数的通用形式
        print('以下为计算的结果:')
        return function(*args, **kwargs)     # 返回值的通用形式
    return in_func

@out_func           # func1 = out_func(func1)
def func1(x=1,y=2):
    """ 求和函数 """
    rst = x + y
    return rst

print(func1(x=2, y=3))

@out_func
def func2():
    print('func2')
func2()

6. 多个装饰器

def out_func1(function):
    print('开始装饰...1')
    def in_func1(*args, **kwargs):     # 闭包参数的通用形式
        print('01-以下为计算的结果:')
        return function(*args, **kwargs)     # 返回值的通用形式
    return in_func1


def out_func2(function):
    print('开始装饰...2')
    def in_func2(*args, **kwargs):     # 闭包参数的通用形式
        print('02-以下为计算的结果:')
        return function(*args, **kwargs)     # 返回值的通用形式
    return in_func2

@out_func2          # func1 = out_func2(func1)
@out_func1          # func1 = out_func1(func1)
def func1(x=1,y=2):
    """ 求和函数 """
    rst = x + y
    return rst

print(func1())

"""
开始装饰...1
开始装饰...2
02-以下为计算的结果:
01-以下为计算的结果:
3
"""

7. 装饰器中传参

  • 案例

需求:将原函数执行指定次数?

def dec(n):
    def out_func(function):
        def in_func(*args, **kwargs):
            # 使用for来调用原函数func1
            for _ in range(n):
                function(*args, **kwargs)
        return in_func
    return out_func


@dec(3)     # dec(3) 返回 out_func
def func1():
    print('我中了500万...')


func1()
  • 练习

使用一个装饰器在函数的结果之前,打印:

  1. 这个是两数之和的结果
  2. 这个是两数之差的结果
def dec(flag):
    def out_func(function):
        def in_func(*args, **kwargs):
            # 判断是求和,还是求差
            if flag == '1':
                print('这个是两数之和的结果:')
            elif flag == '0':
                print('这个是两数之差的结果:')
            return function(*args, **kwargs)
        return in_func
    return out_func


@dec('1')
def func1(x, y):
    return x + y

print(func1(2, 3))


@dec('0')
def func2(x, y):
    return x - y

print(func2(5, 2))

8. 类装饰器

class MyDecrator(object):

    # 定义初始化方法,传入函数的引用
    def __init__(self, function):
        self.__function = function

    # 修改定义一个方法来返回一个装饰器
    def __call__(self, *args, **kwargs):
        print('登录某宝')
        return self.__function(*args, **kwargs)


@MyDecrator         # 相当于 func1 = MyDecrator(func1)
def func1():
    print('在某宝买东西...')

func1()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值