Several interesting classes in Bottle

DictPropery

    The role of this class is to turn the method that returns a dictionary-like object in the class into a read_only controllable attribute (descriptor).

class Bottle(object):

    @DictProperty('environ', 'bottle.request.query', read_only=True)
    def query(self):
        """ The :attr:`query_string` parsed into a :class:`FormsDict`. These
            values are sometimes called "URL arguments" or "GET parameters", but
            not to be confused with "URL wildcards" as they are provided by the
            :class:`Router`. """
        get = self.environ['bottle.get'] = FormsDict()
        pairs = _parse_qsl(self.environ.get('QUERY_STRING', ''))
        for key, value in pairs:
            get[key] = value
        return get
    GET = query

class DictProperty(object):
    """ Property that maps to a key in a local dict-like attribute. """

    def __init__(self, attr, key=None, read_only=False):  # attr, key, read_only接收装饰器的三个位置参数
        self.attr, self.key, self.read_only = attr, key, read_only

    def __call__(self, func):  # func接收类方法request.query
        functools.update_wrapper(self, func, updated=[])
        self.getter, self.key = func, self.key or func.__name__
        return self

    def __get__(self, obj, cls):  # 当访问request.GET的时候,就会调用该方法, obj为当前对象,cls为当前类
        if obj is None: return self  # obj为None说明被装饰的方法作为类变量来访问(Bottle.query),返回描述符自身
        key, storage = self.key, getattr(obj, self.attr) 
        if key not in storage: storage[key] = self.getter(obj)  # 如果bottle.request.query不在storage也就是不在request.environ中的时候,在request.environ中添加'bottle.request.query':request.query(self), 即reqeuest.query(self)的返回值:GET参数的字典.
        return storage[key]

    def __set__(self, obj, value):  # 当request.GET被赋值时,调用__set__
        if self.read_only: raise AttributeError("Read-Only property.")  # raise read only
        getattr(obj, self.attr)[self.key] = value  # 在request.environ字典中添加一个'bottle.request.query':value。

    def __delete__(self, obj):  # 当该类方法被装饰的方法别删除是调用
        if self.read_only: raise AttributeError("Read-Only property.")
        del getattr(obj, self.attr)[self.key]  # 从request.environ字典中删除bottle.request.query

    The DictProperty class is a decorator as well as a descriptor. It can be seen that it is actually operating, the environ dictionary of its managed instance request, when the instance of this descriptor is accessed for the first time, the result of request.query(self) is put into the environ dictionary. When you access request.GET multiple times later, you can get the result directly in environ without repeating the calculation.

Bottle

class Bottle(object):

    def __call__(self, environ, start_response):  # 因为bottle__call__方法就是wsgi的协议函数,所以Bottle()实例就作为了wsgi服务器的appliction函数
        """ Each instance of :class:'Bottle' is a WSGI application. """
        return self.wsgi(environ, start_response)

    def __enter__(self):  # return
        """ Use this application as default for all module-level shortcuts. """
        default_app.push(self)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        default_app.pop()

    bottle implements the context manager protocol, so you can write like this, the endpoint '/hello' in the context manager will not be affected by the endpoint of the same name outside the with block. Of course these two endpoints are not on a Bottle instance either. . . .

@route('/hello')
def greet(name):
    return HTTPResponse('hello2')

with Bottle() as b_app:
    @b_app.route('/hello')
    def hello():
        return HTTPResponse('hello')

    run(host='localhost', port='8888', debug=True, reloader=True)

MultiDict

    This class implements a dictionary whose value is stored in a list. When storing repeated key values, the values ​​will be stored in the form of a list. By default, the last value in the list is returned.

class MultiDict(DictMixin):
    """ This dict stores multiple values per key, but behaves exactly like a
        normal dict in that it returns only the newest value for any given key.
        There are special methods available to access the full list of values.
    """

    def __init__(self, *a, **k):
        self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items())

    def __delitem__(self, key):
        del self.dict[key]

    def __getitem__(self, key):
        return self.dict[key][-1]

    def __setitem__(self, key, value):
        self.append(key, value)

    def keys(self):
        return self.dict.keys()

    def get(self, key, default=None, index=-1, type=None):
        try:
            val = self.dict[key][index]
            return type(val) if type else val
        except Exception:
            pass
        return default

    def append(self, key, value):
        """ Add a new value to the list of values for this key. """
        self.dict.setdefault(key, []).append(value)

    def replace(self, key, value):
        """ Replace the list of values with a single value. """
        self.dict[key] = [value]

    def getall(self, key):
        """ Return a (possibly empty) list of values for a key. """
        return self.dict.get(key) or []

    #: Aliases for WTForms to mimic other multi-dict APIs (Django)
    getone = get
    getlist = getall

You can examine the behavior of the update and pop methods of the MultiDict generated object:

In [69]: dm = MultiDict()
In [70]: dm['a'] = 1
In [71]: dm['a'] = 2
In [72]: dm.getlist('a')
Out[72]: [1, 2]
In [73]: dm.update({'a':3})
In [74]: dm.getlist('a')
Out[74]: [1, 2, 3]
In [75]: dm.pop('a')
Out[75]: 3
In [76]: dm['a']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-76-54e5ceebc4de> in <module>()
----> 1 dm['a']
e:\py3env\lib\site-packages\bottle-0.13.dev0-py3.6.egg\bottle.py in __getitem__(self, key)
   2093
   2094     def __getitem__(self, key):
-> 2095         return self.dict[key][-1]
   2096
   2097     def __setitem__(self, key, value):
KeyError: 'a'

    Why does MultiDict not rewrite the update method? When the update method is used to update the key'a', the value will be added to the value list by default. Because the update method is implemented by operating dict[key]. That is, the __getitem__, __setitem__, __delitem__ methods are used. As long as the three under methods are re-customized, update has the current behavior. You can locate an update implementation in the MutableMapping class.

    The FormsDict class extends MultiDict, so has similar behavior.

    The HeaderDict class also inherits MulticDict, but rewrites __setitem__, __getitem__ and other methods, the value is still stored in the form of a list but there can only be one item.

In [85]: hd = HeaderDict()
In [86]: hd['a']='b'
In [87]: hd['a']='c'
In [89]: hd.getall('a')
Out[89]: ['c']

    The key of HeaderDict is processed by str.title when accessing, so the key is case-insensitive, and the '-' and '_' in the key are also processed equivalently, that is, request.headers.get( 'CONTENT-LENGTH') and request.headers.get('content_length') are equivalent.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325434462&siteId=291194637