无论如何创建frozenset
,如何以一种可行的方式为frozenset
实现Multiton设计模式?
我正在寻找的类的行为类似于frozenset
,但是 gaurantees “完全实习”:对于任何两个实例,如果a == b
则{{ 1}}。
this question的答案
似乎为传递给构造函数的每个参数都生成一个实例 (并且似乎还假定它们是可哈希的)。但是给定的a is b
可以通过许多不同的方式来构造:构造函数可能会获得具有不同元素顺序或不可哈希列表的元组;或者您可以使用诸如a.union(b)之类的运算符来创建Frozenset等。
动机自然是在试图节省内存。我有一个图,其中有许多顶点(除其他外)用重复的frozenset
标记。通过从旧的顶点创建新的顶点来“增长”图形,并通过在旧的顶点中添加或删除元素来获得新的frozenset
。
非常感谢!
答案 0 :(得分:0)
这是一种可能的解决方案。
class Uniquifier(object) :
"""
This class accepts an immutable object and returns a "canonical" instance of it, if one exists, or keeps it in store as that canonical instance. This is used as a kind of clunky multiton implementation.
Of course instances are not destroyed until this object is destroyed.
"""
def __init__(self):
self._universe = {}
def uniquify(self, item):
try :
return self._universe[item]
except KeyError :
self._universe[item] = item
return self._universe[item]
运行此:
a = frozenset([3,5])
b = frozenset([5,3])
c = frozenset([3]).union([5])
print a==b, b==c
print a is b, b is c, a is c
导致:
True True
False False False
但这是
universe = Uniquifier()
a = universe.uniquify(frozenset([3,5]))
b = universe.uniquify(frozenset([5,3]))
c = universe.uniquify(frozenset([3]).union([5]))
print a == b, b==c
print a is b, b is c, a is c
给予
True True
True True True
根据需要。
我希望使用一些Python魔术可以将Uniquifier逻辑隐藏在“幕后”,但是我想这具有简单明了的优势。
答案 1 :(得分:0)
您可以使用__new__
方法在frozenset
周围创建包装器。我引用the doc:
新()主要用于允许不可变类型(例如int,str或tuple)的子类自定义实例创建。在自定义元类中也通常会覆盖它,以自定义类的创建。
这个想法是缓存每个创建的包装,并为相同的frozenset
返回始终相同的实例。
有一个小技巧:frozenset
本身就是frozenset
的元素,也应该包装它们。
class FrozenSetWrapper:
_cache = {}
def __new__(cls, iterable=[]):
fs = frozenset(FrozenSetWrapper(e) if isinstance(e, frozenset) else e
for e in iterable) # wrap recursively
fsw = FrozenSetWrapper._cache.get(fs)
if fsw is None: # was not in cache
fsw = super(FrozenSetWrapper, cls).__new__(cls) # create an object
fsw._fs = fs # init
fsw.__doc__ = fs.__doc__
FrozenSetWrapper._cache[fs] = fsw # cache
return fsw # and return
示例:
f1 = FrozenSetWrapper([1,2,3])
f2 = FrozenSetWrapper([1,2,3])
print(f1, f2)
# <__main__.FrozenSetWrapper object at 0x7f7894f2fa90> <__main__.FrozenSetWrapper object at 0x7f7894f2fa90>
现在,我们必须重新实现frozenset
的方法才能获得完美的匹配。对于其中一些人来说这很容易:只需将工作委托给包装好的frozenset
:
def __repr__(self):
return self._fs.__repr__()
def __iter__(self):
return self._fs.__iter__()
...
但是对于某些方法,您必须处理frozenset
和FrozenSetWrapper
:
def __contains__(self, e):
elif isinstance(e, frozenset):
e = FrozenSetWrapper(e)
return self._fs.contains(e)
def __eq__(self, other):
if isinstance(other, FrozenSetWrapper):
return self is other
elif isinstance(other, frozenset)
return self._fs == other
else:
return False
...
或返回类型:
def __and__(self, other):
if isinstance(other, FrozenSetWrapper):
return FrozenSetWrapper(self._fs.__and__(other._fs))
elif isinstance(other, frozenset):
return FrozenSetWrapper(self._fs.__and__(other))
else:
raise TypeError("unsupported operand type(s) ...")
实习的想法很有意义,但由于情况极端,实施起来可能很棘手。