您希望如何实现缓存实际上取决于您的 Python 代码将在哪个平台上运行。
您需要一个相当持久的“缓存”,因为地址的位置不会经常更改:-),因此数据库(在键值模式下)似乎是最好的。
所以在很多情况下,我会选择sqlite3
一个优秀的、非常轻量级的 SQL 引擎,它是 Python 标准库的一部分。除非我可能更喜欢我需要运行的 MySQL 实例,否则一个优点可能是这将允许在不同节点上运行的多个应用程序共享“缓存”——SQL 和非 SQL 的其他 DB 将有利于后者,取决于您的限制和偏好。
但是,如果我在 Google App Engine 上运行,那么我将使用它包含的数据存储。除非我有特定的理由要在多个不同的应用程序之间共享“缓存”,在这种情况下,我可能会考虑替代方案,例如 google cloud sql 和 google storage,以及另一个由专用“缓存服务器”GAE 应用程序组成的替代方案我自己的服务 RESTful 结果(可能带有端点?)。再次选择!非常非常依赖于您的约束和偏好(延迟、每秒查询数大小等)。
因此,请澄清您所在的平台,以及您对数据库“缓存”的其他限制和偏好,然后可以轻松显示实现的非常简单的代码。但是在你澄清之前展示六种不同的可能性不会很有成效。
补充:由于评论建议sqlite3
可能是可以接受的,并且代码中最好地显示了一些重要的细节(例如,如何序列化和反序列化 in geopy.location.Location
/from 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的位,我选择pickle
了“最高协议” -1
(cPickle
与 Python 2 或 3 一样好,除非我有特别的理由不这样做:-)。当然,我test.db
为测试中使用的 sqlite 数据库使用了不同的文件名,因此您可以毫不犹豫地清除它来测试一些变化,而用于“生产”代码的默认文件名保持不变(它使用相对的文件名是一个非常可疑的设计选择 - 意思是“在当前目录中” - 但是决定将此类文件放置在何处的适当方法非常依赖于平台,我没有
如果还有其他问题,请询问(也许最好单独提出一个新问题,因为这个答案已经变得如此之大!-)。