python中闭包函数与装饰器函数

闭包

首先知道闭包函数的语法特征:

  • 函数嵌套定义
  • 外部函数返回内部函数的引用
  • 内部函数可以调用外部函数的自由变量

外部函数的作用是创建内部函数并且返回内部函数的引用。

def line(k, b):
    """外部函数的作用: 创建内部函数并且返回内部函数的引用"""
    def line_in(x):
        y = k * x + b
        print(y)
    return line_in

line1 = line(1,1)
line1(10)
line1(20)

line2 = line(2,2)
line2(10)
line1(10)

line1和line2都是inner函数代码的引用 为什么运行结果不一样呢?
因为两个闭包为不同的对象且使用自由变量不一样,所以闭包运算的结果就不一样

闭包的内部函数可以修改自由变量的值,在内部函数使用nonlocal 关键字

def line(k, b):
    """外部函数的作用: 创建内部函数并且返回内部函数的引用"""
    # d = [k]
    def line_in(x):
        # py3中 nonlocal关键字用以修改 自由变量<不可变类型>
        nonlocal k
        k = k + 1
        y = k * x + b

        # py2 py3都支持的方式  -  间接使用 并没有修改真正k的值
        # d[0] += 1
        # y = d[0] * x + b
        print(y)
    return line_in

l1 = line(1,1)

# 打印内部函数的自由变量
print(l1.__closure__)
print(l1.__closure__[0])
print(l1.__closure__[0].cell_contents)
l1(99)

 (<cell at 0x000001EEFC387738: int object at 0x000000006E556DE0>, <cell at 0x000001EEFC387C48: int object at 0x000000006E556DE0>)
<cell at 0x000001EEFC387738: int object at 0x000000006E556DE0>
1
199

装饰器

使用装饰器,提供一个语法糖@(Synatax Sugar)。

装饰器是基于闭包函数,遵循封闭开放的代码设计原则,在保证不修改原代码的基础上扩展代码功能。

装饰器其实是闭包的特例, 其外部函数传的参数是函数名而已。

def check(func):
#def check(*args, **kwargs):
    def wrapper(*args, **kwargs):
        print("添加验证功能")
        func(*args, **kwargs)
        print("添加结束处理功能")
    return wrapper

@check
def func1(num):
    """不容易发生改变的功能"""
    print("this is fun1")
    print("fun1 num %s" % num)

@check
def func2(num1,num2):
    print("this is func2")
    print("func2 num1 %s num2 %s" % (num1, num2))


# func1 = check(func1)
func1(2)
func2(3, 4)

添加验证功能
this is fun1
fun1 num 2
添加结束处理功能
添加验证功能
this is func2
func2 num1 3 num2 4
添加结束处理功能

check是基于闭包的装饰器函数,func是被装饰函数的引用。

装饰器函数只能接收一个参数,即被装饰函数的引用。

装饰器可以装饰有任意参数的函数。

使用装饰器的方式,@装饰器名 放在被装饰函数的上一行。

装饰器的灵魂代码即为: func1 = check(func1)   

@check 就是对灵魂代码的封装,很方便

装饰器demo 统计冒泡排序耗时

import time
import random

def resume_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        # 接收func函数的返回值
        result = func(*args, **kwargs)
        end_time = time.time()
        cost_time = end_time - start_time
        print("cost_time:%s" % cost_time)
        # 返回func函数的返回值
        return result
    return wrapper


@resume_time
def bubble_sort(list):
    time.sleep(0.6)
    n = len(list)
    for i in range(n-1):
        for j in range(n-1-i):
            j = i + 1
            if list[j] > list[j+1]:
                list[j], list[j+1] = list[j+1], list[j]
    return list


lyst = [i for i in range(1000)]
random.shuffle(lyst)
print(bubble_sort(lyst))

多个装饰器修饰一个函数

# 定义函数:完成包裹数据
def makeBold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

# 定义函数:完成包裹数据
def makeItalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makeBold
def test1():
    return "hello world-1"

@makeItalic
def test2():
    return "hello world-2"

@makeBold
@makeItalic
def test3():
    return "hello world-3"

print(test1())
print(test2())
print(test3())
<b>hello world-1</b>
<i>hello world-2</i>
<b><i>hello world-3</i></b>
调用test3() 相当于makeBold(makeItalic(test3))()

由于装饰器函数只能接收一个参数,若想要多传入几个参数,使装饰器功能更加丰富如何做?

可以使用装饰器工厂,传入一个flag参数,当flag为0时耗时时间取浮点形式,flag为1时取整型。

import time

# 接收参数生成装饰器
def factory(flag):
    # flag 向下传递
    def resume_time(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            cost_time = end_time - start_time
            # 接收上层传来的flag
            if flag == 0:
                print("cost_time:%s" % cost_time)
            elif flag == 1:
                print("cost_time:%d" % int(cost_time))
            return result
        return wrapper
    return resume_time

# 调用factory函数传入flag为1
@factory(1)
def bubble_sort(list):
    time.sleep(0.6)
    n = len(list)
    for i in range(n-1):
        for j in range(n-1-i):
            j = i + 1
            if list[j] > list[j+1]:
                list[j], list[j+1] = list[j+1], list[j]
    return list


lyst = [i for i in range(1000)]
random.shuffle(lyst)
print(bubble_sort(lyst))
 下面为装饰过程
1. 调用factory(1)
2. 将步骤1得到的返回值,即resume_time返回, 然后resume_time(bubble_sort)
3. 将resume_time(bubble_sort)的结果返回,即wrapper
4. 让bubble_sort = wrapper,即bubble_sort现在指向wrapper

类装饰器 

装饰器函数其实是这样一个接口约束,它必须接受一个callbale对象作为参数,然后返回一个callable对象。在Pthon中一般callable对象都是函数,但还有一种情况就是某个对象重写了__call__()方法,那么这个对象就是callable的。

class Test(object):
    def __init__(self, func):
        print("---初始化---")
        print("func name is %s"%func.__name__)
        self.__func = func
    def __call__(self):
        print("---装饰器中的功能---")
        self.__func()

@Test
def test():
    print("----test---")
test()

---初始化---
func name is test
---装饰器中的功能---
----test---

说明:
1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
   并且会把test这个函数名当做参数传递到__init__方法中
   即在__init__方法中的属性__func指向了test指向的函数

2. test指向了用Test创建出来的实例对象 test = Test(test)

3. 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法

4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
   所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体

猜你喜欢

转载自blog.csdn.net/w18306890492/article/details/82910915