美文网首页清风PythonPython3入门笔记及知识点整理python3
Python装饰器总结,带你几步跨越此坑!

Python装饰器总结,带你几步跨越此坑!

作者: 清风Python | 来源:发表于2019-08-20 01:43 被阅读27次
    关于诉求

    昨天简单聊了下Flask的学习感想,然后分享了一些Flask的学习方式与视频。其中提到在学习Python Web前,请先将python基础及装饰器等知识有一个了解,这样学习起来不至于太过吃力。


    私信

    然后,今天有朋友私信说对python的类和装饰器不甚了解,希望能讲讲这些知识。关于函数、方法、类,我之前发过一篇文章,就不再赘述了。其实去年详细总结过一篇关于Python装饰器的文章,只不过是在公司博客写的,没办法复制出来,所以今天就这之前的知识做一个总结和复习吧。

    引子

    谈及python装饰器,很多人第一时间想到的是@这个符号。在方法上一行加上@decorator就行了。但很多人看着都会、一用就跪,而且很多时候,我们都不知道什么场景下适合使用装饰器。那么今天就带大家一步步了解装饰器的使用吧

    装饰器(Decorator)是python的一个重要部分,简单来说,他是修改其他函数功能的函数。
    他们有助于让我们的代码更简短,也更Pythonic!

    万物皆对象

    在Python的世界中,万物皆对象,听起来比较抽象,但其实理解起来很简单,你可以用将任何一个变量、函数、方法、类等等赋值给另一个变量。只有你了解了这些,才能进一步的理解装饰器。

    函数赋值

    而学习装饰器前,我们先来举一个简单的函数例子:

    def hello(name='BreezePython'):
        return "say hello to {}".format(name)
    
    hello()
    # 'say hello to BreezePython'
    
    hi = hello
    
    hi('tommorow')
    # 'say hello to tommorow'
    

    我们首先创建一个hello的函数,然后调用,之后将hello赋值给hi,之后调用hi,完成了相同的操作,也许你觉得这个例子so easy,那就接着往下走。

    函数嵌套

    当我们在函数中再次定义一个函数是,即完成了一个函数的嵌套操作:

    def hello():
        print('is hello function...')
    
        def hi():
            return 'hi ,nice to meet you!'
    
        def bye():
            return 'bye,stranger...'
    
        print(hi())
        print(bye())
    
    hello()
    output:
    is hello function...
    hi ,nice to meet you!
    bye,stranger...
    
    hi()
    output:
    Traceback (most recent call last):
      File "<input>", line 1, in <module>
    NameError: name 'hi' is not defined
    

    上面的例子看到了,函数中可以调用子函数,但如果你直接去调用子函数,则会抛出未定义的异常,那么我们如何调用子函数?

    函数中返回函数

    让我们由浅入深,先考虑从函数中返回函数

    def hello(name=None):
        print('is hello function...')
    
        def hi():
            return 'hi ,nice to meet you!'
    
        def bye():
            return 'bye,stranger...'
    
        if name == 'BreezePython':
            return hi
        else:
            return bye
    
    main = hello('BreezePython')
    >>> output: is hello function...
    main()
    >>> output: 'hi ,nice to meet you!'
    

    初学者可能对python中的小括号有些迷糊,添加小括号与否有什么影响呢?

    当你在函数或者实例化的对象后添加小括号,代表立即执行;
    然而,当你不添加小阔爱好时,他可以被到处传递,并可以复制给变得变量而不去执行它。

    函数作为参数传递
    def child():
        return 'is child function...'
    
    def main(func):
        print('is main function...')
        print(func())
    
    main(child)
    
    output:
    >>> is main function...
    >>> is child function...
    

    我们新建了一个child函数,然后将child话术传递给main函数,在main函数中调用child函数,达到了将函数作为参数传递的结果。

    python闭包

    我们先将上面的函数嵌套与传参来进行一下合并:

    def main(func):
        print('is main function...')
    
        def child():
            print('it print before exec func...')
            func()
            print('it print after exec func...')
        return child
    
    def alone():
        print("I'm  alone function ...")
    
    fix = main(alone)
    fix()
    
    output:
    >>> is main function...
    >>> it print before exec func...
    >>> I'm  alone function ...
    >>> it print after exec func...
    

    通过合并,我们将上面两个例子进行和组装,变成了一个升级版的闭包,很多人会说,什么是闭包呢?其实很简单...

    我们可以将闭包理解为一种特殊的函数,这种函数由两个函数的嵌套组成,
    且称之为外函数和内函数,外函数返回值是内函数的引用,此时就构成了闭包。

    上面这几个函数的例子大家看完,如果觉得都很简单,那么可以开始今天的装饰器学习了!

    first Decorator

    上面的例子中,我们看到了一个闭包与函数传参的例子,那么装饰器是什么?其实就是闭包+函数传参,如果上面的例子你看懂了,那么现在你只需要对代码格式稍作修改,就变成了一个装饰器!

    def main(func):
        print('is main function...')
    
        def child():
            print('it print before exec func...')
            func()
            print('it print after exec func...')
    
        return child
    
    @main
    def alone():
        print("I'm  alone function ...")
    
    alone()
    
    # output:
    >>> is main function...
    >>> it print before exec func...
    >>> I'm  alone function ...
    >>> alone
    >>> it print after exec func...
    

    我们只是修改了一行代码的格式,就转化成了装饰器,@main就代表func = main(func)
    可这样就算完美的装饰器了么?NO....
    我们将alone函数稍作变更,即可看出问题所在:

    def alone():
        print("I'm  alone function ...")
        print(alone.__name__)
    

    正常情况下,调用alone带引的alone.__name__就是函数名即alone,但如果我们是通过装其实调用后打印呢,结果是什么?相信大家能猜到,是child。child是main函数的内建函数,它重写了我们的函数名,如何解决这个问题呢?

    使用functools.wraps

    from functools import wraps
    
    
    # first Decorator
    def main(func):
        print('is main function...')
    
        @wraps(func)
        def child():
            print('it print before exec func...')
            func()
            print('it print after exec func...')
    
        return child
    
    @main
    def alone():
        print("I'm  alone function ...")
        print(alone.__name__)
    
    alone()
    
    # output:
    >>> is main function...
    >>> it print before exec func...
    >>> I'm  alone function ...
    >>> alone
    >>> it print after exec func...
    

    我们通过引入functools方法中的wraps,保证了函数名称的原始性
    @wraps接受一个函数,进行装饰,并加入了复制函数名称、注释文档、参数列表等功能,这样可以是我们在装饰器里面访问在装饰之前的函数的属性

    装饰器实例

    装饰器比大量的使用在Flask、Django中,学好了它不管是对于你理解flask的路由,还是之后的代码开发都有很多帮助,那么我们来做个简单的例子,日志打印装饰器:

    import time
    from functools import wraps
    
    
    def log_level(level='DEBUG'):
        def log_format(func):
            @wraps(func)
            def format(*args, **kwargs):
                logtime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
                print("[{}]{}: ".format(level, logtime), end='')
                return func(*args, **kwargs)
    
            return format
    
        return log_format
    
    
    @log_level()
    def log1():
        print("Hello,Welcome to 清风Python...")
    
    
    @log_level('ERROR')
    def log2():
        print("清风Python 是我的公众号...")
    
    
    log1()
    time.sleep(1)
    log2()
    
    # output:
    >>> [DEBUG]2019-08-20 01:24:23: Hello,Welcome to 清风Python...
    >>> [ERROR]2019-08-20 01:24:24: 清风Python 是我的公众号...
    
    类的装饰器

    讲了这么多,本来觉得该结束了,可总觉得还差点什么!没错,我们只是讲到了函数的装饰器,那么类的装饰器该如何操作呢?

    import time
    from functools import wraps
    
    
    class Logger:
        def __init__(self,level='DEBUG'):
            self.level = level
    
        def __call__(self, func):
            @wraps(func)
            def log_format(*args, **kwargs):
                log_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
                print("[{}]{}: ".format(self.level, log_time), end='')
                return func(*args, **kwargs)
            return log_format
    
    @Logger()
    def log1():
        print("Hello,Welcome to 清风Python...")
    
    @Logger('Error')
    def log2():
        print("清风Python 是我的公众号...")
    
    log1()
    time.sleep(1)
    log2()
    
    # output:
    >>> [DEBUG]2019-08-20 01:24:23: Hello,Welcome to 清风Python...
    >>> [ERROR]2019-08-20 01:24:24: 清风Python 是我的公众号...
    

    今天的装饰器内容就分享到这里吧...

    The End

    OK,今天的内容就到这里,如果觉得内容对你有所帮助,欢迎点击文章右下角的“在看”。
    期待你关注我的公众号清风Python,如果觉得不错,希望能动动手指转发给你身边的朋友们。
    希望每周一至五清晨的7点10分,都能让清风Python的知识文章叫醒大家!谢谢……

    清风Python

    相关文章

      网友评论

        本文标题:Python装饰器总结,带你几步跨越此坑!

        本文链接:https://www.haomeiwen.com/subject/dvkysctx.html