zoukankan      html  css  js  c++  java
  • Python-装饰器

    一、闭包函数

    闭包函数=函数嵌套定义+函数对象+名称空间与作用域

    闭包函数:在函数中(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。

    1、闭:指的是该函数是定义在一个函数内部的函数
    2、包:值得是该函数访问了一个来自于外层函数的变量

    为函数体传参的方法:

    '''方案一:直接使用参数的形式传递'''
    def wrapper(x):
        print(x)
    
    wrapper(111)
    wrapper(222)
    '''方案二:把函数体想要的参数包给它(即使用闭包的概念)'''
    def outter(x):
        def wrapper():  # wrapper = 闭包函数的内存地址
            print(x)
    
        return wrapper  # 一定不要加括号
    
    f1 = outter(111)  # f = 闭包函数的内存地址
    f2 = outter(222)  # f = 闭包函数的内存地址
    f1()
    f2()
    -----------
    111
    222
    

    乍一看会感觉使用闭包来传参数非常的麻烦,我们之前使用函数,需要参数都是直接传给他,方便也快捷,但是在某些场景下我们定死了某个函数无法直接传参数,那就必须通过其他方式传参数,即闭包的方式,下面介绍的装饰器就是闭包的使用。

    二、@符号的使用

    @符号作用: 
    (1) 可以自动把@符号下面的函数当成参数传递给装饰器
    (2) 把新函数进行返回,让新函数去替换旧函数,以实现功能的扩展.
    如:
    @timer  # 等于func1 = timer(func1),其目的是为了让使用者不改变调用方式,它依然在调用func1,但是实际已经被我们换成了timer
    def func1():
      print(1)
    

    三、叠加多个装饰器的运行步骤

    结论:(记住结论既可)

    加载顺序:自下而上
    执行顺序:自上而下运行内层的wrapper函数

    验证过程:

    # 叠加多个装饰器
    def deco1(func1):  # func1 = wrapper2的内存地址
        def wrapper1(*args,**kwargs):
            print('wrapper1====>')
            res1=func1(*args,**kwargs)
            return res1
        return wrapper1
    
    def deco2(func2):  # func2 = wrapper3的内存地址
        def wrapper2(*args,**kwargs):
            print('wrapper2====>')
            res2=func2(*args,**kwargs)
            return res2
        return wrapper2
    
    def deco3(func3):  # func3 = 最原始的那个被装饰函数的内存地址
        def wrapper3(*args,**kwargs):
            print('wrapper3====>')
            res3=func3(*args,**kwargs)
            return res3
        return wrapper3
    
            # index=wrapper1的内存地址
    @deco1  # deco1(wrapper2的内存地址)=>wrapper1的内存地址
    @deco2  # deco2(wrapper3的内存地址)=>wrapper2的内存地址
    @deco3  # deco3(最原始的那个被装饰函数的内存地址)=>wrapper3的内存地址  
    def index(x,y):
        print('index=>',x,y)
    
    index(1,2)
    -----------------------
    wrapper1====>'
    wrapper2====>
    wrapper3====>
    index=>1,2
    

    四、无参装饰器

    什么是装饰器:

    器:工具
    装饰:为被装饰者添加额外的功能
    

    为何要有装饰器:

    软件一旦上线运行之后,就应该遵循开放封闭原则:
    1、开放指的是对拓展新功能开放
    2、封闭指的是对修改源代码封闭
    这种情况下,我们在写新功能时,若需要用到新的参数,就无法直接通过原函数来传了,必须通过其他方式来传参,目前我们想到的方法,是两种传参的另一种的方式,闭包函数

    定义装饰器的目的:
    定义装饰器就是为了在遵循1和2的前提下来为其他函数添加新功能的

    ps:
    不修改被装饰对象指的是定义与调用都不能修改
    所以下述行为都违反了开放封闭原则:
    ①、修改被装饰对象定义时的源代码
    ②、修改被装饰对象的调用方式

    如何用装饰器:

    '''无参装饰器基本格式'''
    def wrapper(func):
        def inner(*args,**kwargs):
            res = func(*args,**kwargs)
            return func
        return inner 
      '''
      切记不能在此处加括号,加括号就代表运行,相当于先把inner运行一遍,然后拿到返回值return出去.
      return 出去的值是无法加括号在运行的。
      '''
    # 例子
    def wrapper(func):
        def inner(*args,**kwargs):
            res = func(*args,**kwargs)
            return res
        return inner()  # 切记不能在此处加括号,加括号就代表运行
    
    @wrapper
    def func1():
        print(11)
    
    func1()
    -----------------
    11
    TypeError: 'NoneType' object is not callable
    
    '''
    前面打印出来的11其实是运行wrapper里的inner产生的,所以以上程序的运行流程是
    1.先把func1传给wrapper,然后运行了wrapper中的代码,返回 inner()
    2.inner() 就开始执行inner函数,即运行了一遍func1,所以打印了11
    3.此时的func1 = inner()  而inner()又等于func1(),func1没有返回值,默认返回None
    4.变成了None(),报错
    '''
    

    无参装饰器的应用场景以及构建装饰器步骤:

    需求:有一个index函数,此时我们要在index函数基础上添加一个计算运算时间的功能,因为是公司项目,我们不能修改原函数的调用方式,也不能修改源代码。

    # 一、被装饰器对象index如下
    import time
    
    def index(x,y):
        time.sleep(1)
        print("index---->",x,y)
    
    index(1,2)
    
    # 二、为index添加计算运算时间与登录功能
    import time
    from functools import wraps
    
    def timmer(func):
        def wrapper(*args,**kwargs):
            start = time.time()
            res = func(*args,**kwargs)
            end = time.time()
            print(end-start)
            return res
        return wrapper
    
    def login(func):
        def wrapper(*args,**kwargs):
            name = 'yang'
            pwd = '123'
            inp_name = input("请输入您的用户名:").strip()
            inp_pwd = input("请输入您的密码").strip()
            if inp_name == name and inp_pwd == pwd:
                print("登录成功")
                res = func(*args,**kwargs)
                return res
        return wrapper
    
    @timmer
    @login
    def index(x,y):
        time.sleep(1)
        print("index---->",x,y)
    
    index(1,2)
    

    五、有参装饰器

    对于不再需要新参数的装饰器,两层就可以解决了,但是当我们的装饰器需要新的参数,如在登录的时候,我们要判断我们用户名与密码的来源,此时需要外部将来源传进来。而两层的装饰器中,第一层,是为了给原函数传参数,他的参数不能动,而第二层,因为装饰器语法@的原因,也已经定死了此处只能传一个函数名,也不能传参数,所以我们需要构造第三层来接受外部的参数,而在内部的函数可以在不改动自己依然可以获取到外层的函数.

    '''有参装饰器模版'''
    def outter2(x,y,z,a,b):
        def outter1(func):
            def wrapper(*args,**kwargs):
                res=func(*args,**kwargs)
                return res
            return wrapper
        return outter1
    

    例子:

    def outter(engine = 'file'):
        def deco2(func2):
            def wrapper2(*args,**kwargs):
                inp_name = input('username>>>: ').strip()
                inp_pwd = input('password>>>: ').strip()
    
                if engine == "file":
                    print('基于file的认证')
                    if inp_name == "egon" and inp_pwd == "123":
                        print('login successful')
                        res2=func2(*args,**kwargs)
                        return res2
                    else:
                        print('username or password error')
                elif engine == 'mysql':
                    print('基于mysql的认证')
                elif engine == 'ldap':
                    print('基于ldap的认证')
                else:
                    print('未知的engine')
            return wrapper2
        return deco2
    
    @outter(engine='mysql')  # @deco2 # index=deco2(index)
    def index(x,y):
        print('index=>',x,y)
    
    index(1,2)  # index=>wrapper
    

    六、类装饰器

    我们也可以用类来装饰函数

    # 利用类静态方法装饰
    class MyClass:
        @staticmethod
        def zhuangshi1(func):
            def newfunc():
                print("装饰前")
                func()
                print("装饰后")
            return newfunc
    
        def __call__(self, *args, **kwargs):
            return self.zhuangshi2(*args, **kwargs)
    
        def zhuangshi2(self,func):
            def newfunc():
                print('装饰前')
                func()
                print('装饰后')
            return newfunc
    
    
    # 利用类静态方法,本质和原来的用函数装饰一样
    @MyClass.zhuangshi1
    def func1():
        print('运行func1')
    
    func1()
    
    # 利用__call__方法
    @MyClass()
    def func2():
        print('运行了func2')
    
    func2()
    
  • 相关阅读:
    找工作最近的一些收获
    nginx的开源项目
    找工作要看的
    各种排序算法的C实现
    解析递归程序和非递归程序
    Python在centos下的安装
    centos -bash-4.1$ 不显示用户名路径
    python easy_install centos 下安装过程和原理解析
    网络基础学习:
    MATLAB常用数据类型的转换
  • 原文地址:https://www.cnblogs.com/chiyun/p/14063560.html
Copyright © 2011-2022 走看看