• Noah的学习笔记之Python篇:装饰器


    Noah的学习笔记之Python篇:

      1.装饰器

      2.函数“可变长参数”

      3.命令行解析

      

    注:本文全原创,作者:Noah Zhang  (http://www.cnblogs.com/noahzn/)

      年前工作事务比较繁琐,我只能用零碎的时间继续学习Python,决定开一个系列的博文,作为自己深入学习Python的记录吧。名字也取好了,就叫《ZMAN的学习笔记之Python篇》~开篇是关于装饰器的,春节假期码的字哈哈~就让我们开始吧!

      本文的例子都是自己想的,如果不是很合适,请大家提出宝贵意见哈~谢谢啦!

     

    一、为什么要用“装饰器”

      比如我们写了如下一段代码:

    # 打印0~99
    def func():
        for i in range(100):
            print(i)

      我们想要监测执行这个函数花费了多少时间,于是我们将这个函数改成了这样:

    import time
    
    
    # 打印0~99
    def func():
        start = time.time()
        for i in range(100):
            print(i)
        end = time.time()
        print("耗时:%.4f" % (end - start))

      虽然达到了目的,但是我们却改变了原有的函数,而且如果有几十个不同的函数,都这样改动一下,工作量也是非常大的。

      使用了“装饰器”后,就能在不修改原函数的前提下,达到相同的功能。

    二、什么是“装饰器”

      在Python中,函数是“对象”,而装饰器是函数,它的作用就是对已经存在的函数进行装饰。Python中的“装饰器”可以很好地解决那些有“面向切面编程”需求的问题。

      请看例子:

    def deco(ex):
        print('func函数被调用前')
        ex()
        print('func函数被调用后')
        return ex
    
    def func():
        print('func函数被调用')
    
    func = deco(func)
    
    
    >>> 
    func函数被调用前
    func函数被调用
    func函数被调用后

      我写了两个函数,将函数func作为参数传入deco函数,并将返回值赋给func变量。我们可以看成是func函数经过了deco的装饰~

      而这就是装饰器的概念了:装饰器可以说是封装器,让我们在被装饰的函数之前或之后执行一些代码,而不必修改函数本身。利用这点,我们可以做出许多酷炫的功能~

    三、写第一个“装饰器”

      刚才我介绍了装饰器的概念,但这是一个“手工”的装饰器,并没有用到Python的装饰器语法,实际上,装饰器语法非常简单,看例子:

    def deco(ex):
        def _deco():
            print('func函数被调用前')
            ex()
            print('func函数被调用后')
        return _deco
    
    
    @deco
    def func():
        print('func函数被调用')
        #return('OK')
    
    
    func()
    
    
    >>> 
    func函数被调用前
    func函数被调用
    func函数被调用后

      大家可以看到,我在定义函数func的上一行,加了一句“@deco”,这就是装饰器语法了,这样写了之后,能确保每次调用func函数都被deco函数装饰,是不是非常简单呀~~

    四、让被装饰函数带上确定的参数

      如果被装饰函数带可以确定的参数,需要像下面这样对装饰器函数进行修改:

    def deco(ex):
        def _deco(a, b):
            print('%s函数被调用前' % ex.__name__)
            c = ex(a, b)
            print('%s函数被调用后,结果为:%s' % (ex.__name__, c))
            
        return _deco
    
    
    @deco
    def func(a, b):
        print('func函数被调用,传入%s,%s' % (a, b))
        return a+b
    
    
    func(1, 2)
    
    
    >>> 
    func函数被调用前
    func函数被调用,传入1,2
    func函数被调用后,结果为:3

      这个例子的装饰器实现了:打印传入函数的名字、打印两个数字相加结果的功能。我们在原先的deco函数内又定义了一个函数_deco用来接收func函数中的参数。

    五、让被装饰函数带上不确定的参数

    def deco(ex):
        def _deco(*args, **kwargs):
            print('%s函数被调用前' % ex.__name__)
            c = ex(*args, **kwargs)
            print('%s函数被调用后,结果为%s' % (ex.__name__, c))
    
        return _deco
    
    
    @deco
    def func(a, b):
        print('func函数被调用,传入%s,%s' % (a, b))
        return a+b
    
    
    @deco
    def func2(a, b, c):
        print('func2函数被调用,传入%s,%s,%s' % (a, b, c))
        return a+b+c              
    
    
    func(1, 2)
    func2(1, 2, 3)
    
    
    >>> 
    func函数被调用前
    func函数被调用,传入1,2
    func函数被调用后,结果为3
    func2函数被调用前
    func2函数被调用,传入1,2,3
    func2函数被调用后,结果为6

      简单修改我们的代码,使用*args, **kwargs来捕捉不定量的传参,便实现了多个参数的求和。

    六、让装饰器带上参数

    def deco(ex):
        def _deco(func):
            def _deco2():
                print('%s函数被调用前,传入参数为:%s' % (func.__name__, ex))
                func()
                print('%s函数被调用后' % func.__name__)
            return _deco2
        return _deco
    
     
    @deco('parameter1')
    def func():
        print('func函数被调用')
    
     
    func()
    
    
    >>> 
    func函数被调用前,传入参数为:parameter1
    func函数被调用
    func函数被调用后

      如果要让装饰器带上参数,我们要在装饰器函数内部再多定义一层函数,用来接收装饰器的参数~大家可不要搞混了装饰器参数和函数的参数哟~

    七、来个任性的:装饰器和被装饰函数都带参数

    def deco(ex):
        def _deco(func):
            def _deco2(c, d):
                print('%s函数被调用前,装饰器参数为:%s' % (func.__name__, ex))
                x = func(c, d)
                if x > 3:
                    x = x-ex
                else:
                    x = x+ex
                print('%s函数被调用后,计算结果为:%d
    ' % (func.__name__, x))
            return _deco2
        return _deco
    
     
    @deco(3)
    def func(a, b):
        print('func函数执行结果为:%s' % int(a+b))
        return(a+b)
    
     
    func(3, 4)
    func(1, 2)
    
    
    >>> 
    func函数被调用前,装饰器参数为:3
    func函数执行结果为:7
    func函数被调用后,计算结果为:4
    
    func函数被调用前,装饰器参数为:3
    func函数执行结果为:3
    func函数被调用后,计算结果为:6

      最初的func函数只是实现两个数字的相加,经过装饰后实现了对func返回的和的大小进行了分支处理:如果“两数的和大于3”,最后结果为“两数的和减去3”,否则最后结果为“两数的和加上3”。我在这个例子中使用的是“确定”的参数,大家可以自己更改哦~

    八、同时使用多个装饰器

      之前的例子都是只用了一个装饰器,我们当然可以装饰多次啦~

    def deco1(ex):
        def _deco1(string):
            print('deco1被调用前')
            string = ex(string)
            if 'hello' in string:
                string = "You are my old friend."
            else:
                string = "You are my new friend."
            print('deco1被调用后,%s
    ' % string)
    
            return string
                
        return _deco1
    
    
    def deco2(ex):
        def _deco2(string):
            print('deco2被调用前')
            string = ex(string)
            if 'ZMAN' in string:
                string = 'hello, ' + string
            else:
                string = 'Is your name ' + string + '?'
            print('deco2被调用后,%s' % string)
    
            return string
    
        return _deco2
    
    @deco1
    @deco2
    def func(string):
        print('func函数被调用')
        return string
    
    func('ZMAN')
    deco1(deco2(func('John')))
    
    
    >>> 
    deco1被调用前
    deco2被调用前
    func函数被调用
    deco2被调用后,hello, ZMAN
    deco1被调用后,You are my old friend.
    
    deco1被调用前
    deco2被调用前
    func函数被调用
    deco2被调用后,Is your name John?
    deco1被调用后,You are my new friend.

      在这个例子中,我们主要要关注装饰器调用的先后顺序,此时func('ZMAN')和deco1(deco2(func('ZMAN')))是等同的,这个调用顺序大家一看就明白了吧~

    九、实际应用

      最后来个实际应用~好吧,我实在是绞尽脑汁了,写代码的时候正好在吃苹果,那就来个跟水果有关的实例吧(别打我 - -!)

    #综合运用:简单地检测函数传参是否合法
    
    def deco(ex):
        def _deco(*args, **kwargs):
            print('***%s函数被调用前***' % ex.__name__)
            if args:
               if not isinstance(args[0], str):
                   print('★店名参数错误:%s' % args[0])
               if not(isinstance(args[1], int) and args[1]>0):
                   print('★员工参数错误:%s' % args[1])
                   
            else:
                print('★未传入店名和员工数信息!')
    
            if kwargs:
                for i in kwargs:
                    if not ((isinstance(kwargs[i], int)
                        or isinstance(kwargs[i], float))
                        and kwargs[i]>0):
                        print('★水果单价参数错误:%s:%r' % (i, kwargs[i]))
            else:
                print('★未传入水果单价信息!')
    
            a = ex(*args, **kwargs)
            print('***%s函数被调用后***
    ' % ex.__name__)
    
        return _deco
    
    
    @deco
    # 假设传入几家水果店的名称、员工数以及水果单价。店名为字符,员工数为正整数,单价为正数
    def func(*args, **kwargs):
        print('***函数func被调用***')
        brief = args
        detail = kwargs
        return(brief, detail)
                      
    
    func('水果之家', -4, apple=3.5, strawberry=6, orange=3, cherry=8.5,)
    func(123, 8, apple=3, orange=2,)
    func('天然果园', 0.2, )
    func()
    
    
    >>> 
    ***func函数被调用前***
    ★员工参数错误:-4
    ***函数func被调用***
    ***func函数被调用后***
    
    ***func函数被调用前***
    ★店名参数错误:123
    ***函数func被调用***
    ***func函数被调用后***
    
    ***func函数被调用前***
    ★员工参数错误:0.2
    ★未传入水果单价信息!
    ***函数func被调用***
    ***func函数被调用后***
    
    ***func函数被调用前***
    ★未传入店名和员工数信息!
    ★未传入水果单价信息!
    ***函数func被调用***
    ***func函数被调用后***

      代码有点长,但是只要大家耐心看,其实还是挺简单的,没有什么花里胡哨的东西。这个装饰器用来检测传参是否合法~

    十、小结

      第一篇洋洋洒洒那么多个例子,终于写完了!利用“装饰器”,我们无须改写原函数,就能对它进行功能扩充,比如计时、检测传参、记录日志等等。就比如我们有一把枪,我们可以给它加上消音器,又或者是刺刀…不用的时候就拿掉,还是原来的枪~~

      (本文难免有写错或不足的地方,希望大家不吝赐教哦~谢谢!)

  • 相关阅读:
    OkHttp3Stack volley
    Badge分析
    Android Icon数字角标(BadgeNumber)的实现方式
    Android ORMLite 框架的入门用法
    实现FlowLayout 自动排序标签
    iptables端口转发配置[Ubuntu]
    编译csipsimple的native部分
    开发JavaScript组件
    jQuery组件开发
    Groovy小结:java调用Groovy方法并传递参数
  • 原文地址:https://www.cnblogs.com/noahzn/p/4294511.html
Copyright © 2020-2023  润新知