打从大三学了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传入的函数参数, 像当于调用了传入的函数.,看一下结果
这是不是就是我们说到的装饰器, 传入函数, 返回函数
接下来我们做一点小改变
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 那结果会变成什么?? 我们来实验一下把,
是否会感到迷惑, 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.在函数调用中使用”*”,我们需要元组;在函数调用中使用”**”,我们需要一个字典