五:匿名函数,闭包,装饰器

一,匿名函数

1.1,匿名函数初识

匿名函数,顾名思义就是没有名字的函数,那么什么函数没有名字呢?这个就是我们以后面试或者工作中经常用匿名函数 lambda,也叫一句话函数。

例如正常函数:

def func(a,b):
    return a+b
print(func(3,4))

 

转换为匿名函数:

func = lambda a,b: a+b
print(func(3, 4))  # 7

 

1.2 语法

函数名 = lambda 参数:返回值

  1)此函数不是没有名字,他是有名字的,他的名字就是你给其设置的变量,比如func.

  2)lambda 是定义匿名函数的关键字,相当于函数的def.

  3)lambda 后面直接加形参,形参加多少都可以,只要用逗号隔开就行。

func = lambda a,b,*args,sex= 'alex',c,**kwargs: kwargs
print(func(3, 4,c=666,name='alex'))  # {'name': 'alex'}
# 所有类型的形参都可以加,但是一般使用匿名函数只是加位置参数,其他的用不到。

  4)返回值在冒号之后设置,返回值和正常的函数一样,可以是任意数据类型。

   5)匿名函数不管多复杂.只能写一行.且逻辑结束后直接返回数据

例题:

写匿名函数:接收一个可切片的数据,返回索引为0与2的对应的元素(元组形式)。

func = lambda x:(x[0],x[2])
print(func('afafasd'))
View Code

写匿名函数:接收两个int参数,将较大的数据返回。

func = lambda x,y: x if x > y else y
print(func(3,100))
View Code

 

二,闭包

2.1 定义

  1. 闭包是嵌套在函数中的函数。

  2. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。

如何判断闭包,例子:

# 例一:
def wrapper():
    a = 1
    def inner():
        print(a)
    return inner
ret = wrapper()
​
# 例二:
a = 2
def wrapper():
    def inner():
        print(a)
    return inner
ret = wrapper()
​
​
# 例三:
def wrapper(a,b):
    def inner():
        print(a)
        print(b)
    return inner
a = 2
b = 3
ret = wrapper(a,b)

 

以上三个例子,最难判断的是第三个,其实第三个也是闭包,如果我们每次去研究代码判断其是不是闭包,有一些不科学,或者过于麻烦了,那么有一些函数的属性是可以获取到此函数是否拥有自由变量的,如果此函数拥有自由变量,那么就可以侧面证明其是否是闭包函数了了解):

def make_averager():
​
    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)
​
    return averager
avg = make_averager()
# 函数名.__code__.co_freevars 查看函数的自由变量
print(avg.__code__.co_freevars)  # ('series',)
当然还有一些参数,仅供了解:

# 函数名.__code__.co_freevars 查看函数的自由变量
print(avg.__code__.co_freevars)  # ('series',)
# 函数名.__code__.co_varnames 查看函数的局部变量
print(avg.__code__.co_varnames)  # ('new_value', 'total')
# 函数名.__closure__ 获取具体的自由变量对象,也就是cell对象。
# (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,)
# cell_contents 自由变量具体的值
print(avg.__closure__[0].cell_contents)  # []
View Code

2.2 闭包的作用

保存局部信息不被销毁,保证数据的安全性。

2.3 闭包的应用

  1. 可以保存一些非全局变量但是不易被销毁、改变的数据。

  2. 装饰器。

3,装饰器

3.1 开放封闭原则

开放封闭原则具体具体定义是这样:

    1.对扩展是开放的

        我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

    2.对修改是封闭的

        就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对函数内部进行修改,或者修改了函数的调用方式,很有可能影响其他已经在使用该函数的用户。OK,理解了开封封闭原则之后,我们聊聊装饰器。

什么是装饰器?从字面意思来分析,先说装饰,什么是装饰? 装饰就是添加新的,比如你家刚买的房子,下一步就是按照自己的喜欢的方式设计,进行装修,装饰,地板,墙面,家电等等。什么是器?器就是工具,也是功能,那装饰器就好理解了:就是添加新的功能。

    比如我现在不会飞,怎么才能让我会飞?给我加一个翅膀,我就能飞了。那么你给我加一个翅膀,它会改变我原来的行为么?我之前的吃喝拉撒睡等生活方式都不会改变。它就是在我原来的基础上,添加了一个新的功能。

