• 装饰器


    装饰器的功能

    他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更 Pythonic(Python范儿)

    普通的函数示例

    def hi(name="yasoob"):
        return "hi " + name
    
    print(hi())
    # output: 'hi yasoob'
    
    # 我们甚至可以将一个函数赋值给一个变量,比如
    greet = hi
    # 我们这里没有在使用小括号,因为我们并不是在调用hi函数
    # 而是在将它放在greet变量里头。我们尝试运行下这个
    
    print(greet())
    # output: 'hi yasoob'
    
    # 如果我们删掉旧的hi函数,看看会发生什么!
    del hi
    # print(hi()) # NameError: name 'hi' is not defined
    #outputs: NameError
    
    print(greet())
    #outputs: 'hi yasoob'
    

    在 Python 中我们可以在一个函数中定义另一个函数:

    函数加括号代表执行这个函数,不加括号就是个变量,可以被到处传递
    在 if/else 语句中我们返回 greet 和 welcome,而不是 greet() 和 welcome()。
    为什么那样?这是因为当你把一对小括号放在后面,这个函数就会执行;
    然而如果你不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。

    def hi(name="yasoob"):
        print("now you are inside the hi() function")
    
        def greet1():
            return "now you are in the greet() function"
    
        def welcome():
            return "now you are in the welcome() function"
    
        print(greet1())
        print(welcome())
        print("now you are back in the hi() function")
        
    hi()
    # # 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如:
    # greet1() # NameError: name 'greet1' is not defined
    # hi().greet1() # AttributeError: 'NoneType' object has no attribute 'greet1'
    # hi.greet1() # AttributeError: 'function' object has no attribute 'greet1'
    

    从函数中返回函数

    其实并不需要在一个函数里去执行另一个函数,我们也可以将其作为输出返回出来

    def hi(name="yasoob"):
        def greet():
            return "now you are in the greet() function"
    
        def welcome():
            return "now you are in the welcome() function"
    
        if name == "yasoob": # 这里函数中返回函数
            return greet
        else:
            return welcome
    
    a = hi()
    # <function hi.<locals>.greet at 0x000001FBEB4298C8>
    print(a) # 上面清晰地展示了`a`现在指向到hi()函数中的greet()函数
    #现在试试这个
    
    print(a()) # now you are in the greet() function
    a = hi(name = "ali")
    print(a) # <function hi.<locals>.welcome at 0x000001FBEB429A60>
    print(a()) # now you are in the welcome() function
    print(hi()()) # now you are in the greet() function
    

    将函数作为参数传给另一个函数

    def hi():
        return "hi yasoob!"
    
    def doSomethingBeforeHi(func):
        print("I am doing some boring work before executing hi()")
        print(func())
    
    doSomethingBeforeHi(hi)
    #outputs:I am doing some boring work before executing hi()
    #        hi yasoob!
    

    你的第一个装饰器

    def a_new_decorator(a_func):
    
        def wrapTheFunction():
            print("I am doing some boring work before executing a_func()")
    
            a_func()
    
            print("I am doing some boring work after executing a_func()")
    
        return wrapTheFunction
    
    def a_function_requiring_decoration():
        print("I am the function which needs some decoration to remove my foul smell")
    
    a_function_requiring_decoration() # I am the function which needs some decoration to remove my foul smell
    a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
    a_function_requiring_decoration() # I am doing some boring work before executing a_func()
    # I am the function which needs some decoration to remove my foul smell
    # I am doing some boring work after executing a_func()
    
    '''
    它们封装一个函数,并且用这样或者那样的方式来修改它的行为
    现在你也许疑惑,我们在代码里并没有使用 @ 符号?
    那只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用 @ 来运行之前的代码:
    '''
    @a_new_decorator # 装饰器所在的函数(装饰器那个名称的函数一定要有传入装饰器中的参数并调用该参数)
    def a_function_requiring_decoration():
        """Hey you! Decorate me!"""
        print("I am the function which needs some decoration to "
              "remove my foul smell")
    
    a_function_requiring_decoration() # I am doing some boring work before executing a_func()
    # I am the function which needs some decoration to remove my foul smell
    # I am doing some boring work after executing a_func()
    
    
    print(a_function_requiring_decoration.__name__) # wrapTheFunction
    

    functools.wraps函数使原函数的名字和注释文档保持一致

    Ouput 输出应该是 “a_function_requiring_decoration”。
    这里的函数被 warpTheFunction 替代了。它重写了我们函数的名字和注释文档(docstring)。
    幸运的是 Python 提供给我们一个简单的函数来解决这个问题,那就是 functools.wraps

    from functools import wraps
    
    def a_new_decorator(a_func):
        @wraps(a_func)
        def wrapTheFunction():
            print("I am doing some boring work before executing a_func()")
            a_func()
            print("I am doing some boring work after executing a_func()")
        return wrapTheFunction
    
    @a_new_decorator
    def a_function_requiring_decoration():
        """Hey yo! Decorate me!"""
        print("I am the function which needs some decoration to "
              "remove my foul smell")
    
    print(a_function_requiring_decoration.__name__) # a_function_requiring_decoration
    

    蓝本规范

    from functools import wraps
    def decorator_name(f):
        '''
        注意:@wraps 接受一个函数来进行装饰,
        并加入了复制函数名称、注释文档、参数列表等等的功能。
        这可以让我们在装饰器里面访问在装饰之前的函数的属性。
        '''
        @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
    

    使用场景

    授权(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
    print(result) # 8
    

    在函数中嵌入装饰器(不了解带参数的装饰器与在函数中嵌入装饰器是不是同样)

    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 + '
    ')
                    # 这3个连续的return整的我有点昏(不理解)
                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
    '''

    class logit(object):
    
        _logfile = 'out.log'
    
        def __init__(self, func):
            self.func = func
    
        def __call__(self, *args):
            log_string = self.func.__name__ + " was called"
            print(log_string)
            # 打开logfile并写入
            with open(self._logfile, 'a') as opened_file:
                # 现在将日志打到指定的文件
                opened_file.write(log_string + '
    ')
            # 现在,发送一个通知
            self.notify()
    
            # return base func
            return self.func(*args)
    
    
    
        def notify(self):
            # logit只打日志,不做别的
            pass
    # 这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:
    logit._logfile = 'out2.log' # 如果需要修改log文件参数
    @logit
    def myfunc1():
        pass
    
    myfunc1()
    # 输出: myfunc1 was called
    

    现在,我们给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
    

    函数缓存(Function caching)不知道他的使用场景

    lru_cache 的装饰器
    函数缓存允许我们将一个函数对于给定参数的返回值缓存起来。
    当一个 I/O 密集的函数被频繁使用相同的参数调用的时候,函数缓存可以节约时间。
    在 Python 3.2 版本以前我们只有写一个自定义的实现。在 Python 3.2 以后版本,
    有个 lru_cache 的装饰器,允许我们将一个函数的返回值快速地缓存或取消缓存。

    from functools import lru_cache
    
    @lru_cache(maxsize=32)
    def fib(n):
        if n < 2:
            return n
        return fib(n-1) + fib(n-2)
    
    print([fib(n) for n in range(10)])
    '''
    那个 maxsize 参数是告诉 lru_cache,最多缓存最近多少个返回值。
    我们也可以轻松地对返回值清空缓存,通过这样:
    '''
    fib.cache_clear()
    
    
    努力拼搏吧,不要害怕,不要去规划,不要迷茫。但你一定要在路上一直的走下去,尽管可能停滞不前,但也要走。
  • 相关阅读:
    数据库(MySQL):事务
    数据库(MySQL):存储引擎
    操作系统:虚拟存储器
    操作系统:内存管理
    操作系统:进程与线程
    近期目标
    计算机网络:TCP三次握手、四次挥手
    计算机网络:OSI与TCP/IP各层的结构与功能,都有哪些协议
    计算机网络:从输入URL到页面加载
    Java:JVM
  • 原文地址:https://www.cnblogs.com/wkhzwmr/p/15282810.html
Copyright © 2020-2023  润新知