python闭包函数与装饰器

闭包函数

  • 1.什么是闭包函数

    内部函数包含对外部作用域而非全局作用域的引用。作用域的关系在函数定义阶段就已经被固定,与调用位置无关;在任意位置调用函数都要跑到定义函数时的位置寻找作用域关系。

例:

def f1():

    x = 1

    def inner():

        print(x)

    return inner

func = f1()

def f2():

    x = 11111

    func()

f2()

运行结果为 :   x = 1

  • 闭包函数:

def outter():

    x = 1

    def inner():

        print(x)

    return inner

f = outter()

def f2():

    x = 1111

    f()

f2()

def f3():

    x = 222

    f()

f3()

f2 和f3的运行结果均为 x = 1

  • 为函数体传值的方式之一:使用参数的方式传值

def inner(x):

    print(x)

inner(1)

inner(11)

  • 为函数体传值的方式之二:使用闭包方式

def outer(x):

    def inner():

        print(x)

    return inner 

f = outter(222)

f()

  • 闭包的意义与应用
  • 意义:返回的函数对象,不仅是单独一个对象,还将外层包裹这个函数的作用域一起返回,这使得,该函数在何处调用都优先使用自己外层包裹的作用域。

例:

import requests

def get(url):

    response = requests.get(url)

    if response.status_code == 200:

        print(response.text)

get('http://www.baidu.com')

get('http://www.python.org')

利用闭包函数的特性改进如下:

import requests  #调用requests模块应该先安装此模块,在Windows命令行输入:pip install requests

def outter(url):

    def get():

        response = requests.get(url)

        if response.status_code == 200:

            print(response.text)

    return get

baidu = outter('https://www.baidu.com')

python = outter('https://www.python.org')

baidu()

python()

装饰器

  • 什么是装饰器:

    通俗来讲,装饰器就是闭包函数的一种应用场景。开放封闭原则:对修改封闭,对扩展开放。装饰器本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。

强调装饰器的原则:1.不修改装饰对象的源代码;2.不修改被装饰对象的调用方式。

装饰器目标:在遵循1和2的前提下,为被装饰对象添加新功能。

  • 如何使用装饰器:

例:

import time

def index():

    start = time.time()

    print('this is index')

    time.sleep(3)

    stop = time.time()

    print('run time is %s '%(stop - start))

index()

我们将上述代码进行第一次改进:

import time

def index():

    print('this is index')

    time.sleep(3)

def f2():

    print('from f2')

    time.sleep(2)

start = time.time()

index()

stop = time.time()

print('run time is %s '%(stop - start))

若要获取f2的运行时间也是一样:

start = time.time()

f2()

stop = time.time()

print('run time is %s'%(stop - start))

这种改进方式不合理,每次调用都需要重复写代码,我们继续改进:

Import time 

def index():

    print('this is index')

    time.sleep(3)

def timmer(func):

    start = time.time()

    func()

    stop = time.time()

    print('run time is %s'%(stop - start))

timmer(index)

我们发现还是不合理,index被固定住了,timmer这个装饰器仅能用来装饰index,继续改进:

import time 

def index():

    print('this is index')

    time.sleep(3)

def timmer(func):

    def inner():

        start = time.time()

        func()

        stop = time.time()

    return inner 

f = timmer(index)

f()

上述改进稍微好了点,但是不符合装饰器的两大原则之一(我们改变了原函数的调用方式),于是有以下代码

import time

def index():

    print('this is index')

    time.sleep

def timmer(func):

    def wrapper():

        start = time.time()

        func()

        stop = time.time()

        print('run time is %s'%(stop - start))

    return wrapper

index = timmer(index)  # index = wrapper函数的内存地址

index()

至此,我们的本着装饰器的两大原则将以上功能实现了,但是问题随之而来,如果,index函数有返回值呢,如果另外一个函数需要用到timmer这个装饰器,且带有参数,以上的修改就不能满足了。

我们继续改进如下:

import time 

def index():

    print('this is index')

    time.sleep(3)

    return 456

