Python装饰器与闭包(2)

这种情况下装饰器就使用到了闭包。JavaScript中的防抖与节流函数就是这种典型的装饰器行为。新函数一般会使用外部装饰器函数中的变量当做自由变量,对函数作出某种增强行为。

举个例子,我们知道,当Python函数的参数是个可变对象时,会产生意料之外的行为:

def foo(x, y=[]): y.append(x) print(y) foo(1) foo(2) foo(3)

输出:

[1] [1, 2] [1, 2, 3]

这是因为,函数的参数默认值保存在__defaults__属性中,指向了同一个列表:

>>> foo.__defaults__ ([1, 2, 3],)

我们就可以用一个装饰器在函数执行前取出默认值做深复制,然后覆盖函数原先的参数默认值:

import copy def fresh_defaults(func): defaults = func.__defaults__ def deco(*args, **kwargs): func.__defaults__ = copy.deepcopy(defaults) return func(*args, **kwargs) return deco @fresh_defaults def foo(x, y=[]): y.append(x) print(y) foo(1) foo(2) foo(3)

输出:

[1] [2] [3] 接收参数的装饰器

装饰器除了可以接受函数作为参数外,还可以接受其他参数。使用方法是:创建一个装饰器工厂,接受参数,返回一个装饰器,再把它应用到被装饰的函数上,语法如下:

def deco_factory(*args, **kwargs): def deco(func): print(args) return func return deco @deco_factory('factory') def foo(): pass

在Web框架中,通常要将URL模式映射到生成响应的view函数,并将view函数注册到某些中央注册处。之前我们曾经实现过一个简单的注册装饰器,只是注册了view函数,却没有URL映射,是远远不够的。

在Flask中,注册view函数需要一个装饰器:

@app.route('/hello') def hello(): return 'Hello, World'

原理就是使用了装饰器工厂,可以简单的模拟一下实现:

class App: def __init__(self): self.view_functions = {} def route(self, rule): def deco(view_func): self.view_functions[rule] = view_func return view_func return deco app = App() @app.route('/') def index(): pass @app.route('/hello') def hello(): pass for rule, view in app.view_functions.items(): print(rule, ':', view.__name__)

输出:

/ : index /hello : hello

还可以使用装饰器工厂来确定view函数可以允许哪些HTTP请求方法:

def action(methods): def deco(view): view.allow_methods = [method.lower() for method in methods] return view return deco @action(['GET', 'POST']) def view(request): if request.method.lower() in view.allow_methods: ...

重叠的装饰器

装饰器也是可以重叠使用的:

@d1 @d2 def foo(): pass

等同于:

foo = d1(d2(foo))

类装饰器

装饰器的参数也可以是一个类,也就是说,装饰器可以装饰类:

import types def deco(cls): for key, method in cls.__dict__.items(): if isinstance(method, types.FunctionType): print(key, ':', method.__name__) return cls @deco class Test: def __init__(self): pass def foo(self): pass

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/33cd502225d9227cb5ad5c621e299e15.html