├── dict ├── Readme.md ├── TypeConversionDict.py ├── Storage.py ├── base.py ├── DictProperty.py ├── CallbackDict.py ├── CaseInsensitiveDict.py ├── MultiDict.py └── MultiDict2.py ├── context └── ignore_exceptions.py ├── list └── base.py ├── datastructure ├── queue.py └── stack.py ├── README.md ├── .gitignore ├── string └── base.py ├── common └── base.py ├── network └── base.py ├── property ├── CachedProperty.py └── DictProperty.py ├── sequence ├── uniq.py └── chunk.py ├── html └── base.py ├── config └── ConfigDict.py └── cache └── lru_cache.py /dict/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | base.py 一些dict相关的方法 5 | 6 | CaseInsensitiveDict.py 键值大小写不敏感的key 7 | 8 | 9 | -------------------------------------------------------------------------------- /context/ignore_exceptions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | from contextlib import contextmanager 5 | 6 | @contextmanager 7 | def ignored(*exceptions): 8 | try: 9 | yield 10 | except exceptions: 11 | pass 12 | 13 | with ignored(ValueError): 14 | int('string') 15 | -------------------------------------------------------------------------------- /list/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | 5 | #source: https://github.com/bottlepy/bottle/blob/master/bottle.py 6 | def makelist(data): 7 | if isinstance(data, (tuple, list, set, dict)): 8 | return list(data) 9 | elif data: 10 | return [data] 11 | else: 12 | return [] 13 | -------------------------------------------------------------------------------- /datastructure/queue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | 5 | def requeue(queue, index=-1): 6 | """Returns the element at index after moving it to the beginning of the queue. 7 | 8 | >>> x = [1, 2, 3, 4] 9 | >>> requeue(x) 10 | 4 11 | >>> x 12 | [4, 1, 2, 3] 13 | """ 14 | x = queue.pop(index) 15 | queue.insert(0, x) 16 | return x 17 | -------------------------------------------------------------------------------- /datastructure/stack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | 5 | # source: webpy 6 | def restack(stack, index=0): 7 | """Returns the element at index after moving it to the top of stack. 8 | 9 | >>> x = [1, 2, 3, 4] 10 | >>> restack(x) 11 | 1 12 | >>> x 13 | [2, 3, 4, 1] 14 | """ 15 | x = stack.pop(index) 16 | stack.append(x) 17 | return x 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pyutils 2 | ======= 3 | 4 | 看各类源码过程中收集到的好的函数/类/模块等 5 | 6 | 7 | 源码阅读列表; 8 | 9 | * [requests](https://github.com/kennethreitz/requests) [DONE] 10 | * [Python-Snippets](http://snippets.readthedocs.org/en/latest/index.html) [DONE] 11 | * [bottle](https://github.com/defnull/bottle) [DONE] 12 | 13 | Doing 14 | 15 | * [Flask]() 16 | * [Django]() 17 | * [Werkzeug]() 18 | * [webpy]() 19 | 20 | TODO: 21 | 22 | 1. 如何优雅地处理const, 管理一个项目的常量 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | -------------------------------------------------------------------------------- /string/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | 5 | #source: https://github.com/kennethreitz/requests/blob/master/requests/utils.py 6 | def iter_slices(string, slice_length): 7 | """Iterate over slices of a string.""" 8 | pos = 0 9 | while pos < len(string): 10 | yield string[pos:pos + slice_length] 11 | pos += slice_length 12 | 13 | if __name__ == '__main__': 14 | 15 | s = "abcdefg" 16 | for i in iter_slices(s, 2): 17 | print i 18 | #result: 19 | #ab 20 | #cd 21 | #ef 22 | #g 23 | 24 | 25 | -------------------------------------------------------------------------------- /dict/TypeConversionDict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | # from werkzeug 4 | 5 | 6 | class TypeConversionDict(dict): 7 | def get(self, key, default=None, type=None): 8 | try: 9 | rv = self[key] 10 | if type is not None: 11 | rv = type(rv) 12 | except (KeyError, ValueError): 13 | rv = default 14 | 15 | return rv 16 | 17 | 18 | if __name__ == '__main__': 19 | d = TypeConversionDict({'a': '1', 'b': 'i'}) 20 | 21 | a = d.get('a', type=int) 22 | print type(a) 23 | print a 24 | 25 | b = d.get('b', default=0, type=int) 26 | print type(b) 27 | print b 28 | -------------------------------------------------------------------------------- /common/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | import io 5 | import os 6 | 7 | 8 | # source: https://github.com/kennethreitz/requests/blob/master/requests/utils.py 9 | def super_len(o): 10 | if hasattr(o, '__len__'): 11 | return len(o) 12 | 13 | if hasattr(o, 'len'): 14 | return o.len 15 | 16 | if hasattr(o, 'fileno'): 17 | try: 18 | fileno = o.fileno() 19 | except io.UnsupportedOperation: 20 | pass 21 | else: 22 | return os.fstat(fileno).st_size 23 | 24 | if hasattr(o, 'getvalue'): 25 | # e.g. BytesIO, cStringIO.StringIO 26 | return len(o.getvalue()) 27 | 28 | 29 | if __name__ == '__main__': 30 | print super_len("abcde") 31 | print super_len([1, 2, 3]) 32 | -------------------------------------------------------------------------------- /network/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | import socket 5 | import struct 6 | 7 | #source https://github.com/kennethreitz/requests/blob/master/requests/utils.py 8 | def dotted_netmask(mask): 9 | """ 10 | Converts mask from /xx format to xxx.xxx.xxx.xxx 11 | Example: if mask is 24 function returns 255.255.255.0 12 | """ 13 | bits = 0xffffffff ^ (1 << 32 - mask) - 1 14 | return socket.inet_ntoa(struct.pack('>I', bits)) 15 | 16 | 17 | #source https://github.com/kennethreitz/requests/blob/master/requests/utils.py 18 | def is_ipv4_address(string_ip): 19 | try: 20 | socket.inet_aton(string_ip) 21 | except socket.error: 22 | return False 23 | return True 24 | 25 | 26 | if __name__ == '__main__': 27 | print dotted_netmask(24) 28 | print is_ipv4_address("127.0.0.1") 29 | 30 | -------------------------------------------------------------------------------- /property/CachedProperty.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # source: https://github.com/defnull/bottle/blob/master/bottle.py 5 | class cached_property(object): 6 | """ A property that is only computed once per instance and then replaces 7 | itself with an ordinary attribute. Deleting the attribute resets the 8 | property. """ 9 | 10 | def __init__(self, func): 11 | self.__doc__ = getattr(func, '__doc__') 12 | self.func = func 13 | 14 | def __get__(self, obj, cls): 15 | if obj is None: return self 16 | value = obj.__dict__[self.func.__name__] = self.func(obj) 17 | return value 18 | 19 | 20 | class A(object): 21 | 22 | @cached_property 23 | def key(self): 24 | print "call key()" 25 | return "hello" 26 | 27 | if __name__ == '__main__': 28 | a = A() 29 | 30 | 31 | print a.key 32 | print a.key 33 | 34 | 35 | -------------------------------------------------------------------------------- /sequence/uniq.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | 5 | # source: webpy 6 | def uniq(seq, key=None): 7 | """ 8 | Removes duplicate elements from a list while preserving the order of the rest. 9 | 10 | >>> uniq([9,0,2,1,0]) 11 | [9, 0, 2, 1] 12 | 13 | The value of the optional `key` parameter should be a function that 14 | takes a single argument and returns a key to test the uniqueness. 15 | 16 | >>> uniq(["Foo", "foo", "bar"], key=lambda s: s.lower()) 17 | ['Foo', 'bar'] 18 | """ 19 | key = key or (lambda x: x) 20 | seen = set() 21 | result = [] 22 | for v in seq: 23 | k = key(v) 24 | if k in seen: 25 | continue 26 | seen.add(k) 27 | result.append(v) 28 | return result 29 | 30 | 31 | if __name__ == '__main__': 32 | 33 | print uniq([9, 0, 2, 1, 0]) 34 | print uniq(["Foo", "foo", "bar"], key=lambda s: s.lower()) 35 | -------------------------------------------------------------------------------- /sequence/chunk.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | from itertools import chain, islice 5 | 6 | 7 | def chunks(iterable, size, format=iter): 8 | it = iter(iterable) 9 | while True: 10 | yield format(chain((it.next(), ), islice(it, size - 1))) 11 | 12 | 13 | # source: webpy 14 | def group(seq, size): 15 | """ 16 | Returns an iterator over a series of lists of length size from iterable. 17 | 18 | >>> list(group([1,2,3,4], 2)) 19 | [[1, 2], [3, 4]] 20 | >>> list(group([1,2,3,4,5], 2)) 21 | [[1, 2], [3, 4], [5]] 22 | """ 23 | 24 | def take(seq, n): 25 | for i in xrange(n): 26 | yield seq.next() 27 | 28 | if not hasattr(seq, 'next'): 29 | seq = iter(seq) 30 | while True: 31 | x = list(take(seq, size)) 32 | if x: 33 | yield x 34 | else: 35 | break 36 | 37 | 38 | if __name__ == '__main__': 39 | a = range(10) 40 | 41 | for i in chunks(a, 3, format=iter): 42 | print i 43 | print tuple(i) 44 | 45 | print list(group([1, 2, 3, 4, 5], 2)) 46 | -------------------------------------------------------------------------------- /dict/Storage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | 5 | class Storage(dict): 6 | """ 7 | A Storage object is like a dictionary except `obj.foo` can be used 8 | in addition to `obj['foo']`. 9 | 10 | >>> o = Storage(a=1) 11 | >>> o.a 12 | 1 13 | >>> o['a'] 14 | 1 15 | >>> o.a = 2 16 | >>> o['a'] 17 | 2 18 | >>> del o.a 19 | >>> o.a 20 | Traceback (most recent call last): 21 | ... 22 | AttributeError: 'a' 23 | 24 | """ 25 | def __getattr__(self, key): 26 | try: 27 | return self[key] 28 | except KeyError, k: 29 | raise AttributeError(k) 30 | 31 | def __setattr__(self, key, value): 32 | self[key] = value 33 | 34 | def __delattr__(self, key): 35 | try: 36 | del self[key] 37 | except KeyError, k: 38 | raise AttributeError(k) 39 | 40 | def __repr__(self): 41 | return '' 42 | 43 | 44 | if __name__ == '__main__': 45 | o = Storage(a=1) 46 | print o 47 | print o.a 48 | print o['a'] 49 | 50 | o.a = 2 51 | print o.a 52 | 53 | -------------------------------------------------------------------------------- /dict/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | 5 | # source: webpy 6 | def dictreverse(mapping): 7 | """ 8 | Returns a new dictionary with keys and values swapped. 9 | 10 | >>> dictreverse({1: 2, 3: 4}) 11 | {2: 1, 4: 3} 12 | """ 13 | return dict([(value, key) for (key, value) in mapping.iteritems()]) 14 | 15 | 16 | # source: webpy 17 | def dictfind(dictionary, element): 18 | """ 19 | Returns a key whose value in `dictionary` is `element` 20 | or, if none exists, None. 21 | 22 | >>> d = {1:2, 3:4} 23 | >>> dictfind(d, 4) 24 | 3 25 | >>> dictfind(d, 5) 26 | """ 27 | for (key, value) in dictionary.iteritems(): 28 | if element is value: 29 | return key 30 | 31 | 32 | # source: webpy 33 | def dictfindall(dictionary, element): 34 | """ 35 | Returns the keys whose values in `dictionary` are `element` 36 | or, if none exists, []. 37 | 38 | >>> d = {1:4, 3:4} 39 | >>> dictfindall(d, 4) 40 | [1, 3] 41 | >>> dictfindall(d, 5) 42 | [] 43 | """ 44 | res = [] 45 | for (key, value) in dictionary.iteritems(): 46 | if element is value: 47 | res.append(key) 48 | return res 49 | -------------------------------------------------------------------------------- /property/DictProperty.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | import functools 5 | 6 | # source: https://github.com/defnull/bottle/blob/master/bottle.py 7 | class DictProperty(object): 8 | """ Property that maps to a key in a local dict-like attribute. """ 9 | def __init__(self, attr, key=None, read_only=False): 10 | self.attr, self.key, self.read_only = attr, key, read_only 11 | 12 | def __call__(self, func): 13 | functools.update_wrapper(self, func, updated=[]) 14 | self.getter, self.key = func, self.key or func.__name__ 15 | return self 16 | 17 | def __get__(self, obj, cls): 18 | if obj is None: return self 19 | key, storage = self.key, getattr(obj, self.attr) 20 | if key not in storage: storage[key] = self.getter(obj) 21 | return storage[key] 22 | 23 | def __set__(self, obj, value): 24 | if self.read_only: raise AttributeError("Read-Only property.") 25 | getattr(obj, self.attr)[self.key] = value 26 | 27 | def __delete__(self, obj): 28 | if self.read_only: raise AttributeError("Read-Only property.") 29 | del getattr(obj, self.attr)[self.key] 30 | 31 | # 使用 32 | class A(object): 33 | 34 | __slots__ = ('environ', ) 35 | 36 | def __init__(self, environ=None): 37 | self.environ = {} if environ is None else environ 38 | 39 | @DictProperty('environ', 'attr.key') 40 | def key(self): 41 | return "hello" 42 | 43 | if __name__ == '__main__': 44 | 45 | a = A() 46 | print a.environ 47 | print a.key 48 | print a.environ 49 | 50 | -------------------------------------------------------------------------------- /html/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | 5 | #source: https://github.com/bottlepy/bottle/blob/master/bottle.py 6 | def html_escape(string): 7 | """ Escape HTML special characters ``&<>`` and quotes ``'"``. """ 8 | return string.replace('&', '&').replace('<', '<').replace('>', '>')\ 9 | .replace('"', '"').replace("'", ''') 10 | 11 | 12 | #source: https://github.com/bottlepy/bottle/blob/master/bottle.py 13 | def html_quote(string): 14 | """ Escape and quote a string to be used as an HTTP attribute.""" 15 | return '"%s"' % html_escape(string).replace('\n', ' ')\ 16 | .replace('\r', ' ').replace('\t', ' ') 17 | 18 | 19 | # source: django 20 | def htmlquote(text): 21 | r""" 22 | Encodes `text` for raw use in HTML. 23 | 24 | >>> htmlquote(u"<'&\">") 25 | u'<'&">' 26 | """ 27 | text = text.replace(u"&", u"&") # Must be done first! 28 | text = text.replace(u"<", u"<") 29 | text = text.replace(u">", u">") 30 | text = text.replace(u"'", u"'") 31 | text = text.replace(u'"', u""") 32 | return text 33 | 34 | 35 | # source: django 36 | def htmlunquote(text): 37 | r""" 38 | Decodes `text` that's HTML quoted. 39 | 40 | >>> htmlunquote(u'<'&">') 41 | u'<\'&">' 42 | """ 43 | text = text.replace(u""", u'"') 44 | text = text.replace(u"'", u"'") 45 | text = text.replace(u">", u">") 46 | text = text.replace(u"<", u"<") 47 | text = text.replace(u"&", u"&") # Must be done last! 48 | return text 49 | -------------------------------------------------------------------------------- /dict/DictProperty.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | import functools 5 | 6 | 7 | class DictProperty(object): 8 | """ Property that maps to a key in a local dict-like attribute. """ 9 | 10 | def __init__(self, attr, key=None, read_only=False): 11 | self.attr, self.key, self.read_only = attr, key, read_only 12 | 13 | def __call__(self, func): 14 | functools.update_wrapper(self, func, updated=[]) 15 | self.getter, self.key = func, self.key or func.__name__ 16 | return self 17 | 18 | def __get__(self, obj, cls): 19 | if obj is None: 20 | return self 21 | 22 | key, storage = self.key, getattr(obj, self.attr) 23 | if key not in storage: 24 | storage[key] = self.getter(obj) 25 | return storage[key] 26 | 27 | def __set__(self, obj, value): 28 | if self.read_only: 29 | raise AttributeError("Read-Only property.") 30 | 31 | getattr(obj, self.attr)[self.key] = value 32 | 33 | def __delete__(self, obj): 34 | if self.read_only: 35 | raise AttributeError("Read-Only property.") 36 | 37 | del getattr(obj, self.attr)[self.key] 38 | 39 | 40 | class A(object): 41 | __slots__ = ('person', ) 42 | 43 | def __init__(self): 44 | self.person = {} 45 | 46 | @DictProperty('person', 'gender', read_only=True) 47 | def gender(self): 48 | return 1 49 | 50 | @DictProperty('person', 'name', read_only=False) 51 | def name(self): 52 | return 'Tom' 53 | 54 | 55 | 56 | if __name__ == '__main__': 57 | 58 | a = A() 59 | print a.gender 60 | print a.person.get("gender") 61 | 62 | # error: readonly 63 | # a.gender = 2 64 | 65 | 66 | print a.name 67 | a.name = 'Ken' 68 | print a.name 69 | print a.person.get("name") 70 | -------------------------------------------------------------------------------- /dict/CallbackDict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | # from werkzeug 4 | 5 | 6 | class UpdateDictMixin(object): 7 | """Makes dicts call `self.on_update` on modifications. 8 | 9 | .. versionadded:: 0.5 10 | 11 | :private: 12 | """ 13 | 14 | on_update = None 15 | 16 | def calls_update(name): 17 | def oncall(self, *args, **kw): 18 | rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw) 19 | if self.on_update is not None: 20 | self.on_update(self) 21 | return rv 22 | 23 | oncall.__name__ = name 24 | return oncall 25 | 26 | def setdefault(self, key, default=None): 27 | modified = key not in self 28 | rv = super(UpdateDictMixin, self).setdefault(key, default) 29 | if modified and self.on_update is not None: 30 | self.on_update(self) 31 | return rv 32 | 33 | def pop(self, key, default=None): 34 | modified = key in self 35 | if default is None: 36 | rv = super(UpdateDictMixin, self).pop(key) 37 | else: 38 | rv = super(UpdateDictMixin, self).pop(key, default) 39 | if modified and self.on_update is not None: 40 | self.on_update(self) 41 | return rv 42 | 43 | __setitem__ = calls_update('__setitem__') 44 | __delitem__ = calls_update('__delitem__') 45 | clear = calls_update('clear') 46 | popitem = calls_update('popitem') 47 | update = calls_update('update') 48 | del calls_update 49 | 50 | 51 | class CallbackDict(UpdateDictMixin, dict): 52 | """A dict that calls a function passed every time something is changed. 53 | The function is passed the dict instance. 54 | """ 55 | 56 | def __init__(self, initial=None, on_update=None): 57 | dict.__init__(self, initial or ()) 58 | self.on_update = on_update 59 | 60 | def __repr__(self): 61 | return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self)) 62 | 63 | if __name__ == '__main__': 64 | 65 | def callback(the_dict): 66 | print the_dict.items() 67 | 68 | a = CallbackDict(initial={1: 'a'}, on_update=callback) 69 | print a 70 | 71 | a[2] = 'b' 72 | a[1] = 'c' 73 | -------------------------------------------------------------------------------- /dict/CaseInsensitiveDict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | import collections 5 | 6 | 7 | """ 8 | 来源: requests 9 | 文件位置: https://github.com/kennethreitz/requests/blob/master/requests/structures.py 10 | 11 | Key大小写不敏感的Dict 12 | 13 | """ 14 | 15 | 16 | class CaseInsensitiveDict(collections.MutableMapping): 17 | """ 18 | A case-insensitive ``dict``-like object. 19 | 20 | Implements all methods and operations of 21 | ``collections.MutableMapping`` as well as dict's ``copy``. Also 22 | provides ``lower_items``. 23 | 24 | All keys are expected to be strings. The structure remembers the 25 | case of the last key to be set, and ``iter(instance)``, 26 | ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` 27 | will contain case-sensitive keys. However, querying and contains 28 | testing is case insensitive: 29 | 30 | cid = CaseInsensitiveDict() 31 | cid['Accept'] = 'application/json' 32 | cid['aCCEPT'] == 'application/json' # True 33 | list(cid) == ['Accept'] # True 34 | 35 | For example, ``headers['content-encoding']`` will return the 36 | value of a ``'Content-Encoding'`` response header, regardless 37 | of how the header name was originally stored. 38 | 39 | If the constructor, ``.update``, or equality comparison 40 | operations are given keys that have equal ``.lower()``s, the 41 | behavior is undefined. 42 | 43 | """ 44 | def __init__(self, data=None, **kwargs): 45 | self._store = dict() 46 | if data is None: 47 | data = {} 48 | self.update(data, **kwargs) 49 | 50 | def __setitem__(self, key, value): 51 | # Use the lowercased key for lookups, but store the actual 52 | # key alongside the value. 53 | self._store[key.lower()] = (key, value) 54 | 55 | def __getitem__(self, key): 56 | return self._store[key.lower()][1] 57 | 58 | def __delitem__(self, key): 59 | del self._store[key.lower()] 60 | 61 | def __iter__(self): 62 | return (casedkey for casedkey, mappedvalue in self._store.values()) 63 | 64 | def __len__(self): 65 | return len(self._store) 66 | 67 | def lower_items(self): 68 | """Like iteritems(), but with all lowercase keys.""" 69 | return ( 70 | (lowerkey, keyval[1]) 71 | for (lowerkey, keyval) 72 | in self._store.items() 73 | ) 74 | 75 | def __eq__(self, other): 76 | if isinstance(other, collections.Mapping): 77 | other = CaseInsensitiveDict(other) 78 | else: 79 | return NotImplemented 80 | # Compare insensitively 81 | return dict(self.lower_items()) == dict(other.lower_items()) 82 | 83 | # Copy is required 84 | def copy(self): 85 | return CaseInsensitiveDict(self._store.values()) 86 | 87 | def __repr__(self): 88 | return str(dict(self.items())) 89 | 90 | 91 | if __name__ == '__main__': 92 | cid = CaseInsensitiveDict() 93 | cid['Accept'] = 'application/json' 94 | 95 | print cid.keys() 96 | print cid.values() 97 | print cid.items() 98 | 99 | #用处 true / true 100 | print 'accept' in cid 101 | print 'aCCEPT' in cid 102 | 103 | cid['aCCEPT'] = 'application/text' 104 | print cid.items() 105 | 106 | print list(cid) == ['Accept'] # True 107 | 108 | 109 | -------------------------------------------------------------------------------- /dict/MultiDict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | import sys 5 | from collections import MutableMapping 6 | 7 | py3k = sys.version_info > (3, 0, 0) 8 | 9 | 10 | # source : https://github.com/bottlepy/bottle/blob/master/bottle.py 11 | class MultiDict(MutableMapping): 12 | """ This dict stores multiple values per key, but behaves exactly like a 13 | normal dict in that it returns only the newest value for any given key. 14 | There are special methods available to access the full list of values. 15 | """ 16 | 17 | def __init__(self, *a, **k): 18 | self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items()) 19 | 20 | def __len__(self): 21 | return len(self.dict) 22 | 23 | def __iter__(self): 24 | return iter(self.dict) 25 | 26 | def __contains__(self, key): 27 | return key in self.dict 28 | 29 | def __delitem__(self, key): 30 | del self.dict[key] 31 | 32 | def __getitem__(self, key): 33 | return self.dict[key][-1] 34 | 35 | def __setitem__(self, key, value): 36 | self.append(key, value) 37 | 38 | def keys(self): 39 | return self.dict.keys() 40 | 41 | if py3k: 42 | 43 | def values(self): 44 | return (v[-1] for v in self.dict.values()) 45 | 46 | def items(self): 47 | return ((k, v[-1]) for k, v in self.dict.items()) 48 | 49 | def allitems(self): 50 | return ((k, v) for k, vl in self.dict.items() for v in vl) 51 | 52 | iterkeys = keys 53 | itervalues = values 54 | iteritems = items 55 | iterallitems = allitems 56 | 57 | else: 58 | 59 | def values(self): 60 | return [v[-1] for v in self.dict.values()] 61 | 62 | def items(self): 63 | return [(k, v[-1]) for k, v in self.dict.items()] 64 | 65 | def iterkeys(self): 66 | return self.dict.iterkeys() 67 | 68 | def itervalues(self): 69 | return (v[-1] for v in self.dict.itervalues()) 70 | 71 | def iteritems(self): 72 | return ((k, v[-1]) for k, v in self.dict.iteritems()) 73 | 74 | def iterallitems(self): 75 | return ((k, v) for k, vl in self.dict.iteritems() for v in vl) 76 | 77 | def allitems(self): 78 | return [(k, v) for k, vl in self.dict.iteritems() for v in vl] 79 | 80 | def get(self, key, default=None, index=-1, type=None): 81 | """ Return the most recent value for a key. 82 | 83 | :param default: The default value to be returned if the key is not 84 | present or the type conversion fails. 85 | :param index: An index for the list of available values. 86 | :param type: If defined, this callable is used to cast the value 87 | into a specific type. Exception are suppressed and result in 88 | the default value to be returned. 89 | """ 90 | try: 91 | val = self.dict[key][index] 92 | return type(val) if type else val 93 | except Exception: 94 | pass 95 | return default 96 | 97 | def append(self, key, value): 98 | """ Add a new value to the list of values for this key. """ 99 | self.dict.setdefault(key, []).append(value) 100 | 101 | def replace(self, key, value): 102 | """ Replace the list of values with a single value. """ 103 | self.dict[key] = [value] 104 | 105 | def getall(self, key): 106 | """ Return a (possibly empty) list of values for a key. """ 107 | return self.dict.get(key) or [] 108 | 109 | 110 | if __name__ == '__main__': 111 | 112 | d = MultiDict() 113 | 114 | d['foo'] = 1 115 | d['foo'] = 2 116 | 117 | d['bar'] = 3 118 | 119 | print d.get('foo') # default last one 120 | print d.getall('foo') 121 | 122 | print d.items() 123 | print d.allitems() 124 | -------------------------------------------------------------------------------- /config/ConfigDict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | from configparser import ConfigParser 5 | 6 | # source: https://github.com/defnull/bottle/blob/master/bottle.py 7 | class ConfigDict(dict): 8 | """ A dict-like configuration storage with additional support for 9 | namespaces, validators, meta-data, on_change listeners and more. 10 | """ 11 | 12 | __slots__ = ('_meta', '_on_change') 13 | 14 | def __init__(self): 15 | self._meta = {} 16 | self._on_change = lambda name, value: None 17 | 18 | def load_config(self, filename): 19 | """ Load values from an ``*.ini`` style config file. 20 | 21 | If the config file contains sections, their names are used as 22 | namespaces for the values within. The two special sections 23 | ``DEFAULT`` and ``bottle`` refer to the root namespace (no prefix). 24 | """ 25 | conf = ConfigParser() 26 | conf.read(filename) 27 | for section in conf.sections(): 28 | for key, value in conf.items(section): 29 | if section not in ('DEFAULT', 'bottle'): 30 | key = section + '.' + key 31 | self[key] = value 32 | return self 33 | 34 | def load_dict(self, source, namespace=''): 35 | """ Load values from a dictionary structure. Nesting can be used to 36 | represent namespaces. 37 | 38 | >>> c = ConfigDict() 39 | >>> c.load_dict({'some': {'namespace': {'key': 'value'} } }) 40 | {'some.namespace.key': 'value'} 41 | """ 42 | for key, value in source.items(): 43 | if isinstance(key, str): 44 | nskey = (namespace + '.' + key).strip('.') 45 | if isinstance(value, dict): 46 | self.load_dict(value, namespace=nskey) 47 | else: 48 | self[nskey] = value 49 | else: 50 | raise TypeError('Key has type %r (not a string)' % type(key)) 51 | return self 52 | 53 | def update(self, *a, **ka): 54 | """ If the first parameter is a string, all keys are prefixed with this 55 | namespace. Apart from that it works just as the usual dict.update(). 56 | Example: ``update('some.namespace', key='value')`` """ 57 | prefix = '' 58 | if a and isinstance(a[0], str): 59 | prefix = a[0].strip('.') + '.' 60 | a = a[1:] 61 | for key, value in dict(*a, **ka).items(): 62 | self[prefix+key] = value 63 | 64 | def setdefault(self, key, value): 65 | if key not in self: 66 | self[key] = value 67 | 68 | def __setitem__(self, key, value): 69 | if not isinstance(key, str): 70 | raise TypeError('Key has type %r (not a string)' % type(key)) 71 | value = self.meta_get(key, 'filter', lambda x: x)(value) 72 | if key in self and self[key] is value: 73 | return 74 | self._on_change(key, value) 75 | dict.__setitem__(self, key, value) 76 | 77 | def __delitem__(self, key): 78 | self._on_change(key, None) 79 | dict.__delitem__(self, key) 80 | 81 | def meta_get(self, key, metafield, default=None): 82 | """ Return the value of a meta field for a key. """ 83 | return self._meta.get(key, {}).get(metafield, default) 84 | 85 | def meta_set(self, key, metafield, value): 86 | """ Set the meta field for a key to a new value. This triggers the 87 | on-change handler for existing keys. """ 88 | self._meta.setdefault(key, {})[metafield] = value 89 | if key in self: 90 | self[key] = self[key] 91 | 92 | def meta_list(self, key): 93 | """ Return an iterable of meta field names defined for a key. """ 94 | return self._meta.get(key, {}).keys() 95 | 96 | #useage: 97 | #self.config = ConfigDict().load_dict(config) 98 | #self.config = ConfigDict() 99 | #self.config._on_change = functools.partial(self.trigger_hook, 'config') 100 | #self.config.meta_set('autojson', 'validate', bool) 101 | #self.config.meta_set('catchall', 'validate', bool) 102 | #self.config['catchall'] = catchall 103 | #self.config['autojson'] = autojson 104 | -------------------------------------------------------------------------------- /cache/lru_cache.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # source: django 5 | try: 6 | from functools import lru_cache 7 | 8 | except ImportError: 9 | # backport of Python's 3.3 lru_cache, written by Raymond Hettinger and 10 | # licensed under MIT license, from: 11 | # 12 | # Should be removed when Django only supports Python 3.2 and above. 13 | 14 | from collections import namedtuple 15 | from functools import update_wrapper 16 | from threading import RLock 17 | 18 | _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) 19 | 20 | class _HashedSeq(list): 21 | __slots__ = 'hashvalue' 22 | 23 | def __init__(self, tup, hash=hash): 24 | self[:] = tup 25 | self.hashvalue = hash(tup) 26 | 27 | def __hash__(self): 28 | return self.hashvalue 29 | 30 | def _make_key(args, kwds, typed, 31 | kwd_mark = (object(),), 32 | fasttypes = {int, str, frozenset, type(None)}, 33 | sorted=sorted, tuple=tuple, type=type, len=len): 34 | 'Make a cache key from optionally typed positional and keyword arguments' 35 | key = args 36 | if kwds: 37 | sorted_items = sorted(kwds.items()) 38 | key += kwd_mark 39 | for item in sorted_items: 40 | key += item 41 | if typed: 42 | key += tuple(type(v) for v in args) 43 | if kwds: 44 | key += tuple(type(v) for k, v in sorted_items) 45 | elif len(key) == 1 and type(key[0]) in fasttypes: 46 | return key[0] 47 | return _HashedSeq(key) 48 | 49 | def lru_cache(maxsize=100, typed=False): 50 | """Least-recently-used cache decorator. 51 | 52 | If *maxsize* is set to None, the LRU features are disabled and the cache 53 | can grow without bound. 54 | 55 | If *typed* is True, arguments of different types will be cached separately. 56 | For example, f(3.0) and f(3) will be treated as distinct calls with 57 | distinct results. 58 | 59 | Arguments to the cached function must be hashable. 60 | 61 | View the cache statistics named tuple (hits, misses, maxsize, currsize) with 62 | f.cache_info(). Clear the cache and statistics with f.cache_clear(). 63 | Access the underlying function with f.__wrapped__. 64 | 65 | See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used 66 | 67 | """ 68 | 69 | # Users should only access the lru_cache through its public API: 70 | # cache_info, cache_clear, and f.__wrapped__ 71 | # The internals of the lru_cache are encapsulated for thread safety and 72 | # to allow the implementation to change (including a possible C version). 73 | def decorating_function(user_function): 74 | 75 | cache = dict() 76 | stats = [0, 0] # make statistics updateable non-locally 77 | HITS, MISSES = 0, 1 # names for the stats fields 78 | make_key = _make_key 79 | cache_get = cache.get # bound method to lookup key or return None 80 | _len = len # localize the global len() function 81 | lock = RLock() # because linkedlist updates aren't threadsafe 82 | root = [] # root of the circular doubly linked list 83 | root[:] = [root, root, None, None] # initialize by pointing to self 84 | nonlocal_root = [root] # make updateable non-locally 85 | PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields 86 | 87 | if maxsize == 0: 88 | 89 | def wrapper(*args, **kwds): 90 | # no caching, just do a statistics update after a successful call 91 | result = user_function(*args, **kwds) 92 | stats[MISSES] += 1 93 | return result 94 | 95 | elif maxsize is None: 96 | 97 | def wrapper(*args, **kwds): 98 | # simple caching without ordering or size limit 99 | key = make_key(args, kwds, typed) 100 | result = cache_get(key, root) # root used here as a unique not-found sentinel 101 | if result is not root: 102 | stats[HITS] += 1 103 | return result 104 | result = user_function(*args, **kwds) 105 | cache[key] = result 106 | stats[MISSES] += 1 107 | return result 108 | else: 109 | 110 | def wrapper(*args, **kwds): 111 | # size limited caching that tracks accesses by recency 112 | key = make_key(args, kwds, typed) if kwds or typed else args 113 | with lock: 114 | link = cache_get(key) 115 | if link is not None: 116 | # record recent use of the key by moving it to the front of the list 117 | root, = nonlocal_root 118 | link_prev, link_next, key, result = link 119 | link_prev[NEXT] = link_next 120 | link_next[PREV] = link_prev 121 | last = root[PREV] 122 | last[NEXT] = root[PREV] = link 123 | link[PREV] = last 124 | link[NEXT] = root 125 | stats[HITS] += 1 126 | return result 127 | result = user_function(*args, **kwds) 128 | with lock: 129 | root, = nonlocal_root 130 | if key in cache: 131 | # getting here means that this same key was added to the 132 | # cache while the lock was released. since the link 133 | # update is already done, we need only return the 134 | # computed result and update the count of misses. 135 | pass 136 | elif _len(cache) >= maxsize: 137 | # use the old root to store the new key and result 138 | oldroot = root 139 | oldroot[KEY] = key 140 | oldroot[RESULT] = result 141 | # empty the oldest link and make it the new root 142 | root = nonlocal_root[0] = oldroot[NEXT] 143 | oldkey = root[KEY] 144 | oldvalue = root[RESULT] 145 | root[KEY] = root[RESULT] = None 146 | # now update the cache dictionary for the new links 147 | del cache[oldkey] 148 | cache[key] = oldroot 149 | else: 150 | # put result in a new link at the front of the list 151 | last = root[PREV] 152 | link = [last, root, key, result] 153 | last[NEXT] = root[PREV] = cache[key] = link 154 | stats[MISSES] += 1 155 | return result 156 | 157 | def cache_info(): 158 | """Report cache statistics""" 159 | with lock: 160 | return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache)) 161 | 162 | def cache_clear(): 163 | """Clear the cache and cache statistics""" 164 | with lock: 165 | cache.clear() 166 | root = nonlocal_root[0] 167 | root[:] = [root, root, None, None] 168 | stats[:] = [0, 0] 169 | 170 | wrapper.__wrapped__ = user_function 171 | wrapper.cache_info = cache_info 172 | wrapper.cache_clear = cache_clear 173 | return update_wrapper(wrapper, user_function) 174 | 175 | return decorating_function 176 | 177 | 178 | -------------------------------------------------------------------------------- /dict/MultiDict2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | # from werkzeug 4 | 5 | from copy import deepcopy 6 | 7 | 8 | class TypeConversionDict(dict): 9 | def get(self, key, default=None, type=None): 10 | try: 11 | rv = self[key] 12 | if type is not None: 13 | rv = type(rv) 14 | except (KeyError, ValueError): 15 | rv = default 16 | 17 | return rv 18 | 19 | 20 | def iter_multi_items(mapping): 21 | """Iterates over the items of a mapping yielding keys and values 22 | without dropping any from more complex structures. 23 | """ 24 | if isinstance(mapping, MultiDict): 25 | for item in iteritems(mapping, multi=True): 26 | yield item 27 | elif isinstance(mapping, dict): 28 | for key, value in iteritems(mapping): 29 | if isinstance(value, (tuple, list)): 30 | for value in value: 31 | yield key, value 32 | else: 33 | yield key, value 34 | else: 35 | for item in mapping: 36 | yield item 37 | 38 | 39 | iterkeys = lambda d, *args, **kwargs: d.iterkeys(*args, **kwargs) 40 | itervalues = lambda d, *args, **kwargs: d.itervalues(*args, **kwargs) 41 | iteritems = lambda d, *args, **kwargs: d.iteritems(*args, **kwargs) 42 | iterlists = lambda d, *args, **kwargs: d.iterlists(*args, **kwargs) 43 | 44 | 45 | class MultiDict(TypeConversionDict): 46 | """A :class:`MultiDict` is a dictionary subclass customized to deal with 47 | multiple values for the same key which is for example used by the parsing 48 | functions in the wrappers. This is necessary because some HTML form 49 | elements pass multiple values for the same key. 50 | 51 | :class:`MultiDict` implements all standard dictionary methods. 52 | Internally, it saves all values for a key as a list, but the standard dict 53 | access methods will only return the first value for a key. If you want to 54 | gain access to the other values, too, you have to use the `list` methods as 55 | explained below. 56 | 57 | Basic Usage: 58 | 59 | >>> d = MultiDict([('a', 'b'), ('a', 'c')]) 60 | >>> d 61 | MultiDict([('a', 'b'), ('a', 'c')]) 62 | >>> d['a'] 63 | 'b' 64 | >>> d.getlist('a') 65 | ['b', 'c'] 66 | >>> 'a' in d 67 | True 68 | 69 | It behaves like a normal dict thus all dict functions will only return the 70 | first value when multiple values for one key are found. 71 | 72 | From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a 73 | subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will 74 | render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP 75 | exceptions. 76 | 77 | A :class:`MultiDict` can be constructed from an iterable of 78 | ``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2 79 | onwards some keyword parameters. 80 | 81 | :param mapping: the initial value for the :class:`MultiDict`. Either a 82 | regular dict, an iterable of ``(key, value)`` tuples 83 | or `None`. 84 | """ 85 | 86 | def __init__(self, mapping=None): 87 | if isinstance(mapping, MultiDict): 88 | dict.__init__(self, ((k, l[:]) for k, l in iterlists(mapping))) 89 | elif isinstance(mapping, dict): 90 | tmp = {} 91 | for key, value in iteritems(mapping): 92 | if isinstance(value, (tuple, list)): 93 | value = list(value) 94 | else: 95 | value = [value] 96 | tmp[key] = value 97 | dict.__init__(self, tmp) 98 | else: 99 | tmp = {} 100 | for key, value in mapping or (): 101 | tmp.setdefault(key, []).append(value) 102 | dict.__init__(self, tmp) 103 | 104 | def __getstate__(self): 105 | return dict(self.lists()) 106 | 107 | def __setstate__(self, value): 108 | dict.clear(self) 109 | dict.update(self, value) 110 | 111 | def __getitem__(self, key): 112 | """Return the first data value for this key; 113 | raises KeyError if not found. 114 | 115 | :param key: The key to be looked up. 116 | :raise KeyError: if the key does not exist. 117 | """ 118 | if key in self: 119 | return dict.__getitem__(self, key)[0] 120 | raise KeyError(key) 121 | 122 | def __setitem__(self, key, value): 123 | """Like :meth:`add` but removes an existing key first. 124 | 125 | :param key: the key for the value. 126 | :param value: the value to set. 127 | """ 128 | dict.__setitem__(self, key, [value]) 129 | 130 | def add(self, key, value): 131 | """Adds a new value for the key. 132 | 133 | .. versionadded:: 0.6 134 | 135 | :param key: the key for the value. 136 | :param value: the value to add. 137 | """ 138 | dict.setdefault(self, key, []).append(value) 139 | 140 | def getlist(self, key, type=None): 141 | """Return the list of items for a given key. If that key is not in the 142 | `MultiDict`, the return value will be an empty list. Just as `get` 143 | `getlist` accepts a `type` parameter. All items will be converted 144 | with the callable defined there. 145 | 146 | :param key: The key to be looked up. 147 | :param type: A callable that is used to cast the value in the 148 | :class:`MultiDict`. If a :exc:`ValueError` is raised 149 | by this callable the value will be removed from the list. 150 | :return: a :class:`list` of all the values for the key. 151 | """ 152 | try: 153 | rv = dict.__getitem__(self, key) 154 | except KeyError: 155 | return [] 156 | if type is None: 157 | return list(rv) 158 | result = [] 159 | for item in rv: 160 | try: 161 | result.append(type(item)) 162 | except ValueError: 163 | pass 164 | return result 165 | 166 | def setlist(self, key, new_list): 167 | """Remove the old values for a key and add new ones. Note that the list 168 | you pass the values in will be shallow-copied before it is inserted in 169 | the dictionary. 170 | 171 | >>> d = MultiDict() 172 | >>> d.setlist('foo', ['1', '2']) 173 | >>> d['foo'] 174 | '1' 175 | >>> d.getlist('foo') 176 | ['1', '2'] 177 | 178 | :param key: The key for which the values are set. 179 | :param new_list: An iterable with the new values for the key. Old values 180 | are removed first. 181 | """ 182 | dict.__setitem__(self, key, list(new_list)) 183 | 184 | def setdefault(self, key, default=None): 185 | """Returns the value for the key if it is in the dict, otherwise it 186 | returns `default` and sets that value for `key`. 187 | 188 | :param key: The key to be looked up. 189 | :param default: The default value to be returned if the key is not 190 | in the dict. If not further specified it's `None`. 191 | """ 192 | if key not in self: 193 | self[key] = default 194 | else: 195 | default = self[key] 196 | return default 197 | 198 | def setlistdefault(self, key, default_list=None): 199 | """Like `setdefault` but sets multiple values. The list returned 200 | is not a copy, but the list that is actually used internally. This 201 | means that you can put new values into the dict by appending items 202 | to the list: 203 | 204 | >>> d = MultiDict({"foo": 1}) 205 | >>> d.setlistdefault("foo").extend([2, 3]) 206 | >>> d.getlist("foo") 207 | [1, 2, 3] 208 | 209 | :param key: The key to be looked up. 210 | :param default: An iterable of default values. It is either copied 211 | (in case it was a list) or converted into a list 212 | before returned. 213 | :return: a :class:`list` 214 | """ 215 | if key not in self: 216 | default_list = list(default_list or ()) 217 | dict.__setitem__(self, key, default_list) 218 | else: 219 | default_list = dict.__getitem__(self, key) 220 | return default_list 221 | 222 | def items(self, multi=False): 223 | """Return an iterator of ``(key, value)`` pairs. 224 | 225 | :param multi: If set to `True` the iterator returned will have a pair 226 | for each value of each key. Otherwise it will only 227 | contain pairs for the first value of each key. 228 | """ 229 | 230 | for key, values in iteritems(dict, self): 231 | if multi: 232 | for value in values: 233 | yield key, value 234 | else: 235 | yield key, values[0] 236 | 237 | def lists(self): 238 | """Return a list of ``(key, values)`` pairs, where values is the list 239 | of all values associated with the key.""" 240 | 241 | for key, values in iteritems(dict, self): 242 | yield key, list(values) 243 | 244 | def keys(self): 245 | return iterkeys(dict, self) 246 | 247 | __iter__ = keys 248 | 249 | def values(self): 250 | """Returns an iterator of the first value on every key's value list.""" 251 | for values in itervalues(dict, self): 252 | yield values[0] 253 | 254 | def listvalues(self): 255 | """Return an iterator of all values associated with a key. Zipping 256 | :meth:`keys` and this is the same as calling :meth:`lists`: 257 | 258 | >>> d = MultiDict({"foo": [1, 2, 3]}) 259 | >>> zip(d.keys(), d.listvalues()) == d.lists() 260 | True 261 | """ 262 | 263 | return itervalues(dict, self) 264 | 265 | def copy(self): 266 | """Return a shallow copy of this object.""" 267 | return self.__class__(self) 268 | 269 | def deepcopy(self, memo=None): 270 | """Return a deep copy of this object.""" 271 | return self.__class__(deepcopy(self.to_dict(flat=False), memo)) 272 | 273 | def to_dict(self, flat=True): 274 | """Return the contents as regular dict. If `flat` is `True` the 275 | returned dict will only have the first item present, if `flat` is 276 | `False` all values will be returned as lists. 277 | 278 | :param flat: If set to `False` the dict returned will have lists 279 | with all the values in it. Otherwise it will only 280 | contain the first value for each key. 281 | :return: a :class:`dict` 282 | """ 283 | if flat: 284 | return dict(iteritems(self)) 285 | return dict(self.lists()) 286 | 287 | def update(self, other_dict): 288 | """update() extends rather than replaces existing key lists.""" 289 | for key, value in iter_multi_items(other_dict): 290 | MultiDict.add(self, key, value) 291 | 292 | def pop(self, key, default=None): 293 | """Pop the first item for a list on the dict. Afterwards the 294 | key is removed from the dict, so additional values are discarded: 295 | 296 | >>> d = MultiDict({"foo": [1, 2, 3]}) 297 | >>> d.pop("foo") 298 | 1 299 | >>> "foo" in d 300 | False 301 | 302 | :param key: the key to pop. 303 | :param default: if provided the value to return if the key was 304 | not in the dictionary. 305 | """ 306 | try: 307 | return dict.pop(self, key)[0] 308 | except KeyError as e: 309 | if default is not None: 310 | return default 311 | raise KeyError(str(e)) 312 | 313 | def popitem(self): 314 | """Pop an item from the dict.""" 315 | try: 316 | item = dict.popitem(self) 317 | return (item[0], item[1][0]) 318 | except KeyError as e: 319 | raise KeyError(str(e)) 320 | 321 | def poplist(self, key): 322 | """Pop the list for a key from the dict. If the key is not in the dict 323 | an empty list is returned. 324 | 325 | .. versionchanged:: 0.5 326 | If the key does no longer exist a list is returned instead of 327 | raising an error. 328 | """ 329 | return dict.pop(self, key, []) 330 | 331 | def popitemlist(self): 332 | """Pop a ``(key, list)`` tuple from the dict.""" 333 | try: 334 | return dict.popitem(self) 335 | except KeyError as e: 336 | raise KeyError(str(e)) 337 | 338 | def __copy__(self): 339 | return self.copy() 340 | 341 | def __deepcopy__(self, memo): 342 | return self.deepcopy(memo=memo) 343 | 344 | def __repr__(self): 345 | return '%s(%r)' % (self.__class__.__name__, list(iteritems(self, multi=True))) 346 | 347 | if __name__ == '__main__': 348 | d = MultiDict() 349 | 350 | d.add('foo', 1) 351 | d.add('foo', 2) 352 | 353 | d.add('bar', 3) 354 | 355 | print d.get('foo') # default last one 356 | print d.getlist('foo') 357 | 358 | print list(d.items()) 359 | print list(d.lists()) 360 | --------------------------------------------------------------------------------