您的位置:首页 > 编程语言 > Python开发

关于python decorator找到的一篇比较好的文章

2012-12-06 12:11 507 查看

http://flask.pocoo.org/docs/patterns/viewdecorators/#view-decorators

View Decorators

Python has a really interesting feature called function decorators. Thisallow some really neat things for web applications. Because each view inFlask is a function decorators can be used to inject additionalfunctionality to one or more functions. Theroute()decorator
is the one you probably used already. But there are use casesfor implementing your own decorator. For instance, imagine you have aview that should only be used by people that are logged in to. If a usergoes to the site and is not logged in, they should be
redirected to thelogin page. This is a good example of a use case where a decorator is anexcellent solution.

Login Required Decorator

So let’s implement such a decorator. A decorator is a function thatreturns a function. Pretty simple actually. The only thing you have tokeep in mind when implementing something like this is to update the__name__,__module__ and
some other attributes of a function. This isoften forgotten, but you don’t have to do that by hand, there is afunction for that that is used like a decorator (functools.wraps()).

This example assumes that the login page is called
'login' and thatthe current user is stored as
g.user and None if there is no-onelogged in:

from functools import wraps
from flask import g, request, redirect, url_for

def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if g.user is None:
return redirect(url_for('login', next=request.url))
return f(*args, **kwargs)
return decorated_function


So how would you use that decorator now? Apply it as innermost decoratorto a view function. When applying further decorators, always rememberthat theroute()
decorator is the outermost:

@app.route('/secret_page')
@login_required
def secret_page():
pass


Caching Decorator

Imagine you have a view function that does an expensive calculation andbecause of that you would like to cache the generated results for acertain amount of time. A decorator would be nice for that. We’reassuming you have set up a cache like mentioned inCaching.

Here an example cache function. It generates the cache key from aspecific prefix (actually a format string) and the current path of therequest. Notice that we are using a function that first creates thedecorator that then decorates the function. Sounds awful?
Unfortunatelyit is a little bit more complex, but the code should still bestraightforward to read.

The decorated function will then work as follows

get the unique cache key for the current request base on the currentpath.
get the value for that key from the cache. If the cache returnedsomething we will return that value.
otherwise the original function is called and the return value isstored in the cache for the timeout provided (by default 5 minutes).

Here the code:

from functools import wraps
from flask import request

def cached(timeout=5 * 60, key='view/%s'):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
cache_key = key % request.path
rv = cache.get(cache_key)
if rv is not None:
return rv
rv = f(*args, **kwargs)
cache.set(cache_key, rv, timeout=timeout)
return rv
return decorated_function
return decorator


Notice that this assumes an instantiated cache object is available, seeCaching for more information.

Templating Decorator

A common pattern invented by the TurboGears guys a while back is atemplating decorator. The idea of that decorator is that you return adictionary with the values passed to the template from the view functionand the template is automatically rendered. With
that, the followingthree examples do exactly the same:

@app.route('/')
def index():
return render_template('index.html', value=42)

@app.route('/')
@templated('index.html')
def index():
return dict(value=42)

@app.route('/')
@templated()
def index():
return dict(value=42)


As you can see, if no template name is provided it will use the endpointof the URL map with dots converted to slashes +'.html'. Otherwisethe provided template name is used. When the decorated function
returns,the dictionary returned is passed to the template rendering function. IfNone is returned, an empty dictionary is assumed, if something else thana dictionary is returned we return it from the function unchanged. Thatway you can still use
the redirect function or return simple strings.

Here the code for that decorator:

from functools import wraps
from flask import request

def templated(template=None):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
template_name = template
if template_name is None:
template_name = request.endpoint \
.replace('.', '/') + '.html'
ctx = f(*args, **kwargs)
if ctx is None:
ctx = {}
elif not isinstance(ctx, dict):
return ctx
return render_template(template_name, **ctx)
return decorated_function
return decorator


Endpoint Decorator

When you want to use the werkzeug routing system for more flexibility youneed to map the endpoint as defined in theRuleto
a view function. This is possible with this decorator. For example:

from flask import Flask
from werkzeug.routing import Rule

app = Flask(__name__)
app.url_map.add(Rule('/', endpoint='index'))

@app.endpoint('index')
def my_index():
return "Hello world"
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: