函数(三)闭包函数与装饰器

1.闭包函数

什么是闭包函数

  定义在函数内部的函数,并且内部函数会引用外部函数的变量并且外部函数会返回内部函数的函数名(内函数的地址)

闭包函数的意义

  要想使用内部函数,必须通过外部函数,相当于把内函数包住了

直接传参

def index():
    x = 1
    print(x)
index()

 

简单的一个闭包函数

def outer():
    x = 1  # 外部函数的变量
    def inner():
        print(x)  # 内部函数引用了外部函数的变量
    return inner  # 外部函数返回了内部函数的函数名
res = outer()
res()

 

requests模块

  可以做一个简单的小爬虫模块

import requests
def index(url):
    response = requests.get(url)
    if response.status_code == 200:
        print(len(response.text))
index('https://www.baidu.com')

 

2.装饰器简介

可调用的:callable

  简单的来说就是可以加括号()的,不仅函数是可调用的,装饰器也是可调用的

什么是装饰器

  给被装饰对象添加新功能的工具

为什么要用装饰器

  开发封闭原则:对扩展开放,对修改封闭

  具体的来说就是:不改变源代码的情况下增加新的功能

装饰器必须遵循的两个规则

  1.不改变源代码

  2.不改变源代码的调用方式

 

3.一个简单的装饰器

下面来输出一个简单的装饰器

def index(x):
    print(x)
    return 'index'
index(1)  # 这是原来函数输出的

def outer(func):  # 这里的func在下面被index替代
    print(111)
    def inner(*args,**kwargs):
        print(222)
        res = func(*args,**kwargs)  # 这里相当于index()
        return res
    return inner

index = outer(index)  # 括号里的index是上面函数名index,前面的index是重新定义的一个变量名,跟之前的没有任何关系,index相当于inner
index(2)  # 这是新函数输出的,这相当于inner(2)

"1"是第一个index(1)输出的值,后面的是index(2)输出的值,这即表现出了装饰器不改变代码和调用方式的情况下可以增加功能

 这是运行的顺序图,函数只要遇到括号()就会执行函数体代码,所以这里的顺序是这样的

 

统计函数运行时间,time模块

def index():
    print('这是index')

import time
def outer(func):
    print('这是outer')
    def inner():
        time1 = time.time()  # 获取时间戳,离1970-01-01,00:00:00有多少秒
        func()
        time.sleep(3)  # 让cpu暂停三秒
        print('这是inner')
        time2 = time.time()
        print(time2 - time1)  # 两个时间戳相减等于使用多少时间
    return inner

index = outer(index)
index()

 

 

4.装饰器升级版

在函数中加入不定长参数*args和**kwargs可以使函数传入任意数量的参数

def index(name,age = 18):
    print(name,age)
    return 'index函数的返回值'

def outer(func):
    print('这是outer')
    def inner(*args,**kwargs):  # 使用不定长参数可以使装饰器传任意的值
        print('这是inner')
        res = func(*args,**kwargs)
        print(res)  # 使用res可以返回原函数的返回值
        return res
    return inner

index = outer(index)
index('sxc',age = 19)

 

5.装饰器语法糖

语法糖的用法:把装饰器提到前面,然后在被装饰的原函数前加@+装饰器名

还是使用上面的函数

def outer(func):
    print('这是outer')
    def inner(*args,**kwargs):  # 使用不定长参数可以使装饰器传任意的值
        print('这是inner')
        res = func(*args,**kwargs)
        print(res)  # 使用res可以返回原函数的返回值
        return res
    return inner

@outer
def index(name,age = 18):
    print(name,age)
    return 'index函数的返回值'
index('sxc',age = 19)

这样我们可以用一行代码@outer返回原来的index = outer(index),变量重新赋值的问题

注意:语法糖和被装饰对象在书写的时候在紧紧的挨在一起,不能有空行

 

6.装饰器模板和认证装饰器

装饰器模版

def outer(func):
    def inner(*args,**kwargs):  # 可以传任何参数
        # 执行原函数之前的代码
        res = func(*args,**kwargs)  # 执行原函数
        # 执行原函数之后的代码
        return res  # 返回原函数的值
    return inner

 

认证装饰器

import time
user_login ={'islogin':False}
def outer(func):
    def inner(*args,**kwargs):
        if not user_login['islogin']:  # 判断用户是否已经登录,如登录直接进入,未登录则进行登录操作
            username = input('请输入您的用户名>>>:')
            pwd = input('请输入您的密码>>>:')
            if username == 'sxc' and pwd =='123':
                print('登录成功!')
                user_login['islogin'] = True
                res = func(*args,**kwargs)
                return res
            else:
                print('用户名或密码错误')
        else:
            user_login['islogin'] = True
            res = func(*args,**kwargs)
            return res
    return inner

@outer
def index():  # 执行本函数时未登录
    time.sleep(1)
    print('未登录,登录完成后进入本页面')
index()

@outer
def index2():  # 执行本函数时已登录
    time.sleep(3)
    print('已登录,直接进入本页面')
