├── .gitignore ├── MANIFEST.in ├── test.py ├── setup.py ├── README.md ├── LICENSE.txt └── expiredict └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | .pyc 3 | *.pyc 4 | *.swp 5 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md test.py 2 | recursive-include README.md test.py 3 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | import time 3 | 4 | from expiredict import ExpireDict 5 | d = ExpireDict(10) 6 | d['a'] = 'aaa' 7 | d['b'] = 'bbb' 8 | print d.pop('b',123) 9 | print d.clear() 10 | print "clear data :%s"%d.keys() 11 | d['a'] = 'abc' 12 | print "dict length :%s"%len(d) 13 | print "d.keys() : %s"%d.keys() 14 | d.set_ttl('a',2) 15 | print d.ttl('a') 16 | print "return value : %s"%d['a'] 17 | time.sleep(3) 18 | print d['a'] 19 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup, Command 4 | import os 5 | import os.path 6 | 7 | setup( 8 | name='expiredict', 9 | version='3.2', 10 | description='Support Expire and Max length Dict', 11 | url='http://xiaorui.cc', 12 | author='ruifengyun', 13 | author_email='rfyiamcool@163.com', 14 | long_description=open('README.md').read(), 15 | packages=['expiredict'], 16 | license = "MIT", 17 | keywords = "Expire ttl Dict Max Dict author fengyun", 18 | classifiers = [ 19 | 'Development Status :: 4 - Beta', 20 | 'Topic :: Utilities', 21 | 'License :: OSI Approved :: MIT License' 22 | ] 23 | ) 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # expiredict 2 | 3 | ### 介绍 4 | 5 | python的一个小模块,支持时间过期及限制key个数的dict,涵盖所有常用的dict方法。 6 | 7 | [expiredict 更多介绍](http://xiaorui.cc "xiaorui.cc") 8 | 9 | New Future 10 | 11 | 1. support len 12 | 13 | 2. support clear 14 | 15 | To Do List 16 | 17 | 1. logging 18 | 19 | ..... 20 | 21 | ### 安装 22 | 1. 源码安装 23 | 24 | ``` 25 | git@github.com:rfyiamcool/expiredict.git 26 | python setup.py install 27 | ``` 28 | 2. pip 安装 29 | 30 | ``` 31 | pip install expiredict 32 | ``` 33 | 34 | ### 使用方法 35 | 36 | ``` 37 | import time 38 | 39 | from expiredict import ExpireDict 40 | d = ExpireDict(10) 41 | d['a'] = 'abc' 42 | print d.clear() 43 | print "clear data :%s"%d.keys() 44 | d['a'] = 'abc' 45 | print "dict length :%s"%len(d) 46 | print "d.keys() : %s"%d.keys() 47 | d.set_ttl('a',2) 48 | print d.ttl('a') 49 | print "return value : %s"%d['a'] 50 | time.sleep(3) 51 | print d['a'] 52 | ``` 53 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 fengyun rui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /expiredict/__init__.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | ''' 3 | >>> d = ExpireDict(100) 4 | 5 | map1: OrderedDict() 6 | The values stored in the following way: 7 | { 8 | key1: value1, 9 | } 10 | map2 : key_time_map 11 | { 12 | key1: {"expire_time":created_time1}, 13 | } 14 | 15 | ''' 16 | 17 | import time 18 | from threading import RLock 19 | 20 | try: 21 | from collections import OrderedDict 22 | except ImportError: 23 | # Python < 2.7 24 | from ordereddict import OrderedDict 25 | 26 | 27 | class ExpireDict(OrderedDict): 28 | def __init__(self, max_len=0): 29 | assert max_len >= 1 30 | 31 | OrderedDict.__init__(self) 32 | self.max_len = max_len 33 | self.key_time_map = {} 34 | self.lock = RLock() 35 | 36 | def __contains__(self, key): 37 | """ Return True if the dict has a key, else return False. """ 38 | try: 39 | with self.lock: 40 | item = OrderedDict.__getitem__(self, key) 41 | expire_time = self.key_time_map[key].get('expire_time',None) 42 | if expire_time and expire_time >0: 43 | return True 44 | else: 45 | del self[key] 46 | except KeyError: 47 | pass 48 | return False 49 | 50 | def __getitem__(self, key, with_age=False): 51 | """ Return the item of the dict. 52 | 53 | Raises a KeyError if key is not in the map. 54 | """ 55 | with self.lock: 56 | try: 57 | item = OrderedDict.__getitem__(self, key) 58 | except KeyError: 59 | raise KeyError(key) 60 | 61 | if not self.key_time_map.get(key,None): 62 | return item 63 | item_age = (self.key_time_map[key].get('expire_time',0)) - time.time() 64 | if item_age > 0: 65 | if with_age: 66 | return item, item_age 67 | else: 68 | return item 69 | else: 70 | del self[key] 71 | raise KeyError(key) 72 | 73 | def __setitem__(self, key, value): 74 | """ Set d[key] to value. """ 75 | with self.lock: 76 | if len(self) == self.max_len: 77 | self.popitem(last=False) 78 | OrderedDict.__setitem__(self, key,value) 79 | self.key_time_map[key] = {} 80 | 81 | def __llen__(self, key): 82 | return len(OrderedDict.__setitem__(self, key,value)) 83 | 84 | def pop(self, key, default=None): 85 | """ Get item from the dict and remove it. 86 | 87 | Return default if expired or does not exist. Never raise KeyError. 88 | """ 89 | with self.lock: 90 | try: 91 | item = OrderedDict.__getitem__(self, key) 92 | expire_time = self.key_time_map.get(key,{}).get('expire_time',None) 93 | if not expire_time: 94 | del self[key] 95 | del self.key_time_map[key] 96 | return item 97 | 98 | if expire_time - time.time() >0: 99 | del self[key] 100 | return item 101 | return default 102 | 103 | except KeyError: 104 | return default 105 | 106 | def clear(self): 107 | """ 108 | clear all keys and keys in key_time_map 109 | """ 110 | with self.lock: 111 | try: 112 | OrderedDict.clear(self) 113 | self.key_time_map.clear() 114 | return True 115 | except KeyError: 116 | return False 117 | 118 | def ttl(self, key): 119 | """ Return TTL of the `key` (in seconds). 120 | 121 | Returns None for non-existent or expired keys. 122 | """ 123 | expire_time = self.key_time_map.get(key,{}).get('expire_time',None) 124 | if expire_time: 125 | key_ttl = expire_time - time.time() 126 | if key_ttl > 0: 127 | return key_ttl 128 | return None 129 | 130 | def set_ttl(self, key, seconds): 131 | is_have = OrderedDict.__getitem__(self,key) 132 | if is_have: 133 | expire_time = time.time() + seconds 134 | self.key_time_map[key] = {"time":time.time(),"max_age":0,"expire_time":expire_time} 135 | key_ttl = expire_time - time.time() 136 | #self.__setitem__(key,is_have) 137 | #OrderedDict.__setitem__(self, key,is_have) 138 | if key_ttl > 0: 139 | return key_ttl 140 | return None 141 | 142 | def get(self, key, default=None, with_age=False): 143 | " Return the value for key if key is in the dictionary, else default. " 144 | try: 145 | return self.__getitem__(key, with_age) 146 | except KeyError: 147 | if with_age: 148 | return default, None 149 | else: 150 | return default 151 | 152 | def items(self): 153 | """ Return a copy of the dictionary's list of (key, value) pairs. """ 154 | r = [] 155 | for key in self: 156 | try: 157 | r.append((key, self[key])) 158 | except KeyError: 159 | pass 160 | return r 161 | 162 | def values(self): 163 | """ Return a copy of the dictionary's list of values. 164 | See the note for dict.items(). """ 165 | r = [] 166 | for key in self: 167 | try: 168 | r.append(self[key]) 169 | except KeyError: 170 | pass 171 | return r 172 | 173 | def keys(self): 174 | return OrderedDict.keys(self) 175 | 176 | def fromkeys(self): 177 | return OrderedDict.keys(self) 178 | 179 | def iteritems(self): 180 | """ support iteritems""" 181 | return OrderedDict.keys(self) 182 | 183 | def itervalues(self): 184 | return OrderedDict.keys(self) 185 | 186 | def viewitems(self): 187 | raise NotImplementedError() 188 | 189 | def viewkeys(self): 190 | raise NotImplementedError() 191 | 192 | def viewvalues(self): 193 | """ Return a new view of the dictionary's values. """ 194 | raise NotImplementedError() 195 | --------------------------------------------------------------------------------