zoukankan      html  css  js  c++  java
  • python装饰器的深度探究

    1.讲装饰器一般讲到这种代码就可以了,但这篇博客会介绍更多:

    def deco(func):
        def wrapper():
            print("start")
            func() #调用函数
            print("end")
        return wrapper
     
    @deco
    def myfun():
        print "run"
     
    myfun()
    

    2.装饰任意参数的函数:

    def deco(func):
        def warpper(*args,**kwargs):
            print("start")
            func(*args,**kwargs)
            print("end")
        return warpper
         
    @deco
    def myfun1(param1):
        print "run with param %s"%(param1)
    

    装饰器会重写函数的名字和注释文档,@wraps(func)可以解决这个问题

    from functools import wraps
    
    def deco(func):
        @wraps(func)
        def warpper(*args,**kwargs):
            print("start")
            func(*args,**kwargs)
            print("end")
        return warpper
    

    3.django自定义装饰器实现登录验证

    def Check_Login(func):  #自定义登录验证装饰器
        def warpper(request,*args,**kwargs):
            is_login = request.session.get('IS_LOGIN', False)
            if is_login:
                func(request,*args,**kwargs)
            else:
                return HttpResponseRedirect("/polls/login_user")
        return warpper
     
    def login_user(request):
        if request.method == 'POST':
            form = LoginForm(request.POST)
            if form.is_valid():
    		    # 获取post数据,例如 {'username': u'yang1', 'password': 111}
                all_data = form.clean()   
    			u = all_data['Form_username']
    			p = all_data['Form_password']
                exist = User.objects.filter(username = u,password = p).first()
                if exist:
                    request.session['IS_LOGIN'] = True  #设置session的随机字段值
                    request.session['uname'] = exist.username   #设置uname字段为登录用户
                    return HttpResponseRedirect('/polls/home')
                else:
                    return HttpResponse("账户或密码错误")
        else:
            form = LoginForm()
        return render(request, 'polls/login_user.html', {'form': form})
     
    @Check_Login
    def home(request):
            username = request.session.get('uname', False)   #获取登录用户名
            return render(request, 'polls/home.html', {'username': username})
    

    4.带参数的装饰器

    装饰器接受一个函数作为参数,这个毋庸置疑.但是有时候我们需要装饰器接受另外的参数,

    此时需要再加一层函数,实际上是定义了一个生成装饰器的工厂函数,

    调用它,搭配需要的参数,来返回合适的装饰器.

    from functools import wraps
     
    def logit(logfile='out.log'):
        def logging_decorator(func):
            @wraps(func)
            def wrapped_function(*args, **kwargs):
                log_string = func.__name__ + " was called"
                print(log_string)
                # 打开logfile,并写入内容
                with open(logfile, 'a') as opened_file:
                    # 现在将日志打到指定的logfile
                    opened_file.write(log_string + '
    ')
                return func(*args, **kwargs)
            return wrapped_function
        return logging_decorator
     
    @logit()
    def myfunc1():
        pass
    

    5.装饰器类

    比方说有时你只想打日志到一个文件;

    而有时你想把引起你注意的问题发送到一个email,同时也保留日志,留个记录.

    from functools import wraps
     
    class logit(object):
        def __init__(self, logfile='out.log'):
            self.logfile = logfile
     
        def __call__(self, func):
            @wraps(func)
            def wrapped_function(*args, **kwargs):
                log_string = func.__name__ + " was called"
                print(log_string)
                # 打开logfile并写入
                with open(self.logfile, 'a') as opened_file:
                    # 现在将日志打到指定的文件
                    opened_file.write(log_string + '
    ')
                # 现在,发送一个通知
                self.notify()
                return func(*args, **kwargs)
            return wrapped_function
     
        def notify(self):
            # logit只打日志,不做别的
            pass
    

    使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,

    就会调用此方法.这个实现有一个附加优势,在于比嵌套函数的方式更加整洁.

    给 logit 创建子类,来添加 email 的功能:

    class email_logit(logit):
        '''
        一个logit的实现版本,可以在函数调用时发送email给管理员
        '''
        def __init__(self, email='admin@myproject.com', *args, **kwargs):
            self.email = email
            super(email_logit, self).__init__(*args, **kwargs)
     
        def notify(self):
            # 发送一封email到self.email
            # 这里就不做实现了
            pass
    

    从现在起,@email_logit将会和@logit产生同样的效果,

    但是在打日志的基础上,还会多发送一封邮件给管理员.

    6.装饰器的执行顺序总结

    def decorator_a(func):
        print('Get in decorator_a')
    
        def inner_a(*args, **kwargs):
            print('Get in inner_a')
            return func(*args, **kwargs)
        return inner_a
    
    
    def decorator_b(func):
        print('Get in decorator_b')
    
        def inner_b(*args, **kwargs):
            print('Get in inner_b')
            return func(*args, **kwargs)
        return inner_b
    
    
    @decorator_a
    @decorator_b
    def f(x):
        print('Get in f')
        return x * 2
    当你不调用就这么执行时,返回结果是:
    Get in decorator_b
    Get in decorator_a
    当你调用了f(1),返回结果是:
    Get in decorator_b
    Get in decorator_a
    Get in inner_a
    Get in inner_b
    Get in f
    

    所以:定义过程是自下向上,执行顺序是自上向下.

    还有一个<装饰器带类参数>,有点深,看不懂,用到的时候再研究.

    参考地址:http://www.runoob.com/w3cnote/python-func-decorators.html

    参考地址:https://www.cnblogs.com/honey-badger/p/8978168.html

  • 相关阅读:
    十四、自定义构建购物计算组件&表单组件
    从微信小程序到鸿蒙js开发【04】——list组件
    HarmonyOS应用开发-Component体系介绍(一)
    HarmonyOS单模块编译与源码导读
    烧录失败导致boot无法加载的解决措施,再也不怕烧成砖了
    从微信小程序到鸿蒙js开发【03】——fetch获取数据&简单天气预报
    抛弃床的温暖,只为了它丨云库一款跑在鸿蒙系统上的应用丨篇二
    swift 弹窗
    Swift字符串常用操作总结
    swift 随机数
  • 原文地址:https://www.cnblogs.com/fawaikuangtu123/p/9970147.html
Copyright © 2011-2022 走看看