我正在编写一个自定义容器类。组成对象独立于容器创建,并且可以是无容器或多个容器的成员。容器的公共API应支持三种操作:
容器会做一些额外的工作,其精确的实现可能会改变。
如何在此类中编写公共API,以便在更改实现时保持稳定?
如果容器是list
- 就像有效的删除需要知道对象的索引;知道对象本身并不好(我不想在整个容器中搜索元素)。
如果容器是set
- 就像,没有任何东西等同于索引,我需要对象本身。
如果容器就像一个单链表,我需要对被删除对象前面的对象进行某种引用。
如果容器就像一个双向链表,我需要一个对象本身的引用。
我正在考虑让remove方法采用单个参数reference
,它在删除方法之外没有任何意义或用途。迭代将产生一对(object
,reference
)。
这个设计有问题吗?我可以查看一个示例或设计模式吗?
理想情况下,我宁愿让迭代产生一个包含原始object
和reference
的复杂对象,并展示两者的接口。但我不认为这是可行的吗?
答案 0 :(得分:0)
大多数容器类型都有一个方向,它们可以很好地工作 - 从索引到索引,从当前到下一个等等。有些是双向的,但远非所有。
尝试在不使用索引的情况下在python列表中查找值几乎就是O(n)。您需要拥抱O(n),或使用其他类型。
有一点需要注意的是,如果你需要从很多容器类型中快速删除某些东西,你可以在你的值中添加一个“ignore_this”属性。如果将其设置为true,那么所有容器类型都会开始忽略它,甚至在看到它时将其删除。
答案 1 :(得分:0)
只需封装一个list
和一个dict
/一个list
和一个set
,...
大致将内存使用和操作时间加倍,但巧妙的封装通常会使所有与问题相关的操作O(1)
几乎完成。
答案 2 :(得分:0)
如果您使用的是Python 2.7及以上版本,可能值得查看collections.OrderedDict
:http://docs.python.org/library/collections.html#collections.OrderedDict
答案 3 :(得分:0)
除非其他人通过寻找更好的解决方案来帮助我,否则我会做的事情:
# to get __hash__ and __eq__ return id(self)
class Reference:
def __init__(self, item):
self.item = item
class RemovalAPI:
def add_removal_info(self, item, removal_info):
try:
references = item.__reference
except AttributeError:
references = item.__reference = {}
references[Reference(self)] = removal_info
def get_removal_info(self, item):
try:
references = item.__reference
self_reference = Reference(self)
return references[self_reference]
class Container(list, RemovalAPI):
def __iter__(self):
for i in range(len(self)):
item = self[i]
self.add_removal_info(item, i)
yield item
def remove(self, item):
removal_info = self.get_removal_info(item)
del self[removal_info]
def insert(self, item):
self.add_removal_info(item, len(self))
self.append(item)
# do whatever post-processing I need
# ...
如果我决定将实现从list
更改为其他数据结构,则公共接口可以保持不变:
class Container(orderedset, RemovalAPI):
# inheriting __iter__, remove from parent
def insert(self, item):
self.add(item)
# do whatever post-processing I need
# ...
或者...
class Container(linkedlist, RemovalAPI):
def __iter__(self):
it = super().__iter__()
last_item = None
for item in it:
self.add_removal_info(item, last_item)
yield item
def remove(self, item):
removal_info = self.get_removal_info(item)
if removal_info is None:
self.remove_first()
else:
self.remove_after(removal_info)
def insert(self, item):
self.add_removal_info(item, None)
self.add_to_front(item)
# do whatever post-processing I need
# ...