3.2 初始装饰器

3.2.1 定义

在不改变原被装饰的函数的源代码以及调用方式下,为其添加额外的功能。

3.2.2 装饰器的推导

例如,你想测试某个函数的执行效率,你会怎样做?

版本1

import time
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')
​
start_time = time.time()
index()
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')

 版本1分析:你现在已经完成了这个需求,但是有什么问题没有? 虽然你只写了四行代码,但是你完成的是一个测试其他函数的执行效率的功能,如果让你测试一下,小张,小李,小刘的函数效率呢? 你是不是全得复制:

import time
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园首页')
​
def home(name):
    time.sleep(3)  # 模拟一下网络延迟以及代码的效率
    print(f'欢迎访问{name}主页')
​
start_time = time.time()
index()
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')
​
start_time = time.time()
home('太白')
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')
​

重复代码太多了,所以要想解决重复代码的问题,怎么做?我们是不是学过函数,函数就是以功能为导向,减少重复代码,好我们继续整改。

版本2:

import time
​
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')
​
def inner():
    start_time = time.time()
    index()
    end_time = time.time()
    print(f'此函数的执行效率为{end_time-start_time}')
​
inner()

你要是像上面那么做,每次测试其他同事的代码还需要手动改,这样是不是太low了?所以如何变成动态测试其他函数?我们是不是学过函数的传参?能否将被装饰函数的函数名作为函数的参数传递进去呢?

版本3:

import time
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')
​
def home(name):
    time.sleep(3)  # 模拟一下网络延迟以及代码的效率
    print(f'欢迎访问{name}主页')
​
def timmer(func):  # func == index 函数
    start_time = time.time()
    func()  # index()
    end_time = time.time()
    print(f'此函数的执行效率为{end_time-start_time}')
​
timmer(index)

首先,index函数除了完成了自己之前的功能,还增加了一个测试执行效率的功能,对不?所以也符合开放原则。 其次,index函数源码改变了么?没有,但是执行方式改变了,所以不符合封闭原则。 原来如何执行? index() 现在如何执行? inner(index),这样会造成什么问题? 假如index在你的项目中被100处调用,那么这相应的100处调用我都得改成inner(index)。 非常麻烦,也不符合开放封闭原则。

版本4

def timer(func):  # func = index
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner
​
# f = timer(index)
# f()

 

3.2.3 带返回值的装饰器

def timer(func):  # func = index
    def inner():
        start_time = time.time()
        ret = func()
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
        return ret
    return inner
​
index = timer(index)  # inner
print(index())  # print(inner())

 

3.2.4 被装饰函数带参数的装饰器

import time
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')
    return '访问成功'def home(name,age):
    time.sleep(3)  # 模拟一下网络延迟以及代码的效率
    print(name,age)
    print(f'欢迎访问{name}主页')
​
def timer(func):  # func = home
    def inner(*args,**kwargs):  # 函数定义时,*代表聚合:所以你的args = ('太白',18)
        start_time = time.time()
        func(*args,**kwargs)  # 函数的执行时,*代表打散:所以*args --> *('太白',18)--> func('太白',18)
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner
​
​
​
home = timer(home)
home('太白',18)

 

3.2.5 被装饰函数带返回值的装饰器

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func2('aaaaaa')
print(func2('aaaaaa'))

刚刚那个装饰器已经非常完美了,但是正常我们情况下查看函数的一些信息的方法在此处都会失效

def index():
    '''这是一个主页信息'''
    print('from index')

print(index.__doc__)    #查看函数注释的方法
print(index.__name__)   #查看函数名的方法

为了不让他们失效,我们还要在装饰器上加上一点来完善它:

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)
print(index.__name__)

 

3.3 总结

1,装饰器的主要功能:在不改变调用方式的基础上在函数的前、后添加功能

2,装饰器的固定格式

def timer(func):
    def inner(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
    return inner

 

 

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方,为了打印出被装饰函数的注释,和被装饰函数的名字
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

3.4带参数的装饰器

def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''执行函数之前要做的''')
            re = func(*args,**kwargs)
            if flag:
                print('''执行函数之后要做的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()

 

3.5 多个装饰器装饰同一个函数

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()
# 输出结果为:
'''
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
'''

 

posted @ 2020-08-27 07:17  勇敢面对difficult  阅读(122)  评论(0)    收藏  举报