如何处理从 Web API 构建的相互引用的 python 对象?
How to handle python objects built from a web API that have references to each other?
我正在为 Web API 构建一个客户端库,它公开了一些像这样的对象:
# objs/foo/obj_id_1
{id: "foo_id_1"
name: "your momma"
bar: "bar_id_2"}
# objs/bar/obj_id_1
{id: "bar_id_2"
name: "puh-lease"
foo: "foo_id_1"}
因此,foo
类型的对象引用了 bar
类型的对象,反之亦然。
在我的 python 客户端库中,我根据 API 数据构建 python 对象。 Foo
实例将具有包含 Bar
实例的 bar
属性,而不仅仅是 bar
的 id,而 Bar
实例具有对 Foo
foo
属性中的实例。
我的 python 类 有 save
和 refresh
方法 POST 或从网络获取 API,他们这样做对于子对象。例如,如果我调用 a_foo.refresh()
,这也会自动调用 a_foo.bar.refresh()
。
以上是一个非常简化的示例,可能有许多不同的 类 都指的是 bar
或 foo
或许多其他类型中的任何一个的相同实例我们得到的对象。
我想我的问题实际上是两个问题:
- 什么是好的设计或策略来确保当我从 api 数据构建一个对象时,它对其他对象的所有引用都指向 相同的 对象如果我已经根据之前的 api 请求构建了该对象?
- 当我调用
save
或 refresh
时,当两个或多个对象相互引用时,防止无限循环的好的设计或策略是什么?
这是一个相当宽泛的问题。
1) 一种方法是使用工厂方法模式。示例:
def get_foo(_id):
get_foo.foos = dict()
def real_get_foo(_id):
# get foo from the API
get_foo.foos[_id] = foo
return foo
if _id in get_foo.foos:
return get_foo.foos[_id]
return real_get_foo(_id)
2) 我不认为进行嵌套保存是个好主意。如果我写 foo.bar.x = 5
后跟 foo.save()
我不希望 bar
被保存。为什么?因为我在 foo
上调用了 save()
,所以我不必担心相关对象的不必要保存。
对于#1,你可以使用一个二级字典来保存获取的对象
objectCache
|- foo
| |- foo_id_1 : <obj>
| |- foo_id_2 : <obj>
| |- ...
|
|- bar
| |- bar_id_1: <obj>
| |- bar_id_2: <obj>
| |- ...
|
|- baz
| |- baz_id_1: <obj>
| |- baz_id_2: <obj>
| |- ...
|
|- ...
你会像这样使用它:
def get_or_make_fetched_object(cls, id):
return object_cache.setdefault(cls, {}).setdefault(id, cls())
my_foo = get_or_make_fetched_object(Foo, 'foo_id_1')
这里的技巧部分是如何摆脱不再引用的对象(例如,如果 foo_id_1 从 bar_id_1 切换到 bar_id_35),以避免内存泄漏,因为 object_cache
字典将无限期保留对对象的引用,除非从缓存中删除。
解决内存使用问题的一种可能方法是使用 gc.get_referrers() 为每个缓存对象获取 refcount
的清理函数。基本上缓存中 refcount
等于 2 的对象可以从缓存中删除(1 个计数来自 gc
,另一个来自缓存)。这不适用于循环引用...
至于#2,您可以为一个对象加上保存日期的时间戳,与当前保存时间戳匹配的对象将被跳过,以避免无限递归。但是,只保存从属对象是有意义的。
我正在为 Web API 构建一个客户端库,它公开了一些像这样的对象:
# objs/foo/obj_id_1
{id: "foo_id_1"
name: "your momma"
bar: "bar_id_2"}
# objs/bar/obj_id_1
{id: "bar_id_2"
name: "puh-lease"
foo: "foo_id_1"}
因此,foo
类型的对象引用了 bar
类型的对象,反之亦然。
在我的 python 客户端库中,我根据 API 数据构建 python 对象。 Foo
实例将具有包含 Bar
实例的 bar
属性,而不仅仅是 bar
的 id,而 Bar
实例具有对 Foo
foo
属性中的实例。
我的 python 类 有 save
和 refresh
方法 POST 或从网络获取 API,他们这样做对于子对象。例如,如果我调用 a_foo.refresh()
,这也会自动调用 a_foo.bar.refresh()
。
以上是一个非常简化的示例,可能有许多不同的 类 都指的是 bar
或 foo
或许多其他类型中的任何一个的相同实例我们得到的对象。
我想我的问题实际上是两个问题:
- 什么是好的设计或策略来确保当我从 api 数据构建一个对象时,它对其他对象的所有引用都指向 相同的 对象如果我已经根据之前的 api 请求构建了该对象?
- 当我调用
save
或refresh
时,当两个或多个对象相互引用时,防止无限循环的好的设计或策略是什么?
这是一个相当宽泛的问题。
1) 一种方法是使用工厂方法模式。示例:
def get_foo(_id):
get_foo.foos = dict()
def real_get_foo(_id):
# get foo from the API
get_foo.foos[_id] = foo
return foo
if _id in get_foo.foos:
return get_foo.foos[_id]
return real_get_foo(_id)
2) 我不认为进行嵌套保存是个好主意。如果我写 foo.bar.x = 5
后跟 foo.save()
我不希望 bar
被保存。为什么?因为我在 foo
上调用了 save()
,所以我不必担心相关对象的不必要保存。
对于#1,你可以使用一个二级字典来保存获取的对象
objectCache
|- foo
| |- foo_id_1 : <obj>
| |- foo_id_2 : <obj>
| |- ...
|
|- bar
| |- bar_id_1: <obj>
| |- bar_id_2: <obj>
| |- ...
|
|- baz
| |- baz_id_1: <obj>
| |- baz_id_2: <obj>
| |- ...
|
|- ...
你会像这样使用它:
def get_or_make_fetched_object(cls, id):
return object_cache.setdefault(cls, {}).setdefault(id, cls())
my_foo = get_or_make_fetched_object(Foo, 'foo_id_1')
这里的技巧部分是如何摆脱不再引用的对象(例如,如果 foo_id_1 从 bar_id_1 切换到 bar_id_35),以避免内存泄漏,因为 object_cache
字典将无限期保留对对象的引用,除非从缓存中删除。
解决内存使用问题的一种可能方法是使用 gc.get_referrers() 为每个缓存对象获取 refcount
的清理函数。基本上缓存中 refcount
等于 2 的对象可以从缓存中删除(1 个计数来自 gc
,另一个来自缓存)。这不适用于循环引用...
至于#2,您可以为一个对象加上保存日期的时间戳,与当前保存时间戳匹配的对象将被跳过,以避免无限递归。但是,只保存从属对象是有意义的。