2012-03-26 12 views
9

संक्षेप में: यह जांचने का सबसे तेज़ तरीका क्या है कि पाइथन में एक बड़ी सूची बदल गई है या नहीं? 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__ विधि है, लेकिन डिफ़ॉल्ट रूप से यह सिर्फ वस्तु की id देता है , जो तब नहीं बदलता जब हम किसी सूची में तत्व जोड़ते हैं)।

+1

आपकी 'change_data' विधि कैसी दिखती है? इसके अलावा 'self.subjects' को self.data में डेटाम के लिए' self.subjects = set (डेटाम ['विषय'] के रूप में बनाया जा सकता है)। – eumiro

+0

मुझे लगता है कि आपको कुछ और विवरण देने की आवश्यकता हो सकती है। क्या आपके पास पुराने और नए संस्करण हैं? क्या आप जमे हुए लोगों का उपयोग कर सकते हैं? क्या मामला मायने रखता है? क्या आपका कोड परिवर्तन कर रहा है? – Marcin

+5

क्या आपके पास अभी भी 'have_changed' इंस्टेंस वैरिएबल हो सकता है जब भी आप 'डेटा' बदलते हैं? अन्यथा, आपको शायद सबकुछ का प्रतिनिधित्व करने के लिए प्रॉक्सी ऑब्जेक्ट की आवश्यकता है, लेकिन वास्तविक 'डेटा' में 'has_changed' है। – agf

उत्तर

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

आप आसानी से, अचार लाइब्रेरी का उपयोग कर किसी भी वस्तु की स्ट्रिंग प्रतिनिधित्व प्राप्त कर सकते हैं, और फिर 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)) 

तो, कि एक तरीका नहीं है, मैं नहीं जानता कि यदि यह सबसे तेज़ तरीका है। यह मनमानी वस्तुओं के लिए काम करेगा। लेकिन, जैसा कि एएफएफ ने कहा, आपके मामले में यह निश्चित रूप से अधिक कुशल होगा यदि आप एक चर has_changed का उपयोग कर सकते हैं जिसे आप वास्तव में डेटा संशोधित करते समय संशोधित करते हैं।

+0

यह वास्तव में काम करता है, लेकिन बहुत धीमी है। समस्या यह है कि मुझे हमेशा यह नहीं पता कि कोई ऑपरेशन सूची में बदलाव करेगा या नहीं, साथ ही मेरे द्वारा उपयोग किए जाने वाले कई विश्लेषण अन्य लोगों द्वारा लिखे गए हैं, और उनमें से सभी को बदलने योग्य नहीं है)। फ़ंक्शन को पूरी तरह सटीक भी नहीं होना चाहिए, एक त्वरित 'has_probably_changed' करेगा। –

1

hashlib एक बफर की जरूरत है, और कहा कि सूची की एक स्ट्रिंग प्रतिनिधित्व के निर्माण अव्यावहारिक है।

आप कई चरणों में update हैश कर सकते हैं:

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

तो, आप एक स्ट्रिंग प्रतिनिधित्व करने के लिए सभी सूची बदलने की आवश्यकता नहीं है। आप बस इसे फिर से चालू करते हैं, केवल एक आइटम स्ट्रिंग में परिवर्तित होते हैं और update पर कॉल करते हैं।

संबंधित मुद्दे