zoukankan      html  css  js  c++  java
  • python 装饰器

    一、装饰器的形成过程

    import time
    
    def func1():
        print("in fun1")
        time.sleep(8)
    
    def timer(func):
        def inner():
            start = time.time()
            func()
            print("执行时间", time.time() - start)
        return inner
    
    func1 = timer(func1)
    func1()

    碍眼的那句话就是还要在做一次赋值调用。。。

    你觉得碍眼,python的开发者也觉得碍眼,所以就为我们提供了一句语法糖来解决这个问题!

    import time
    
    def timer(func):
        def inner():
            start = time.time()
            func()
            print("执行时间", time.time() - start)
        return inner
    
    @timer
    def func1():
        print("in fun1")
        time.sleep(8)
    
    func1()

    到这里,我们可以简单的总结一下:

      装饰器的本质:一个闭包函数

      装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展

    还有一个问题要解决,刚刚我们讨论的装饰器都是装饰不带参数的函数,现在要装饰一个带参数的函数怎么办呢?

    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 #==> func1 = timer(func1) def func1(a,b): print('in func1') time.sleep(2) @timer #==> func2 = timer(func2) def func2(a): print('in func2 and get a:%s'%a) time.sleep(3) func1('aaaaaa','bbbbbb') func2('aaaaaa')

    我们还可以在装饰器上加上一点来完善它:

    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__)

    二、开放封闭原则

      1.对扩展是开放的

        为什么要对扩展开放呢?

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

      2.对修改是封闭的

        为什么要对修改封闭呢?

        就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

    装饰器完美的遵循了这个开放封闭原则。

     

    三、装饰器的主要功能和装饰器的固定结构

    装饰器的主要功能:

    在不改变函数调用方式的基础上在函数的前、后添加功能。

    装饰器的固定格式:

    装饰器的固定格式
    def timer(func):
        def inner(*args,**kwargs):
            '''执行函数之前要做的'''
            re = func(*args,**kwargs)
            '''执行函数之后要做的'''
            return re
        return inner
    
    
    装饰器的固定格式——wraps版
    from functools import wraps
    
    def deco(func):
        @wraps(func) #加在最内层函数正上方
        def wrapper(*args,**kwargs):
            return func(*args,**kwargs)
        return wrapper

    四、带参数的装饰器

    假如你有成千上万个函数使用了一个装饰器,现在你想把这些装饰器都取消掉,你要怎么做?

    一个一个的取消掉? 没日没夜忙活3天。。。

    过两天你领导想通了,再让你加上。。。

    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()

    五、多个装饰器装饰同一个函数

    有些时候,我们也会用到多个装饰器装饰同一个函数的情况。

    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()

     总结:

    不带参数的装饰器:

    • 外层函数:参数为func,func代表需要被装饰的函数;返回内层函数的引用;外层函数的取名做到见名思意
    • 内层函数:参数为*args,**kwargs,代表被装饰函数(func)的形参;内层函数返回被装饰函数执行完后的的返回值,所以一般这样写:return func(*args,**kwargs)

    带参数装饰器:

    跟不带参数的装饰器也类似,把所带参数作为最外层函数的形参,最外层函数返回第二层函数的引用,第二层函数返回最内层函数(wrapper)的引用,wrapper返回被装饰函数(func)执行完后的返回值,第二层函数的参数也是func,最内层函数的参数如果func没有其他参数也就是*args,**kwargs

    关于带参数的装饰器下面举个栗子,由于这里的func(需要被装饰的函数)代表django里的视图函数,所以除了*args,**kwargs,还有request这个特殊的参数:

    def login_check(*methods):
      def _login_check(func):
        def wrapper(request, *args, **kwargs):
          #获取token
          token = request.META.get('HTTP_AUTHORIZATION')
          if not methods:
            #如果当前没传任何参数,则直接返回视图函数
            return func(request, *args, **kwargs)
          else:
            #检查当前request.method 是否在参数列表中
            if request.method not in methods:
              return func(request, *args, **kwargs)
          #当前必须有token
          if not token:
            result = {'code': 109, 'error': 'Please give me token !!'}
            return JsonResponse(result)
    
          try:
            res = jwt.decode(token, TOKEN_KEY)
          except Exception as e:
            print('login check is error %s'%(e))
            result = {'code': 108, 'error': 'The token is wrong !!! '}
            return JsonResponse(result)
    
          #token校验成功
          username = res['username']
          try:
            user = UserProfile.objects.get(username=username)
          except:
            user = None
    
          if not user:
            result = {'code': 110, 'error': 'The user is not existed'}
            return JsonResponse(result)
    
          #将user 赋值给 request对象
          request.user = user
    
          return func(request, *args, **kwargs)
        return wrapper
      return _login_check

    methods代表视图函数里的reques.method,由于可能有多种可能,所以前面加一个*

    该装饰器的使用:

     

  • 相关阅读:
    eclipse下c/cpp " undefined reference to " or "launch failed binary not found"问题
    blockdev 设置文件预读大小
    宝宝语录
    CentOS修改主机名(hostname)
    subprocess报No such file or directory
    用ldap方式访问AD域的的错误解释
    英特尔的VTd技术是什么?
    This virtual machine requires the VMware keyboard support driver which is not installed
    Linux内核的文件预读详细详解
    UNP总结 Chapter 26~29 线程、IP选项、原始套接字、数据链路访问
  • 原文地址:https://www.cnblogs.com/hooo-1102/p/10830527.html
Copyright © 2011-2022 走看看