這個類的做用是將類中返回類字典對象的方法,變成一個read_only可控的屬性(描述符)。python
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
DictProperty這個類是一個裝飾器,也是一個描述符。能夠看出它其實是在操做,它的託管實例request的environ字典,當第一次訪問這個描述符的實例時,會把request.query(self)的結果放到environ這個字典裏。以後屢次訪問request.GET時,不用重複計算直接在environ中拿到結果。服務器
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實現了上下文管理器協議,因此你能夠像下面這樣寫,上下文管理器中的這個端點'/hello',不會受with塊外的那個同名端點影響。固然這兩個端點也不在一個Bottle實例上。。。。app
@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)
這個類實現了一個,value使用list存儲的字典 ,存儲重複 key 值時候 values 會以 list 形式都保存起來,默認返回list中最後一個value。ide
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
能夠探查一下 MultiDict生成對象的update 和 pop方法的行爲:函數
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'
MultiDict沒有重寫update方法爲何,使用update方法給更新key'a'的時候,會默認將value加到value list裏呢。由於update方法是的實現是經過操做dict[key]實現的。也就是使用了__getitem__, __setitem__, __delitem__方法。只要從新定製了這三個under方法,update就擁有如今的行爲。能夠定位到MutableMapping類中看一個update的實現。this
FormsDict類繼承了MultiDict,因此有相似的行爲。spa
HeaderDict類也繼承了MulticDict,不太重寫了__setitem__,__getitem__等方法,value仍是以list的形式存儲但只能有一個item。debug
In [85]: hd = HeaderDict() In [86]: hd['a']='b' In [87]: hd['a']='c' In [89]: hd.getall('a') Out[89]: ['c']
HeaderDict的key在存取的時候使用str.title處理過,因此key是無關大小寫的,同時對key中的'-'和'_'也作了等效處理,也就是request.headers.get('CONTENT-LENGTH')和request.headers.get('content_length')等效。code