python进阶--装饰器

打从大三学了python后, 就没有学习python语法了, 在此之前学的都是和其他语言一样的语法, 自此在做项目的时候遇到好多python高级语法的知识, 现在再来系统的学习一下, 省的总是百度. 先来说说python的装饰器吧.

为什么会有装饰器这个奇怪的东西呢?  有时候是不是会觉得一个框架的功能缺少你想要的功能, 但是呢, 你总不能改人家的框架吧, 又想增加自己的功能, 这时装饰器就是你的不二选择, 顾名思义就是起装饰作用的一坨代码. 来看看官方解释吧.

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

先来看看早期的版本python2.4以下的装饰器是怎么写的,先从一个简单的例子说起, 比如我想写个方法, 让他输出hello world 相比大家都会写吧,  这可是程序员的第一课

def fun():
    print('Hello world')
fun()

然后如果我想要在打印helloworld前打印#####################呢? 可能有些会直接在print前打印, 这里介绍优雅的写法

def fun():
    print('Hello world')

def  Decorator(func):
    def test():
        print('#####################')
        return func()
    return test()

Decorator(fun)

这个代码的执行流程在这分析一下: 代码中定义了两个方法, 最后调用了后面的方法, 着重分析后面的方法, 后面的方法传入一个函数, 而在方法里面又定义了一个函数, 定义玩后在return 这个函数, 则先执行这个函数, 然后在退出, 我们看看这个函数做了啥事, 首先打印一串#, 之后再return传入的函数参数, 像当于调用了传入的函数.,看一下结果

python进阶--装饰器_第1张图片

这是不是就是我们说到的装饰器, 传入函数, 返回函数

接下来我们做一点小改变

def fun():
    print('Hello world')

def  Decorator(func):
    def test():
        print('#####################')
        return func()
    return test

fun()
fun = Decorator(fun)
fun()

猜猜结果吧?  喝口水来

 

直接看结果吧:

是否很惊讶??  前后的fun()函数变了吗?  这里说个小细节, 就是后面那个方法返回的是test 而不是像前面的例子为test(), 那返回这个会不会调用test函数了???  答案是不会的,  只是把函数返回而非调用,  要明白return test 和return test()的区别, 这里在说一下, 如果把上面的return func() 换成return func 那结果会变成什么?? 我们来实验一下把,

python进阶--装饰器_第2张图片

是否会感到迷惑,  helloworld怎么没打印了??  这是不是和我们上面说的一样, return func()  才会调用func() returnfunc不会调用func()函数, 在这里我们还是返回return func(), 在这调用了一下Decorator方法后fun的功能是不是改变了?  是不是增加了打印#的功能?  那我们有没有改变fun的源码实现的这个功能, 明显湿没有的吧,  这就是装饰器!!!! 在python2.4以后的版本引入了@符号, 表示调用装饰器,  只要在需要增加功能的方法前用@调用装饰器, 那就可以增加功能, 比如上面的写法可以写成

def  Decorator(func):
    def test():
        print('#####################')
        return func()
    return test

@Decorator
def fun():
    print('Hello world')

fun()

这里需要注意的是: 装饰器一定要在需要装饰的方法前定义或者引入相应的装饰器模块, 不然会报错!!!!!!

是否会想这不是函数吗, 函数一般都可以加参数吧, 那有参数的装饰器怎么写???  下面引入带参数的装饰器写法:  在写代码的过程中, 遇到一个问题, 装饰器是不是不能改变函数的参数, 那么装饰前后得一模一样, 那么return的时候也得返回参数,  有思路了

def  Decorator(func):
    def test(*args, **kwargs):
        print('#####################')
        return func(*args, **kwargs)
    return test

@Decorator
def fun(i):
    print(i)

fun(1)

输出结果:

这里 可能有些同学不太熟悉(*args, **kwargs)的用法, 这个等会我们在细说, 现在只要只要这是可以传多个参数的就行了, 看上面的代码执行顺序是, 先定义了装饰器, 然后装饰fun方法, 最后调用装饰后的fun方法, 这里着重说参数传递, 装饰器把test方法返回, 先从原始写法看吧  fun = Decorator(fun), 调用Decorator方法, 遇到定义方法, 先不过, 再遇到return test, 像当于fun=test, 而 那么是不是test和fun传的参数要一模一样, 不然会出bug? 这就是写法中的(*args, **kwargs)的参数在test和func中一模一样. 这里我们是不是有一个框架了, 可以传入多个参数的装饰器框架, 只要根据传入的参数实现不同的功能, 或者直接实现不同的功能, 那么功能代码应该替换print('#####################')代码段就可以了, 接下来我们根据传入的参数, 来打印相应的参数值吧,

def  Decorator(func):
    def test(*args, **kwargs):
        print(*args)
        return func(*args, **kwargs)
    return test

@Decorator
def fun(i):
    print(i)

fun(1)

结果湿打印了两次1,  到此, 装饰器就讲到此, 最后以一个通用模板的装饰器结束

def  Decorator(func):
    def test(*args, **kwargs):
        ########users add fun#######
        print(*args)
         ########users add fun end#######
        return func(*args, **kwargs)
    return test

@Decorator
def fun(*args, **kwargs):
    #####fun part#######
    pass

fun(1, 2, 3)

其中: users add fun为需要为fun函数增添功能的代码段到users add fun end 结束, 而fun函数, 表示要装饰函数的源码或者从其他模块调用的方法

--------------------------------------------------------华丽的分割线--------------------------------------------------------------------------------------------------------

接下来我们说说*args 和**kwargs 的使用方法

当函数的参数不确定时,可以使用*args 和**kwargs,*args 没有key值,**kwargs有key值。当定义了这两个后, 可以传入多个参数, 不过最好不要多于7个, 直接上代码

def fun_var_args(farg, *args):  
    print "arg:", farg  
    for value in args:  
        print "another arg:", value  
  
fun_var_args(1, "two", 3) # *args可以当作可容纳多个变量组成的list

结果:

arg: 1 
another arg: two 
another arg: 3 

代码e二:

def fun_var_kwargs(farg, **kwargs):  
    print "arg:", farg  
    for key in kwargs:  
        print "another keyword arg: %s: %s" % (key, kwargs[key])  
  
  
fun_var_kwargs(farg=1, myarg2="two", myarg3=3) # myarg2和myarg3被视为key, 感觉**kwargs可以当作容纳多个key和value的dictionary

结果:

arg: 1  
another keyword arg: myarg2: two  
another keyword arg: myarg3: 3

看到这是不是明白了, **kwargs用法其实就是当传入key=key-value时, 而很多的键和值存储在kwargs字典中, 需要逐个访问参数, 其中key-value=kwargs[key]

总结:

1、*args和**kwargs主要用于定义函数的可变参数

2、*args:发送一个非键值对的可变数量的参数列表给函数

3、**kwargs:发送一个键值对的可变数量的参数列表给函数

4、如果想要在函数内使用带有名称的变量(像字典那样),那么使用**kwargs。

定义可变参数的目的是为了简化调用。

5、*args和**kwargs不是固定的,只有前面的*和**是固定不可变的,后面的名称可以随意改,例如*vals代表非键值对的可变数量的参数,**parms代表可变数量的键值对参数。使用*args和**kwargs,是一种约定成俗的习惯,你也可以不使用这个名称。

6、当要同时使用*args和**kwargs时,*args必须写在**kwargs之前。

7.在函数调用中使用”*”,我们需要元组;在函数调用中使用”**”,我们需要一个字典

 

 

 

 

 

你可能感兴趣的:(Python)