博客地址: http://www.cnblogs.com/yudanqu/
概念: 装饰器是一个闭包, 把一个函数当做参数返回一个替代版的函数, 本质上就是一个返回函数的函数
装饰器就是在我们需要的一个函数外面包装一个外壳, 当我们希望这个函数更漂亮时, 可以通过改变这个包装的样子即可, 而不需要更改原函数, 也可以避免出错.
1, 简单的装饰器
- def func2(func):
- def inner():
- print('************')
- func()
- return inner
- def func1():
- print("this is one")
- f = func2(func1)
- f()
下面我解释一下这个装饰器, 可以看到, func1 是我们真正需要的函数, 这时候我们想在输出这个函数时再多添加一些功能, 那么我们就需要对 func1 这个函数进行装饰. func2 是一个外部的输出函数, 他的参数是 func, 这只是个形参, 使用的时候将需要装饰的函数传入即可, 当我们执行 func2 这个函数的时候, 其内部还有一个函数 inner(), 但是里面 inner 只是创建了这个函数而没有被调用 (这个大家能理解吗, 就是我们在定义一个函数时, 他不会自动被调用, 我们使用他时需要人为的通过这个函数来调用一下) 内部读取代码仅读到 def inner()这里并不进去, 继续向下就到了 return inner, 这里返回了内部的那个函数名, 但也只是个名字没有被调用 (函数名后面加上小括号才可以调用). 那么 return 返回给谁呢, 就给了下面的 f, 用 f 来接收 func2 的 return 值, 现在 f 就是 return 的 inner. 进行最后一步, 调用 inner, 上一步我们已经知道 f 就是 inner, 调用他就加个小括号就可以了. 调用了 inner 函数后, 他的内部有 func() 这个函数, 并且调用了, 所以可以直接执行 func 内的语句块. func 就是最外面传进来的参数, 也就是我们需要被装饰的函数. 小伙伴们慢慢理一下思路, 一定要把第一个理清楚了, 那么后面就轻而易举了.
2, 装饰器进阶(含有一个参数)
- def func4(func):
- def inner(age):
- if age < 0:
- age = 0
- func(age)
- return inner
- def func3(age):
- print("this is two %d" % age)
- f = func4(func3)
- f(-7)
看一下, 这个框架和第一个几乎一样对吧, 唯独多了一个参数, 这个参数是最后由我们装饰的那个函数来使用, 由最外部传入. 我们把参数给在了 inner 函数, 这是为什么呢? 我们想一下, 我们装饰的函数是在什么时候调用的, 是不是在调用了 inner 函数后, inner 函数内自动调用的啊. 那么在调用 inner 函数时才调用的目标函数, 我们不就应该把目标函数的参数从这里传进去吗(最外层的函数是个包装的作用, 他是为了返回 inner 函数的地址, 来为之后调用 inner 做准备), 我们在用 f 接收这个 inner 函数, 我们调用他还需要加个小括号 f(), 这才是调用 inner, 既然这里调用, 正好有个传入参数的接口, 我们趁着这个机会把想要实现的 func3 函数的参数扔进去, 岂不美哉, 接下来我们执行 func3 函数的时候, 只需要把上面接收到的参数再传到自己的接口里就好了.
22, 改进
- @func4
- def func3(age):
- print("this is two %d" % age)
- # 这时就可以直接使用原来的 func3 函数了, 不需要引入变量来接收, 就已经可以使用装饰器里的内容
- func3(-7)
@符号是装饰器的语法糖, 在定义函数时, 可以避免赋值操作
那么这段代码有什么作用呢?
其实他可以替换掉上方代码的后四行, 代码中最开始的装饰器是一样的, 我们改进在使用装饰器上. 上面我们通过先定义一个目标函数, 然后把他作为参数传入装饰器里, 然后用一个变量 f 来接收, 最终调用 f()来实现装饰作用, 这样是不是繁琐了点呢, 我们做了这么多工作, 而且可能会把我们搞晕, 其中还引入了变量, 当代码很长时, 那么多的变量我们怎么记得住呢. 这时就有了语法糖. 大约在 python2.4 就开始可以使用 @符号了. 也许大家见过, 有时候会有 @staticmethod,@classmethod 的字眼, 这就是 python 的内置装饰器.
@func4 的作用就是替代了上方在装饰器里传参数然后赋值的过程, 可以理解为通过这样一个符号, 就已经把自己变成装饰了之后的样子, 那么我们再使用的时候, 只需要和以往一样, 调用函数, 传参就可以了. 当然是不是真正的变了, 当下方再次使用这个函数时, 如果不添加语法糖还会是函数自己.
但此时的装饰器只能接收一个参数, 为整形, 因为内部使用了判断, 这样的话我们很多事情就不是很方便了, 使用装饰器就是方便我们进行后续的操作, 这样的话我们使用不一样的功能还得总是修改装饰器. 那么, 我们继续向下看.
3, 进进阶装饰器(通用装饰器)
- def func5(func):
- def inner(*args,**kwargs):
- # 功能
- print("&&&&&&")
- func(*args,**kwargs)
- return inner
- @func5
- def func6(name,age,gender=1,number='00000000'):
- print('%s is %d years old,number is %s,gender:%d' % (name,age,number,gender))
- func6('张三',18,0,'05162002')
- # 函数的参数理论上是无限制的, 但实际上最好不要超过 6 到 7 个
这个其实没有太多要说的, 只是把参数换成了不定长参数
*args: 可以接受不限量个参数, 将他们打包成 tuple 给函数
**kwargs: 可以将关键字参数打包成字典给函数
有了上面两个, 那么几乎所有的参数都可以随便输入了.
- '''我理解为偏函数就是可以通过控制参数来实现功能'''
- # 这样的一个功能, 接下来实现它
- print(int('1010', base = 2))
- # base = 2 意思是把字符串当做二进制来计算, 就是把这个字符串以二进制来判断他是多少, 以十进制输出
- # 第一种方法
- def int2(str, base = 2): # 表示设置默认值为 2. 将来用 base 值来转换
- return int(str, base)
- # 第二种方法(偏函数)
- import functools # 这个模块帮我们定义偏函数
- int2 = functools.partial(int, base = 2)
- '''断言'''
- def func(num, div):
- assert (div != 0), "div 不能为 0" # 断言
- return num/div
- func(10, 0)
- # 函数本身分母不为零, 如果为零那么将报错, 使用断言, 当没有错误时不产生效果, 当有错误时会告诉你哪里错了
来源: https://www.cnblogs.com/yudanqu/p/9091826.html