def home(name):

    print('welcome %s to home page'%name)

    time.sleep(2)

def timmer(func):

    def wrapper(*args, **kwargs):

        start = time.time()

        res = func(*args, **keargs)

        stop = time.time()

        print('run time is %s'%(stop - start))

        return res

    return wrapper

index = timmer(index)

home = timmer(home)

res = index()

home = ('zhangsan')

至此,我们的修改将近完善了,python内置了更简洁的方法,如下:

import time

def time(func):

    def wrapper(*args, **kwargs):

        start = time.time()

        res = func(*args, **kwargs)

        stop = time.time()

        print('run time is %s'%(stop - start))

        return res

    return wrapper

@timmer    #等同于index = timmer(index)

def index():

    print('this is index')

    time.sleep(3)

    return 234

@timmer  #等同于home = timmer(home)

def home(name):

    print('welcome %s to home page'%name)

    time.sleep(2)

res = index()

home('zhangsan')

上述@timmer写法在python中叫‘语法棒棒糖’在被装饰对象的正上方,并且是单独一行写上@装饰器名

  • 有参装饰器

介绍了对带参数的函数和有返回值的函数进行装饰,那么有没有带参数的装饰器呢,如果有,该怎么用呢,我们通过以下一段代码来看:(我们假设做一个登录验证的小功能)

import time

current_user = {'user': None}

def deco(func):

    def wrapper(*args, **kwargs):

        if current_user['user']:

            res = func(*args, **kwargs)

            return res  # 已经登录过,无需再输入用户名和密码

        user = input('username>>: ').strip()

        pwd = input('password>>: ').strip()     

        if user == 'zhangsan'  and pwd == '123' :  # 我们在这里假设数据库里面的一位用户的账号密码是这样

            print('login sucessful') #记录用户登录状态

            current_user = user

            res = func(*args, **kwargs)

            return res

        else:

            print('username or password error')

    return wrapper

@deco

def index():

    print('welcome to index page')

    time.sleep(0.5)

@deco 

def home(name):

    print('welcome %s to home page'%name)

    time.sleep(0.5)

index()

home('zhangsan')

我们很快就发现上面写的有些不合理,若是用户有数据库方面的验证,还有一些别的数据文件验证方式呢,于是我们改进如下:

import time

current_user = {'user':None}

def auth(engine = 'file'):

    def deco(func):

        def wrapper(*args, **kwargs):

            if current_user['user']:  #记录登录状态。

                res = func(*args, **kwargs)

                return res

            user = input('username>>: ').strip()

            pwd = input('password>>: ').strip()

            if engine == 'file' :   #假设基于文件认证

                if user == 'zhangsan' and pwd == '123' :

                    print('login sucessful')

                    current_user['user'] = user  #记住登录状态

                    res = func(*args, **kwargs)

                    return res

                else:

                    print('username or password error')

            elif engine == 'mysql' :  #代码我这边就先不写了 -_-||

                print('mysql')

            else:

                print('etc')

        return wrapper

    return deco 

@auth(engine = 'file')

def index():

    print('welcome to index page')

    time.sleep(1)

@auth(engine = 'file')

def home(name)    

    print('welcome to %s home page'%name)

    time.sleep(1)

index()

home('zhangsan')

简单理解,带参数的装饰器就是在原闭包的基础上又加上一层闭包,好处是针对不同参数做不同的处理。

那介绍了这么多,如果在实际应用中针对每个类别都需要写个装饰器的话,估计累死了。有没有通用的万能装饰器呢,看如下代码:

def outer(func):

    def inner(*args, **kwargs)

        res = func(*args, **kwargs)

        return res

    return inner

@outer

def test():

    print('a test')

@outer 

def test2():

    print('another test')

    return 'python'

@outer

 def test3(x):

    print('last test for %s'%x)

test()

test2()

test3()

把上面几种示例结合起来,就完成了通用装饰器的功能,原理都同上,就不过多废话了。



猜你喜欢

转载自blog.csdn.net/zhou_le/article/details/80656404