提前了解知识
赋值调用
函数也是对象,函数对象是可以被赋值给变量,所以变量也可以调用函数。1
2
3
4
5
6
7
8def hello():
print("hello world")
a = hello() #函数hello()的返回值给a
print(a) #由于函数hello()无返回值,所以a的结果为None
print("###########")
b = hello #此处将函数hello赋值给b,不是字符串的赋值
b() #b()相当于hello()
开发中的开放封闭原则
写代码要遵循开放封闭原则,该原则不仅适用于面向对象开发,也适用于函数式编程,简单点说,就是以实现的功能代码不允许被修改,但可以被扩展。
- 封闭:以实现的功能代码
开放:对扩展开放
装饰器作用
装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
装饰器经常用于如下场景:插入日志、性能测试、事务处理、缓存、权限校验等。通过使用装饰器,可以抽离处大量与函数功能本身无关的雷同代码并继续重用。
简单来说,装饰器就是在不改变函数本身的前提下,在函数前面或者后面添加一些额外功能。
装饰器通过@
调用。装饰器实例
无参数的装饰器
对于前面的函数
1
2def hello():
print("hello world")现在需要打印日志,在前后加上时间点,在不改变原有函数
hello()
的情况下如何做?1
2
3
4
5
6
7
8import datetime
def log(func):
def wrapper():
print("start_time: {0}".format(datetime.datetime.now()))
func()
print("end_time: {0}".format(datetime.datetime.now()))
return wrapper
上面的log
是一个装饰器(decorator),接受一个函数为参数(func),并返回一个函数(wrapper)。使用的时候使用前面说的@
语法,把装饰器置于函数的定义处。1
2
3
def hello():
print("hello world")
调用hello()
函数的时候,不仅会运行hello()
函数本身,还会运行在函数前面和后面加上时间点。1
2
3
4 hello()
start_time: 2018-04-18 16:30:57.998073
hello world
end_time: 2018-04-18 16:30:58.001074
代码分析
将@log
放在hello()
函数的定义处,相当于执行了语句hello = log(hello)
。
由于log()
是一个装饰器,返回一个函数,所以原来的hello()
函数仍然存在,但是同名的hello
变量指向了新的函数,于是调用hello()
将执行新函数,新函数是谁呢?新函数是log()
函数的返回值,也就是wrapper()
函数。
带参数的装饰器
当我们需要传递参数的时候时候怎么处理呢?对于上面的日志处理函数,要传递参数author
该怎么处理呢?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import datetime
def log(name):
def author(func):
def wrapper():
print("author: {0}".format(name))
print("start_time: {0}".format(datetime.datetime.now()))
func()
print("end_time: {0}".format(datetime.datetime.now()))
return wrapper
return author
def hello():
print("hello world")
hello()
输出结果1
2
3
4author: xiaohh
start_time: 2018-04-18 20:13:44.912610
hello world
end_time: 2018-04-18 20:13:44.912610
代码分析@log("xiaohh")
相当于执行了语句hello = log("xiaohh")(hello)
。
首先执行log("xiaohh")
,返回的是author
函数,再调用返回的函数,参数是hello
函数,返回值最终是wrapper
函数。
注意点
在上述的函数调用之后,最终返回的是wrapper
函数,所以它的__name__
属性发生了变化。1
print(hello.__name__) #输出结果wrapper
此处需要把原始函数的__name__
等属性赋值到wrapper()
函数中,否则有些依赖函数签名的代码执行会报错。要实现该功能使用functools.wraps
就行。
- 对于不带参数的装饰器
1 | import datetime |
- 对于带参数的装饰器
1 | import datetime |
输出结果1
print(hello.__name__) #输出结果hello
也就是在定义wrapper
函数之前加上@functools.wraps(func)
即可。
基于类的装饰器
类的构造函数init()接受一个函数,然后重载call()并返回一个函数达到装饰器函数的效果。
具体的调用和程序执行过程其实和函数实现的装饰器是一致的。
无参数的类的装饰器
1 | import datetime |
输出结果1
2
3start_time: 2018-04-18 21:05:56.422751
hello world
end_time: 2018-04-18 21:05:56.422751
这是对前面函数实现的不带参数的装饰器采用类方式实现。
带参数的类的装饰器
1 |
|
输出结果1
2
3
4author: xiaohh
start_time: 2018-04-18 21:13:27.538287
hello world
end_time: 2018-04-18 21:13:27.538287
这是对前面函数实现的带参数的装饰器采用类方式实现。
python内置装饰器
内置装饰器有3个,分别是staticmethod
、classmethod
和propety
。