frozensets的“Multiton”实现每个值只有一个实例

2024-09-29 23:22:46 发布

您现在位置:Python中文网/ 问答频道 /正文

如何实现frozenset的Multiton设计模式,无论frozenset是如何创建的,都能正常工作?你知道吗

我要寻找的是一个行为类似于frozenset的类,但是它是“完全的内部”的:对于任何两个实例,如果a == b,那么a is b。你知道吗

this question的回答 似乎为传递给构造函数的每个参数都生成一个实例(并且似乎假定这些参数是可哈希的)。但是一个给定的frozenset可以用许多不同的方式构造:构造器可以得到元素顺序不同的元组,或者不可破坏的列表;或者您可以使用一些操作符,比如a.union(b)来创建frozenset,等等

动机自然是——努力保存记忆。我有一个图,其中有许多顶点用循环的frozenset标记。这个图是通过从旧顶点创建新顶点来“生长”的,而新的frozenset是通过从旧顶点添加或删除元素来获得的。你知道吗

非常感谢!你知道吗


Tags: 实例元素列表参数顺序is方式设计模式
2条回答

可以使用__new__方法在frozenset周围创建包装器。我引用the doc

new() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.

其思想是缓存创建的每个包装,并始终为相同的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__()

...

但是对于某些方法,必须处理frozensetFrozenSetWrapper

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) ...")

实习的想法是有道理的,但实施起来可能会很棘手,因为边缘的情况。你知道吗

这里有一个可能的解决方案。你知道吗

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的魔法可以隐藏“引擎盖下”的简单逻辑,但我猜这有一个直接和透明的优势。你知道吗

相关问题 更多 >

    热门问题