index2()

这个认证装饰器可以判断用户是否已经登录,并且登录操作只需进行一次,下面的函数块都不会进行登录操作

 

7.多层装饰器

一般见到的多层装饰器都是在装饰器后再套一层装饰器,最外层函数也能传参,这样可以执行更多的功能

import time
user_login ={'islogin':False}
def out_outer(today):
    # 再套一层装饰器的原因是可以传入更多的参数,从而执行更多的功能
    def outer(func):
        def inner(*args,**kwargs):
            if today == 'monday':
                if not user_login['islogin']:  # 判断用户是否已经登录,如登录直接进入,未登录则进行登录操作
                    username = input('请输入您的用户名>>>:')
                    pwd = input('请输入您的密码>>>:')
                    if username == 'sxc' and pwd =='123':
                        print('登录成功!')
                        user_login['islogin'] = True
                        res = func(*args,**kwargs)
                        return res
                    else:
                        print('用户名或密码错误')
                else:
                    user_login['islogin'] = True
                    res = func(*args,**kwargs)
                    return res
            else:
                print('今天你不能登录')
        return inner
    return outer

@out_outer('sunday')  # 可以传更多的参数,可以进行更多的判断,执行更多的功能
def index():  # 执行本函数时未登录
    time.sleep(1)
    print('未登录,登录完成后进入本页面')
index()

@out_outer('monday')
def index2():  # 执行本函数时已登录
    time.sleep(3)
    print('已登录,直接进入本页面')
index2()import time
user_login ={'islogin':False}
def out_outer(today):
    # 再套一层装饰器的原因是可以传入更多的参数,从而执行更多的功能
    def outer(func):
        def inner(*args,**kwargs):
            if today == 'monday':
                if not user_login['islogin']:  # 判断用户是否已经登录,如登录直接进入,未登录则进行登录操作
                    username = input('请输入您的用户名>>>:')
                    pwd = input('请输入您的密码>>>:')
                    if username == 'sxc' and pwd =='123':
                        print('登录成功!')
                        user_login['islogin'] = True
                        res = func(*args,**kwargs)
                        return res
                    else:
                        print('用户名或密码错误')
                else:
                    user_login['islogin'] = True
                    res = func(*args,**kwargs)
                    return res
            else:
                print('今天你不能登录')
        return inner
    return outer

@out_outer('sunday')  # 可以传更多的参数,可以进行更多的判断,执行更多的功能
def index():  # 执行本函数时未登录
    time.sleep(1)
    print('未登录,登录完成后进入本页面')
index()

@out_outer('monday')
def index2():  # 执行本函数时已登录
    time.sleep(3)
    print('已登录,直接进入本页面')
index2()

不是只能传一个参数,还能传更多的参数

 

装饰器执行的顺序:装饰时从下往上,执行时从上往下

def outter1(func1):
    print('加载了outter1')
    def wrapper1(*args,**kwargs):
        print('执行了wrapper1')
        res1=func1(*args,**kwargs)
        return res1
    return wrapper1
def outter2(func2):
    print('加载了outter2')
    def wrapper2(*args,**kwargs):
        print('执行了wrapper2')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2
def outter3(func3):
    print('加载了outter3')
    def wrapper3(*args,**kwargs):
        print('执行了wrapper3')
        res3=func3(*args,**kwargs)
        return res3
    return wrapper3
@outter1  # index = outter1(wapper2)
@outter2  # wrapper2 = outter2(wrapper3)
@outter3  # wrapper3 = outter3(最原始的index函数内存地址)
def index():
    print('from index')
index()

 

注意:

  装饰器在装饰的时候 顺序从下往上
  装饰器在执行的时候 顺序从上往下

 

8.装饰器的补充(完善装饰器)

def outer(func):
    '''
    这是outer的注释
    :param func:
    :return:
    '''
    def inner(*args,**kwargs):  # 可以传任何参数
        '''
        这是inner的注释
        :param args:
        :param kwargs:
        :return:
        '''
        # 执行原函数之前的代码
        res = func(*args,**kwargs)  # 执行原函数
        # 执行原函数之后的代码
        return res  # 返回原函数的值
    return inner

@outer
def index():  # 原函数
    '''
    这是index的注释
    :return:
    '''
    return '原函数的返回值'

print(help(index))  # 当我们在打印index的注释的时候会打印装饰器内index指代的inner的注释
print(index.__name__)  # 查看函数名字符串形式,这也会打印inner

打印注释和函数名字符串形式的时候都会打印原字符串,虽然功能没破坏,但是违背了我们不想让用户察觉的原则

这时候我们就可以使用(from functools import wraps)内置方法改善装饰器

在装饰器外部声明这个方法,再在装饰器外层调用这个方法

输出结果

这样就可以实现:

  用户查看被装饰函数的函数名的时候查看到的就是被装饰函数本身
  用户查看被装饰函数的注释的时候查看到的就是被装饰函数的注释

 11

posted @ 2019-07-11 23:59  瓜落桥下塘  阅读(179)  评论(0编辑  收藏  举报