检查python中的巨大列表是否已更改

时间:2012-03-26 11:21:13

标签: python hash

简而言之:检查python中的巨大列表是否已更改的禁区方法是什么? hashlib需要一个缓冲区,并且构建该列表的字符串表示是不可行的。

总之:我有一个巨大的词典列表代表数据。我对这些数据进行了大量分析,但是所有分析都需要一些元数据方面,即。主题集(列表中的每个词典都有一个主题键,有时我只需要一个列表,其中包含数据集中存在数据的所有主题。)。所以我想实现以下内容:

class Data:
    def __init__(self, ...):
        self.data = [{...}, {...}, ...] # long ass list of dicts
        self.subjects = set()
        self.hash = 0

    def get_subjects(self):
        # recalculate set of subjects only if necessary
        if self.has_changed():
            set(datum['subject'] for datum in self.data)

        return self.subjects

    def has_changed(self):
        # calculate hash of self.data
        hash = self.data.get_hash() # HOW TO DO THIS?
        changed = self.hash == hash
        self.hash = hash # reset last remembered hash
        return changed

问题是如何实现has_changed方法,或者更具体地说,get_hash(每个对象已经有__hash__方法,但默认情况下它只返回对象的{{1}当我们将元素附加到列表时,它不会改变。)

3 个答案:

答案 0 :(得分:7)

更复杂的方法是使用代理数据元素而不是本机列表和字典,这可以标记对其属性的任何更改。为了使其更加灵活,您甚至可以编写回调函数,以便在发生任何更改时使用。

因此,假设您只需处理数据结构上的列表和字典 - 当访问对象上的任何数据更改方法时,我们可以使用从dict继承的类和带回调的列表。方法的完整列表在http://docs.python.org/reference/datamodel.html

# -*- coding: utf-8 -*-
# String for doctests and  example:
"""
            >>> a = NotifierList()
            >>> flag.has_changed
            False
            >>> a.append(NotifierDict())
            >>> flag.has_changed
            True
            >>> flag.clear()
            >>> flag.has_changed
            False
            >>> a[0]["status"]="new"
            >>> flag.has_changed
            True
            >>> 

"""


changer_methods = set("__setitem__ __setslice__ __delitem__ update append extend add insert pop popitem remove setdefault __iadd__".split())


def callback_getter(obj):
    def callback(name):
        obj.has_changed = True
    return callback

def proxy_decorator(func, callback):
    def wrapper(*args, **kw):
        callback(func.__name__)
        return func(*args, **kw)
    wrapper.__name__ = func.__name__
    return wrapper

def proxy_class_factory(cls, obj):
    new_dct = cls.__dict__.copy()
    for key, value in new_dct.items():
        if key in changer_methods:
            new_dct[key] = proxy_decorator(value, callback_getter(obj))
    return type("proxy_"+ cls.__name__, (cls,), new_dct)


class Flag(object):
    def __init__(self):
        self.clear()
    def clear(self):
        self.has_changed = False

flag = Flag()

NotifierList = proxy_class_factory(list, flag)
NotifierDict = proxy_class_factory(dict, flag)

2017年更新

一个人确实生活和学习:本机列表可以通过绕过魔术方法的调用通过本机方法进行更改。傻瓜证明系统是相同的方法,但继承自collections.abc.MutableSequence,并将本机列表保留为代理对象的内部属性。

答案 1 :(得分:2)

您可以使用pickle库轻松获取任何对象的字符串表示形式,然后将其传递给hashlib,如您所说:

import pickle
import hashlib

data = []
for i in xrange(100000):
    data.append({i:i})

print hashlib.md5(pickle.dumps(data))

data[0] = {0:1}
print hashlib.md5(pickle.dumps(data))

所以,这是一种方式,我不知道它是否是最快的方式。它适用于任意对象。但是,正如agf所说,在你的情况下,如果你可以使用每次实际修改数据时修改的变量has_changed,那肯定会更有效率。

答案 2 :(得分:1)

  

hashlib需要一个缓冲区,并构建一个字符串表示形式   列表是不可行的。

您可以通过多个步骤update哈希:

>>> import hashlib
>>> m = hashlib.md5()
>>> m.update("Nobody inspects")
>>> m.update(" the spammish repetition")

因此,您无需将所有列表转换为字符串表示形式。你只需迭代它,只转换为字符串只调用一个项目并调用update