• python装饰器


      装饰器,顾名思义,是用来装饰某样东西的。那么它是用来装饰什么东东的呢?答案是函数。总结一下,装饰器就是修改其他函数某些功能的函数。

      接下来,让我们一步一步来编写一个装饰器。

      一、什么是函数

    #在这里myfunc是一个变量,也是一个函数。二myfunc()则是一个函数调用
    def myfunc(args = "Python"):
    	return "Hello " + args
    	
    myval = myfunc#声明一个变量将myfunc赋值给这个新变量,实际上myfunc是函数的地址(注意当前没有小括号),myval变量保存的是myfunc函数的地址
    
    print(myval,myfunc)#当前输出的内容是相同的,即myfunc函数的地址
    
    del myfunc#将原来的myfunc变量删除,之后myfunc变量将不可见,也就不能再调用myfunc函数了
    
    print(myval,myval())#输出myval变量的值,并调用该函数
    
    print(myfunc)#会报错,因为该变量已被删除
    print(myfunc())#如果没有上面的输出语句,同样会报错,因为myfunc变量被删除,也就不涉及到调用myfunc函数了
    

     输出结果如下:

     二、在函数中定义函数

    def myfunc(args = "Python"):
    	print("Hello "+args)
    	def insideFunc():
    		return "i am the inside function"
    		
    	print(insideFunc())
    	print("now i am the outside function")
    	
    
    myfunc()
    insideFunc()
    

     输出结果如下:

    三、从函数中返回函数

    def myfunc(args = "Python"):
    	print("Hello "+args)
    	def insideFunc():
    		return "i am the inside function"
    		
    	print(insideFunc())
    	print("now i am the outside function")
    	
    	return insideFunc;#注意:该出返回没有小括号,否则,该处将是返回insideFunc函数执行后的结果
    	
    
    func = myfunc();
    print(func,func())
    

     输出结果如下:

    四、将函数作为参数传递给另一个函数

    def myfunc(args = "Python"):
    	print("Hello "+args)
    	return "funny"
    	
    def doSomething(func):
    	print("now ,in function doSomething")
    	print(func())
    
    
    doSomething(myfunc)
    

      输出结果如下:

     五、应用以上只是,编写第一个装饰器函数

    def myDecrator(func):
    	def insideFunc():
    		print("before func out")
    		func()
    		print("after func out")
    		
    	return insideFunc;
    	
    
    def myfunc(args = "python"):
    	print("Hello "+args)
    	
    
    myfunc = myDecrator(myfunc)
    myfunc()
    	
    

      

     输出结果如下:

    六、使用@来运行之前的代码

    def myDecrator(func):
    	def insideFunc():
    		print("before func out")
    		func()
    		print("after func out")
    		
    	return insideFunc;
    	
    
    @myDecrator
    def myfunc(args = "python"):
    	print("Hello "+args)
    	
    myfunc()
    print("Over")
    	
    

      输出结果如下:

     

    七、存在的小问题

     

    def myDecrator(func):
    	def insideFunc():
    		print("before func out")
    		func()
    		print("after func out")
    		
    	return insideFunc;
    	
    
    @myDecrator
    def myfunc(args = "python"):
    	print("Hello "+args)
    	
    myfunc()
    
    print(myfunc.__name__)
    	
    

     当我们试图输出myfunc.__name__时,输出的名称为insideFunc,如下:

      这里的函数myfunc被insideFunc替代了。它重写了我们函数的名字和注释文档。幸运的是Python提供给我们一个简单的函数来解决这个问题,那就是functools.wraps。我们修改上一个例子来使用functools.wraps:

    from functools import wraps
    
    def myDecrator(func):
    	@wraps(func)
    	def insideFunc():
    		print("before func out")
    		func()
    		print("after func out")
    		
    	return insideFunc;
    	
    
    @myDecrator
    def myfunc(args = "python"):
    	print("Hello "+args)
    	
    myfunc()
    
    print(myfunc.__name__)
    	
    

      输出结果如下:

    现在看起来舒服多了。

    八、蓝本规范

    from functools import wraps
    def decorator_name(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            if not can_run:
                return "Function will not run"
            return f(*args, **kwargs)
        return decorated
     
    @decorator_name
    def func():
        return("Function is running")
     
    can_run = True
    print(func())
    # Output: Function is running
     
    can_run = False
    print(func())
    # Output: Function will not run
    

      注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

    使用场景

    现在我们来看一下装饰器在哪些地方特别耀眼,以及使用它可以让一些事情管理起来变得更简单。

    授权(Authorization)

    装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权:

    from functools import wraps
     
    def requires_auth(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            auth = request.authorization
            if not auth or not check_auth(auth.username, auth.password):
                authenticate()
            return f(*args, **kwargs)
        return decorated
    

      

    日志(Logging)

    日志是装饰器运用的另一个亮点。这是个例子:

    from functools import wraps
     
    def logit(func):
        @wraps(func)
        def with_logging(*args, **kwargs):
            print(func.__name__ + " was called")
            return func(*args, **kwargs)
        return with_logging
     
    @logit
    def addition_func(x):
       """Do some math."""
       return x + x
     
     
    result = addition_func(4)
    # Output: addition_func was called
    

      

    带参数的装饰器

    来想想这个问题,难道@wraps不也是个装饰器吗?但是,它接收一个参数,就像任何普通的函数能做的那样。那么,为什么我们不也那样做呢? 这是因为,当你使用@my_decorator语法时,你是在应用一个以单个函数作为参数的一个包裹函数。记住,Python里每个东西都是一个对象,而且这包括函数!记住了这些,我们可以编写一下能返回一个包裹函数的函数。

    在函数中嵌入装饰器

    我们回到日志的例子,并创建一个包裹函数,能让我们指定一个用于输出的日志文件。

    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
     
    myfunc1()
    # Output: myfunc1 was called
    # 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
     
    @logit(logfile='func2.log')
    def myfunc2():
        pass
     
    myfunc2()
    # Output: myfunc2 was called
    # 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串
    

      

    装饰器类

    现在我们有了能用于正式环境的logit装饰器,但当我们的应用的某些部分还比较脆弱时,异常也许是需要更紧急关注的事情。比方说有时你只想打日志到一个文件。而有时你想把引起你注意的问题发送到一个email,同时也保留日志,留个记录。这是一个使用继承的场景,但目前为止我们只看到过用来构建装饰器的函数。

    幸运的是,类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式,来重新构建logit。

    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
    

      这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:

    @logit()
    def myfunc1():
        pass
    

      现在,我们给 logit 创建子类,来添加 email 的功能(虽然 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
    

      

    注:以上内容参考自https://www.runoob.com/w3cnote/python-func-decorators.html

  • 相关阅读:
    Linux下解析域名命令-dig 命令使用详解
    重写、覆盖、重载、多态几个概念的区别分析
    介绍python中运算符优先级
    介绍Python中6个序列的内置类型
    Mysql(Mariadb)数据库主从复制
    winscp中使用sudo的方法
    git push跳过用户名和密码认证配置教程
    案例:通过shell脚本实现mysql数据备份与清理
    毕业季,我的Linux求职之路
    PHP和ajax详解
  • 原文地址:https://www.cnblogs.com/rabbit0212/p/11214788.html
Copyright © 2020-2023  润新知