您希望如何实现缓存实际上取决于您的 Python 代码将在什么平台上运行。
您需要一个相当持久的“缓存”,因为地址的位置不会经常变化:-),所以数据库(在键值模式下)似乎是最好的。
所以在很多情况下,我会选择sqlite3,这是一个出色的、非常轻量级的 SQL 引擎,它是 Python 标准库的一部分。除非我可能更喜欢一个我无论如何都需要运行的 MySQL 实例,否则一个优点可能是这将允许在不同节点上运行的多个应用程序共享“缓存”——SQL 和非 SQL 的其他 DB 将有利于后者,取决于您的限制和偏好。
但是,例如,如果我在 Google App Engine 上运行,那么我将使用它包含的数据存储。除非我有特定的理由要在多个不同的应用程序之间共享“缓存”,在这种情况下,我可能会考虑替代方案,例如 google cloud sql 和 google storage,以及另一个由专用“缓存服务器”GAE 应用程序组成的替代方案我自己的服务 RESTful 结果(可能带有端点?)。再次,选择非常非常依赖于您的约束和偏好(延迟、每秒查询数大小等)。
所以请说明您所在的平台,以及您对数据库“缓存”的其他限制和偏好,然后可以轻松显示非常简单的实现代码。但是在你澄清之前展示六种不同的可能性不会很有成效。
添加:由于 cmets 建议 sqlite3 可能是可以接受的,并且有一些重要的细节最好在代码中显示(例如,如何将 geopy.location.Location 的实例序列化和反序列化到/从 sqlite3 blob -- 其他底层数据库很可能会出现类似的问题,并且解决方案相似),我认为解决方案示例可能最好在代码中显示。因此,由于“地理缓存”显然最好作为自己的模块实现,我编写了以下简单的geocache.py...:
import geopy
import pickle
import sqlite3
class Cache(object):
def __init__(self, fn='cache.db'):
self.conn = conn = sqlite3.connect(fn)
cur = conn.cursor()
cur.execute('CREATE TABLE IF NOT EXISTS '
'Geo ( '
'address STRING PRIMARY KEY, '
'location BLOB '
')')
conn.commit()
def address_cached(self, address):
cur = self.conn.cursor()
cur.execute('SELECT location FROM Geo WHERE address=?', (address,))
res = cur.fetchone()
if res is None: return False
return pickle.loads(res[0])
def save_to_cache(self, address, location):
cur = self.conn.cursor()
cur.execute('INSERT INTO Geo(address, location) VALUES(?, ?)',
(address, sqlite3.Binary(pickle.dumps(location, -1))))
self.conn.commit()
if __name__ == '__main__':
# run a small test in this case
import pprint
cache = Cache('test.db')
address = '1 Murphy St, Sunnyvale, CA'
location = cache.address_cached(address)
if location:
print('was cached: {}\n{}'.format(location, pprint.pformat(location.raw)))
else:
print('was not cached, looking up and caching now')
g = geopy.geocoders.GoogleV3()
location = g.geocode(address)
print('found as: {}\n{}'.format(location, pprint.pformat(location.raw)))
cache.save_to_cache(address, location)
print('... and now cached.')
我希望这里说明的想法足够清楚 - 每个设计选择都有替代方案,但我尽量保持简单(特别是,我使用了一个简单的 example-cum-mini-test模块直接运行,而不是一套适当的单元测试...)。
关于序列化到/从 blob 的部分,我选择了带有“最高协议”(-1) 协议的 pickle——cPickle 当然在 Python 2 中也一样好(而且速度更快:-) 但是这些天我尝试编写与 Python 2 或 3 一样好的代码,除非我有特定的理由不这样做:-)。当然,我为测试中使用的 sqlite 数据库使用了不同的文件名test.db,因此您可以毫无疑虑地清除它来测试一些变化,而用于“生产”代码的默认文件名保持不变完整的(它是一个相当可疑的设计选择使用相对的文件名 - 意思是“在当前目录中” - 但是决定将此类文件放置在哪里的适当方式非常依赖于平台,而且我不想在这里进入这样的exoterica:-)。
如果还有其他问题,请提出(也许最好单独提出一个新问题,因为这个答案已经变得如此之大!-)。