9
votes

Vérifiez si énorme liste en python a changé

En bref: quel est le moyen jeûné pour vérifier si une liste énorme en python a changé? hashlib code> a besoin d'un tampon, et la construction d'une représentation de chaîne de cette liste est infaisable

longue. J'ai une liste énorme de dictionnaires représentant des données. Je lance un certain nombre d'analyses sur ces données, mais il y a quelques aspects méta-données qui sont requises par toutes les analyses, par exemple. le l'ensemble des sujets (chaque dict dans la liste a une clé de sujet, et parfois j'ai juste besoin d'une liste de tous les objets qui ont des données présentes dans l'ensemble de données.). Je voudrais donc mettre en œuvre ce qui suit: p>

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


5 commentaires

Comment votre méthode change_data ressembler? Aussi self.subjects peut être construit comme self.subjects = set (niveau de référence [ 'sujet'] pour référence dans self.data) .


Je pense que vous pourriez avoir besoin de donner plus de détails. Avez-vous des deux versions anciennes et nouvelles? Pouvez-vous utiliser frozendicts? Est-ce que la matière de commande? Votre code crée les changements?


Pouvez-vous avoir juste un has_changed variable d'instance vous définissez à chaque fois que vous changez données ? Dans le cas contraire, vous avez probablement besoin d'un objet proxy de déléguer tout sauf has_changed réel données .


Mis à part la question: rendre votre Hériter de la classe de données de « objet » ou vous obtiendrez des erreurs subtiles et difficiles à identifier lorsqu'ils ont besoin de plus de soutien de mécanismes Python OO (ce qui est exactement votre cas)


Plus précisément - idetifying si un Liste a changé est relativement facile - il change si l'identifiant d'un objet qu'il contient changent. Vous semblez avoir besoin d'être informé de tout changement dans tous les éléments imbriqués mutables sur votre liste, est-ce pas?


3 Réponses :


3
votes

Vous pouvez facilement obtenir la représentation de chaîne d'un objet à l'aide de la bibliothèque de conserves au vinaigre, puis le transmettre à hashlib, comme vous avez dit:

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


1 commentaires

Cela fonctionne bien, mais est assez lent. Le problème est que je ne sais pas toujours si une opération modifiera la liste (plus grand nombre des analyses que j'utilise ont été écrites par d'autres personnes, et changer tous est impossible). La fonction ne doit même pas être tout à fait précis, un rapide has_probably_changed va faire.



2
votes

hashlib a besoin d'un tampon, et la construction d'une chaîne de caractères représentant que la liste est infaisable. p>

Vous pouvez de hachage de mise à jour en plusieurs étapes: p>

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


0 commentaires

8
votes

Une approche plus sophistiquée, il serait de travailler avec des éléments de données proxy au lieu de listes natives et des dictionnaires, ce qui pourrait signaler tout changement à leurs attributs. Pour le rendre plus flexible, vous pouvez même le code d'un rappel à utiliser dans le cas de modifications

Alors, en supposant suffit de traiter des listes et des dictionnaires sur votre structure de données -. Nous pouvons travailler avec des classes héritant de dict et liste avec un rappel automatique lorsque des données procédé de changement de l'objet est accessible la liste complète des méthodes est en http : //docs.python.org/reference/datamodel.html p>

# -*- 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)


1 commentaires

Je chaleureusement deuxième cette approche. Si la détection des changements après le fait est trop cher (que votre description indique qui est certainement le cas), suivre simplement les changements qu'ils se produisent. La raison d'utiliser une empreinte de hachage comme Nkosinathi est à l'origine essayé si vous devez conserver plusieurs versions mises en cache et ont besoin d'une façon de les identifier de manière unique. Si tout ce que vous faites est la détection des changements, cette approche est beaucoup plus approprié.