首页 > 编程 > Python > 正文

Bottle框架中的装饰器类和描述符应用详解

2020-02-16 10:31:29
字体:
来源:转载
供稿:网友

最近在阅读Python微型Web框架Bottle的源码,发现了Bottle中有一个既是装饰器类又是描述符的有趣实现。刚好这两个点是Python比较的难理解,又混合在一起,让代码有些晦涩难懂。但理解代码之后不由得为Python语言的简洁优美赞叹。所以把相关知识和想法稍微整理,以供分享。

正文

Bottle是Python的一个微型Web框架,所有代码都在一个bottle.py文件中,只依赖标准库实现,兼容Python 2和Python 3,而且最新的稳定版0.12代码也只有3700行左右。虽然小,但它实现了Web框架基本功能。这里就不以过多的笔墨去展示Bottle框架,需要的请访问其网站了解更多。这里着重介绍与本文相关的重要对象request。在Bottle里,request对象代表了当前线程处理的请求,客户端发送的请求数据如表单数据,请求网站和cookie都可以从request对象中获得。下面是官方文档中的两个例子
from bottle import request, route, response, template

# 获取客户端cookie以实现登陆时问候用户功能@route('/hello')def hello():  name = request.cookie.username or 'Guest'  return template('Hello {{name}}', name=name) # 获取形如/forum?id=1&page=5的查询字符串中id和page变量的值route('/forum')def display_forum():  forum_id = request.query.id  page = request.query.page or '1'  return template('Forum ID: {{id}} (page {{page}})', id=forum_id, page=page)

那么Bottle是如何实现的呢?根据WSGI接口规定,所有的HTTP请求信息都包含在一个名为envrion的dict对象中。所以Bottle要做的就是把HTTP请求信息从environ解析出来。在深入Request类如何实现之前先要了解下Bottle的FormsDict。FormsDict与字典类相似,但扩展了一些功能,比如支持属性访问、一对多的键值对、WTForms支持等。它在Bottle中被广泛应用,如上面的示例中cookie和query数据都以FormsDict存储,所以我们可以用request.query.page的方式获取相应属性值。

下面是0.12版Bottle中Request类的部分代码,0.12版中Request类继承了BaseRequest,为了方便阅读我把代码合并在一起,同时还有重要的DictProperty的代码。需要说明的是Request类__init__传入的environ参数就是WSGI协议中包含HTTP请求信息的envrion,而query方法中的_parse_qsl函数可以接受形如/forum?id=1&page=5原始查询字符串然后以[(key1, value1), (ke2, value2), …]的list返回。

class DictProperty(object):  """ Property that maps to a key in a local dict-like attribute. """  def __init__(self, attr, key=None, read_only=False):    self.attr, self.key, self.read_only = attr, key, read_only  def __call__(self, func):    functools.update_wrapper(self, func, updated=[])    self.getter, self.key = func, self.key or func.__name__    return self  def __get__(self, obj, cls):    if obj is None: return self    key, storage = self.key, getattr(obj, self.attr)    if key not in storage: storage[key] = self.getter(obj)    return storage[key]  def __set__(self, obj, value):    if self.read_only: raise AttributeError("Read-Only property.")    getattr(obj, self.attr)[self.key] = value  def __delete__(self, obj):    if self.read_only: raise AttributeError("Read-Only property.")    del getattr(obj, self.attr)[self.key]class Request:  def __init__(self, environ=None):    self.environ {} if environ is None else envrion    self.envrion['bottle.request'] = self  @DictProperty('environ', 'bottle.request.query', read_only=True)  def query(self):    get = self.environ['bottle.get'] = FormsDict()    pairs = _parse_qsl(self.environ.get('QUERY_STRING', ''))    for key, value in pairs:      get[key] = value    return get            
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表