装饰器基础
- 装饰器是可调用对象,其参数是另一个函数。装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。
def deco(func):
def inner():
print('running inner()')
return inner
@deco
def target():
print('running target()')
# 调用的是inner函数
target()
- 装饰器在被函数定义之后立即运行,通常是在导入装饰器模块的时候。被装饰的函数则是在被调用时运行。
变量作用域
- 全局变量
def f1(a):
print(a)
print(b)
#这里将会报NameError错误,由于没有定义b
f1(3)
# 在调用f1前定义b,则不会报错
b = 6
f1(3)
# 下面的产生了赋值b的语句,所以b将变成局部变量,print(b)没有值,将报错
b = 6
def f2(a):
print(a)
print(b)
b = 9
f2(3)
# 让上面的f2 b变成全局变量,用global声明
b = 6
def f2(a):
global b
print(a)
print(b)
b = 9
f3(3)
- 闭包:指延伸了作用域的函数,它能访问定义体之外定义的非全局变量
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total/len(series)
avg = make_averager()
avg(10)
avg(4)
这里averager函数中的series是自由变量(指未在本地作用域中绑定的变量),averager的闭包延伸到那个函数的作用域之外,包含自由变量series的绑定。
# __code__属性中保存局部变量和自由变量的名称
avg.__code__.co_varnames
avg.__code__.co_freevars
# series在__closure__属性中
avg.__closure__
# cell_contents中保存着真正的值
avg.__closure__[0].cell_contents
闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。
- nonlocal:声明自由变量
# 这里和上面的f2一样,count += 1 赋值了,将count变成了局部变量,运行将会报错
def make_averager():
count = 0
total = 0
def averager(new_value):
count += 1
total +=new_value
return total / count
return averager
# 这里可以使用nonlocal将count、total声明为自由变量
def make_averager():
count = 0
total = 0
def averager(new_value):
nonlocal count, total
count += 1
total +=new_value
return total / count
return averager
装饰器
# 函数运行时间
import time
def clock(func):
def clocked(*args):
t0 = time.perf_counter()
# 函数开始执行
result = func(*args) # 自由变量func, 相当于执行被修饰的函数
# 函数执行结束
elapsed = time.perf_counter() - t0
name = func.__name__
arg_str = ', '.join(repr(arg) for arg in args)
print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result))
return clocked # 返回取代被装饰的函数
@clock
def snooze(seconds):
time.sleep(seconds)
@clock
def factorial(n):
return 1 if n < 2 else n * factorial(n-1)
snooze(.123)
factorial(6)
- functools.wraps装饰器:把相关的属性从func复制到clocked中
import time
import functools
def clock(func):
@functools.wraps(func)
def clocked(*args, **kwargs):
t0 = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - t0
name = func.__name__
arg_lst = []
if args:
arg_lst.append(', '.join(repr(arg) for arg in args))
# 处理关键字参数
if kwargs:
pairs = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())]
arg_str = ', '.join(arg_lst)
print('[%0.8fs] %s(%s) -> %r ' % (elapsed, name, arg_str, result))
return result
return clocked
- functools.lru_cache:实现缓存优化
import functools
@functools.lru_cache()
@clock
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 2) + fibonacci(n - 1)
fibonacci(6)
- functools.singledispatch:把普通函数变成泛函数,根据第一个参数的类型,以不同方式执行相同的操作
from functools import singledispatch
from collections import abc
import numbers
import html
@singledispatch
def htmlize(obj):
content = html.escape(repr(obj))
return '<pre>{}</pre>'.format(content)
@htmlize.register(str)
def _(text):
content = html.escape(text).replace('\n', '<br>\n')
return '<p>{0}</p>'.format(content)
@htmlize.register(numbers.Integral)
def _(n):
return '<pre>{0} (0x{0:x})</pre>'.format(n)
@htmlize.register(tuple)
@htmlize.register(abc.MutableSequence)
def _(seq):
inner = '</li>\n<li>'.join(htmlize(item) for item in seq)
return '<ul>\n<li>' + inner + '</li>\n</ul>'
- 参数化装饰器:通过装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上
#
registry = set()
def register(active=True):
def decorate(func):
print('running register(active=%s)->decorate(%s)' % (active, func))
if active:
registry.add(func)
else:
registry.discard(func)
return func
return decorate
@register(active=False)
def f1():
print('running f1()')
@register()
def f2():
print('running f2()')
def f3():
print('running f3()')
registry
registry()(f3)
registry(active=False)(f2)
registry
- 参数化clock装饰器
import time
DEFAULT_FMT = '[{elapsed:0.8f}s] {name}({args}) -> {result}'
def clock(fmt=DEFAULT_FMT):
def decorate(func):
def clocked(*_args):
t0 = time.time()
_result = func(*_args)
elapsed = time.time() - t0
name = func.__name__
args = ', '.join(repr(arg) for arg in _args)
result = repr(_result)
print(fmt.format(**locals()))
return _result
return clocked
return docorate
@clock()
def snooze(seconds):
time.sleep(seconds)
for i in range(3):
snooze(.123)
@clock('{name}: {elapsed}s')
def snooze(seconds):
time.sleep(seconds)
for i in range(3):
snooze(.123)
@clock('{name}({args}) dt={elapsed:0.3f}s')
def snooze(seconds):
time.sleep(seconds)
for i in range(3):
snooze(.123)
总结
- 进一步了解了装饰器的概念和基础知识,其实就是语法糖,可以简单理解为给被装饰的函数添加功能,如Flask中登录视图就用到装饰器,通过装饰器判断用户session是否登录。
- 闭包的概念,闭包与匿名函数,还有自由变量
- 参数化装饰器,通过调用装饰器工厂函数返回装饰器,再应用到要装饰的函数上,实现参数化装饰器
流畅的python2015