Python字典如何记忆已被访问的键?

9
我希望创建一个数据结构,它的行为类似于字典,但有一种额外的功能,即跟踪哪些键已经被“使用”。请注意,我不能只是弹出正在被重用的值。
该结构应支持以下三种情况,即在访问时将键标记为已使用:
if key in d:
    ...
d[key]
d.get(key)

这是我写的内容:

这是我所写的:

class DictWithMemory(dict):

    def __init__(self, *args, **kwargs):
        self.memory = set()
        return super(DictWithMemory, self).__init__(*args, **kwargs)

    def __getitem__(self, key):
        self.memory.add(key)
        return super(DictWithMemory, self).__getitem__(key)

    def __contains__(self, key):
        self.memory.add(key)
        return super(DictWithMemory, self).__contains__(key)

    def get(self, key, d=None):
        self.memory.add(key)
        return super(DictWithMemory, self).get(key, d)

    def unused_keys(self):
        """
        Returns the list of unused keys.
        """
        return set(self.keys()).difference(self.memory)

由于我对字典的内部机制不是很熟悉,是否有更好的方法来实现这个结果?


1
你多频繁地使用 unused_keys() 函数?如果你使用修饰器来在 setter 中添加键值到一个集合中,同时在 getter 中移除这个集合中的键值,它可能会有更好的性能表现。但是 优雅 这一部分不太确定。 - Aprillion
2
另外:为什么unused_keys应该返回一个列表?它没有固有的顺序,因此返回一个集合是有意义的。 - Thomas K
3
我必须说,如果我遇到一个词典其可观察状态被in.get()等修改了,那我会感到非常惊讶。 http://en.wikipedia.org/wiki/Principle_of_least_astonishment - NPE
2
keys() 返回一个列表有点像历史的偶然,因为集合是相对较新的添加。在 Python 3 中,d.keys() 返回一个迭代器,而不是一个列表。 - Thomas K
1
@Thomas K:unused_keys现在返回一个集合。 - badzil
显示剩余12条评论
1个回答

4
这里有一个解决方案,使用元类完全抽象了所有内容。我不确定这是否更加优雅,但如果您改变用于存储使用的键的方式,它会提供一定程度的封装:
class KeyRememberer(type):

    def __new__(meta, classname, bases, classDict):
        cls = type.__new__(meta, classname, bases, classDict)

        # Define init that creates the set of remembered keys
        def __init__(self, *args, **kwargs):
            self.memory = set()
            return super(cls, self).__init__(*args, **kwargs)
        cls.__init__ = __init__

        # Decorator that stores a requested key in the cache
        def remember(f):
            def _(self, key, *args, **kwargs):
                self.memory.add(key)
                return f(self, key, *args, **kwargs)
            return _

        # Apply the decorator to each of the default implementations
        for method_name in [  '__getitem__', '__contains__', 'get' ]:
            m = getattr(cls, method_name)
            setattr(cls, method_name, remember(m))

        return cls


class DictWithMemory(dict):

    # A metaclass that ensures the object
    # has a set called 'memory' as an attribute,
    # which is updated on each call to __getitem__,
    # __contains__, or get.
    __metaclass__ = KeyRememberer

    def unused_keys(self):
        """
        Returns the list of unused keys.
        """
        print "Used", self.memory
        return list(set(super(DictWithMemory,
                              self).keys()).difference(self.memory))

1
我喜欢你使用元类来限制,从而允许动态配置哪些方法被视为“消费者”。 - badzil
我同意@badzil的评论,认为也许它应该更进一步,允许其客户定义或覆盖哪些方法被视为消费者 - 我相信这个功能很容易添加。 - martineau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接