2012-03-26 21 views
9

Tóm lại: cách nhịn ăn để kiểm tra xem danh sách lớn trong python có thay đổi không? hashlib cần một bộ đệm và xây dựng một chuỗi đại diện của danh sách đó là không khả thi.Kiểm tra xem danh sách lớn trong python đã thay đổi

Lâu: Tôi có danh sách HUGE từ điển đại diện cho dữ liệu. Tôi chạy một số phân tích về dữ liệu này, nhưng có một vài khía cạnh siêu dữ liệu được yêu cầu bởi tất cả các phân tích, tức là. tập hợp các chủ đề (mỗi dict trong danh sách có một khóa chủ đề, và đôi khi tôi chỉ cần một danh sách của tất cả các đối tượng có dữ liệu có trong tập dữ liệu.). Vì vậy, tôi muốn thực hiện những điều sau:

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 

Câu hỏi đặt ra là làm thế nào để thực hiện các phương pháp has_changed, hay cụ thể hơn, get_hash (mỗi đối tượng đã có một phương pháp __hash__, nhưng theo mặc định nó chỉ trả về đối tượng của id , không thay đổi khi chúng ta ví dụ thêm một phần tử vào danh sách).

+1

Phương thức 'change_data' của bạn trông như thế nào? Cũng 'self.subjects' có thể được xây dựng như' self.subjects = set (datum ['subject'] cho datum trong self.data) '. – eumiro

+0

Tôi nghĩ bạn có thể cần cung cấp thêm một số chi tiết. Bạn có cả phiên bản cũ và mới không? Bạn có thể sử dụng frozendicts? Liệu trật tự có quan trọng không? Mã của bạn có tạo thay đổi không? – Marcin

+5

Bạn có thể chỉ có biến thể 'has_changed' mà bạn đặt bất cứ khi nào bạn thay đổi' dữ liệu'? Nếu không, bạn có thể cần một đối tượng proxy để ủy nhiệm mọi thứ nhưng 'has_changed' thành' dữ liệu' thực. – agf

Trả lời

7

Một cách tiếp cận phức tạp hơn sẽ làm việc với các phần tử dữ liệu proxy thay vì danh sách gốc và từ điển, có thể gắn cờ bất kỳ thay đổi nào với thuộc tính của chúng. Để làm cho nó linh hoạt hơn, bạn thậm chí có thể mã gọi lại để được sử dụng trong trường hợp có bất kỳ thay đổi nào. Vì vậy, giả sử bạn chỉ phải đối phó với danh sách và từ điển trên cấu trúc dữ liệu của bạn - chúng tôi có thể làm việc với các lớp kế thừa từ dict và danh sách với một cuộc gọi lại khi bất kỳ phương pháp thay đổi dữ liệu nào trên đối tượng được truy cập Danh sách đầy đủ các phương thức trong 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 cập nhật

người ta sống và học tập: danh sách bản địa có thể được thay đổi bằng các phương pháp tự nhiên bởi các cuộc gọi mà bỏ qua những phương pháp kỳ diệu. Hệ thống chứng minh lừa là cách tiếp cận tương tự, nhưng kế thừa từ collections.abc.MutableSequence thay vào đó, hãy giữ một danh sách gốc làm thuộc tính nội bộ của đối tượng proxy của bạn.

+1

Tôi chân thành thứ hai cách tiếp cận này. Nếu phát hiện thay đổi sau khi thực tế là quá đắt (mà mô tả của bạn chỉ ra rằng chắc chắn là trường hợp), chỉ cần theo dõi những thay đổi khi chúng xảy ra. Lý do để sử dụng một dấu vân tay băm như Nkosinathi ban đầu cố gắng là nếu bạn cần phải giữ nhiều phiên bản được lưu trữ và cần một cách nhận dạng duy nhất chúng. Nếu tất cả những gì bạn đang làm là phát hiện những thay đổi, cách tiếp cận này phù hợp hơn nhiều. –

2

Bạn có thể dễ dàng có được chuỗi đại diện của bất kỳ đối tượng sử dụng thư viện dưa, và sau đó vượt qua nó để hashlib, như bạn nói:

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

Vì vậy, đó là một cách, tôi không biết nếu đó là cách nhanh nhất. Nó sẽ làm việc cho các đối tượng tùy ý. Nhưng, như agf nói, trong trường hợp của bạn, nó chắc chắn sẽ hiệu quả hơn nếu bạn có thể sử dụng biến số has_changed mà bạn sửa đổi mỗi khi bạn thực sự sửa đổi dữ liệu.

+0

Điều đó thực sự hiệu quả nhưng khá chậm. Vấn đề là tôi không bao giờ biết liệu một hoạt động sẽ thay đổi danh sách (cộng với nhiều phân tích mà tôi sử dụng đã được viết bởi những người khác, và việc thay đổi tất cả chúng không khả thi). Hàm này thậm chí không cần phải hoàn toàn chính xác, một 'has_probably_changed' sẽ nhanh chóng thực hiện. –

1

hashlib cần bộ đệm và xây dựng chuỗi đại diện cho rằng danh sách không khả thi.

Bạn có thể update băm trong nhiều bước sau:

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

Vì vậy, bạn không cần phải chuyển đổi tất cả các danh sách để một chuỗi đại diện. Bạn chỉ cần lặp lại nó, chuyển đổi thành chuỗi chỉ một mục và gọi số update.

Các vấn đề liên quan