简短的说:他们是修改其他函数的功能的函数。
一、一切皆对象
首先看一下python
中的函数:
def hi(name=“hello”):
return name + “world”
print(hi())
# output : ‘hello world’
# 我们可以将一个函数赋值给一个变量:
greet = hi
# 这里没有使用小括号,因为我们并不是在调用hi函数,而是将它放在greet里头。
print(greet())
# output : ‘hello world’
如果我们删掉旧的hi函数,看看会怎么样?
del hi
print(hi())
# output : NameError
print(greet())
# output : ‘hell world’
二、在函数中定义函数
在python
中可以在一个函数中定义另一个函数:
def hi(name=‘robot’):
print(“now you are inside the hi() function”)
def greet():
return “now you are in the greet() function”
def welcome():
return “now you are in the welcome() function”
print(greet())
print(welcome())
print(“now you are back in the hi() funciton”)
hi()
# output: now you are inside the hi() function
now you are in the greet() function
now you are in the welcome() function
now you are back in the hi() funciton
- 无论何时调用
hi()
函数,greet()
welcome()
函数将会被同时被调用。 - 然后,
greet()
和welcome()
函数,在hi
函数之外是不能访问的。
greet()
# output : NameError: name ‘greet’ is not defined.
三、从函数中返回函数
其实并不需要在一个函数中去执行另一个函数,我们也可以将其作为输出返回来:
def hi(name=“robot”):
def greet():
return “now you are in the greet() function”
def welcome():
return “now you are in the welcome() function”
if name == “robot”:
return greet
else:
return welcome
a = hi()
print(a)
# output : <function greet at ox89w8e8t7ew>
可以清晰的看到,a
现在指向的是hi()
函数中的greet()
函数。
再次看看这个代码。在if/else
语句中,我们返回greet
和welcome
,而不是greet()
和welcome()
。因为当你把一对小括号放在后面,这个函数就会执行;然而如果你不放小括号在后面,那它可以被到处传递。并且可以赋值给别的变量而不去执行它。
四、将函数作为参数传递给另一个函数
def hi():
return “hi robot”
def doSomethingBeforehi(func):
print(“I am some boring work before executing hi()”)
print(func())
doSomethingBeforehi(hi)
# output: I am some boring work before executing hi()
# hi robot
五、第一个装饰器
def a_new_decorator(a_func):
def wrapTheFunction():
print(“I am some boring work before executing a_func()”)
a_func()
print(“I am some boring work before executing a_func()”)
return wrapTheFunction
def a_function_requiring_decoration():
print(“I am the function which needs some decoration to remove my foul smell”)
a_function_requiring_decoration()
# output: I am the function which needs some decoration to remove
a_function_requiring_decoration = a_new_decoration(a_function_requiring_decoration)
a_function_requiring_decoration()
# output:
# I am some boring work before executing a_func()
# I am the function which needs some decoration to remove
# I am some boring work before executing a_func()
上面的例程展示了装饰器在做的事情,它们封装了一个函数,并且用这样或那样的方式来修改它的行为。对于上面的例程,我们可以使用@
来简化使用
def a_new_decorator(a_func):
def wrapTheFunction():
print(“I am some boring work before executing a_func()”)
a_func()
print(“I am some boring work before executing a_func()”)
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
print(“I am the function which needs some decoration to remove my foul smell”)
a_function_requiring_decoration()
# output:
# I am some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am some boring work before executing a_func()
这里的@a_newf_decorator
等价于:
a_function_requiring_decoration = a_new_decoration(a_function_requiring_decoration)
但是这样仍然会存在一个问题:
print(a_function_requiring_decoration.__name__)
# output : wrapTheFunction
这并不是我们想要的,我们希望output
输出应该是:a_function_requiring_decoration
。
这里的函数被wrapTheFunction
替代了。它重写了我们的函数名和注释文档(doctoring
)。幸运的是python
提供了一个简单的函数来解决这个问题,那就是functools.wraps
。接下来看怎么使用:
from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print(“I am some boring work before executing a_func()”)
a_func()
print(“I am some boring work before executing a_func()”)
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
print(“I am the function which needs some decoration to remove my foul smell”)
print(a_function_requiring_decoration.__name__)
# output : a_function_requiring_decoration
规范示例:
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not can_run:
return “Function will not run”
return f(*args, **kwargs)
return decorated
@decorator_name
def func():
return(“Function is running”)
can_run = True
print(func())
# output: Function is running
can_run = False
print(func())
# output: Function will not run
注意:@wraps
接受一个函数来进行修饰,并加入了复制函数名/注释文档/参数列表等功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
六、装饰器的应用
1、日志是装饰器运用的一个亮点
from functools import wraps
# 定义一个装饰器
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs): # *args, **kwargs 是指 func 函数调用时传入的参数
print(func.__name__. + “was called”)
return func(*args, **kwargs)
@logit
def addition_func(x):
return x + x
result = addition_func(4)
# output: addition_func was called
2、在函数中嵌入装饰器
回到日志的例子,并创建一个包裹函数,让我们指定一个用于输入的日志文件。
from functools import wraps
def logit(logfile=‘out.log’):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + “ was called”
print(log_string)
# 打开logfile文件,并写入内容
with open(logfile, ‘a’) as opened_file:
# 现在将日志打到指定的logfile
opened_file.write(log_string + ‘\n’)
return func(*args, **kwargs)
return wrapped_function
return logging_decorator
@logit()
def myfunc1():
pass
myfunc1()
# output: myfunc1 was called
3、装饰器类
在python
中类也可以用来构建装饰器。如下所示:
# coding=utf-8
from functools import wraps
import logging
class Log(object):
def __init__(self):
# 创建 logger
self.logger = logging.getLogger('Logger')
self.logger.setLevel(logging.INFO)
# 写入日志
fh = logging.FileHandler('./runtime.log')
fh.setLevel(logging.INFO)
# 输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# handler 输出格式
formatter = logging.Formatter('[%(asctime)s][%(filename)s][%(levelname)s] ## %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 绑定 handler
self.logger.addHandler(fh)
self.logger.addHandler(ch)
def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):
self.logger.info("%s is excuted!" % func.__name__)
return func(*args, **kwargs)
return wrapped_function
@Log()
def myfunc():
print("hello log")
if __name__ == '__main__':
myfunc()