2014-02-02 4 views
3

Мое приложение полагается на list и dict структуры данных для поддержания текущего состояния. Теперь мне нужно отслеживать, когда элемент добавляется/удаляется из списка или изменения данных dict. Я googled и узнал, что есть collections.abc.Sequence (для списка) и collections.abc.MutableMapping (для dict), но они очень ограничены, и результат не может быть использован вместо списка/dict (append, clear, ...). Я думал о каком-то прокси-классе, который переадресует вызовы и предоставляет вызовы, которые будут вызываться до/после того, как какой-либо метод будет перенаправлен, но не нашел ничего, что даже выглядит так.Python 3: список крючков и изменения dict

Так что мой вопрос: как я перехватываю мутаторы данных структур? Что-то я не знаю?

+1

Единственный способ, с помощью которого я пришел, - реализовать подкласс списка/dict самостоятельно, затем переопределить 'append',' clear' и т. Д. – zhangxaochen

ответ

3

подклассов ДИКТ может быть:

class DictWatch(dict): 
    def __init__(self, *args, **kwargs): 
     self.callback = kwargs.pop('callback') 
     dict.__init__(self, args) 

    def __setitem__(self, key, val): 
     # YOUR HOOK HERE 
     self.callback(key, val) 
     dict.__setitem__(self, key, val) 

    # and other overrided dict methods if you need them 

Демо:

>>> def cb(k,v): 
...  print k,v 
... 
>>> d=DictWatch(callback=cb) 
>>> d['key']='100' 
key 100 
+0

Я видел некоторое обсуждение, в котором говорится, что наследование встроенных типов является плохим идея. Или это неправильно? – Daniel

+0

@ Daniel Я думаю, что это нормально, если все, что вам нужно, это переходы ... – ndpu

+0

ОК, тогда я пойду так, спасибо :) – Daniel

4

Несмотря на подклассы встроенные классы официально поддерживается, я бы до сих пор рекомендую вам рассмотреть возможность использования прокси-класса.

Проблема в том, что это не хорошо документировано, когда вызывается переопределенные методы. Например, если вы переопределите метод __setitem__, он не будет вызываться, когда вы измените свой dict, используя метод extend, например. mydict.extend({"a": 42}). Вам также придется переопределить extend. Очень легко забыть переопределить какой-то неясный метод, и модификация будет происходить без использования вызова callback (тогда как если бы вы использовали прокси-сервер, он бы выбрал AttributeError, указав, что вы забыли определить какой-либо метод).

Это еще не все. Некоторые встроенные методы, которые получают словарь через аргумент не будет вызывать никаких переопределены методы вообще в CPython, как показано в следующем example from PyPy wiki:

>>>> class D(dict): 
....  def __getitem__(self, key): 
....   return 42 
.... 
>>>> 
>>>> d1 = {} 
>>>> d2 = D(a='foo') 
>>>> d1.update(d2) 
>>>> print d1['a'] 
foo # but 42 in PyPy 
0

Использование прогул, http://hooky.readthedocs.com

Пример hooky.List:

#!/usr/bin/env python 

from hooky import List 


class MyList(List): 
    def _before_add(self, key, item): 
     print('before add, key: {}, item: {}'.format(key, repr(item))) 

    def _after_add(self, key, item): 
     print(' after add, key: {}, item: {}'.format(key, repr(item))) 

    def _before_del(self, key): 
     print('before_del, key: ', key) 

    def _after_del(self, key): 
     print(' after_del, key: ', key) 


l = MyList(['a', 'b']) 

l.append(1) 

l.extend(['f', 'g', 2]) 

l.pop() 

l[2:3] = ['c', 'd', 'e'] 

print(l) 

l.clear() 

print(l) 

распечатывают:

before add, key: 0, item: 'a' 
after add, key: 0, item: 'a' 
before add, key: 1, item: 'b' 
after add, key: 1, item: 'b' 
before add, key: 2, item: 1 
after add, key: 2, item: 1 
before add, key: 3, item: 'f' 
after add, key: 3, item: 'f' 
before add, key: 4, item: 'g' 
after add, key: 4, item: 'g' 
before add, key: 5, item: 2 
after add, key: 5, item: 2 
before_del, key: -1 
after_del, key: -1 
before_del, key: 2 
after_del, key: 2 
before add, key: 2, item: 'c' 
after add, key: 2, item: 'c' 
before add, key: 3, item: 'd' 
after add, key: 3, item: 'd' 
before add, key: 4, item: 'e' 
after add, key: 4, item: 'e' 
['a', 'b', 'c', 'd', 'e', 'f', 'g'] 
before_del, key: -1 
after_del, key: -1 
before_del, key: -1 
after_del, key: -1 
before_del, key: -1 
after_del, key: -1 
before_del, key: -1 
after_del, key: -1 
before_del, key: -1 
after_del, key: -1 
before_del, key: -1 
after_del, key: -1 
before_del, key: -1 
after_del, key: -1 
[] 
Смежные вопросы