2012-03-24 1 views
4

Я хотел бы создать класс, который ведет себя как переменная python, но вызывает некоторую функцию обратного вызова при изменении/чтении переменной.создание класса, который ведет себя как любая переменная, но имеет обратный вызов при изменении/чтении

Другими словами, я хотел бы иметь возможность использовать класс следующим образом:

x=myClass(change_callback, read_callback) 

определяет х как экземпляр MyClass. Конструктор (INIT) занимает 2 функции, В параметре: функция будет вызываться каждый раз, х изменяется, и функция будет вызываться каждый раз, х «читать»

следующее утверждение:

x=1 

«сохранит» 1 и вызовет вызов change_callback(1), который мог бы что угодно сделать.

Следующее утверждение:

a=x 

будет получить сохраненное значение и вызвать read_callback(), которые, возможно, изменить сохраненное значение и делать другие вещи.

Я бы хотел, чтобы это работало с любым типом, например. чтобы иметь возможность написать такие вещи, как:

x=[1 2 3], который вызвал бы change_callback([1 2 3])

x.append(4) вызовет change_callback([1 2 3 4]) (и, возможно, призыв к read_callback() раньше)

x={'a':1} вызовет change_callback({'a':1})

print(x) спровоцирует звоните в read_callback() ... и, конечно, возвратите последнее сохраненное значение для печати.

Идея заключается в том, что любой доступ к переменной может быть зарегистрирован или генерировать другие вычисления ... без видимых на вид.

У меня такое ощущение, что это должно быть возможно, но я действительно не вижу, что мой объект должен унаследовать от ... Если мне нужно ограничить меня одним типом, например. список, есть ли способ переопределить все операторы присваивания (включая такие методы, как append() ...) в «один раз», сохраняя исходное поведение (метод базового класса) и добавляя обратный вызов ...

Или есть более подходящие способы (модули ...) для достижения тех же целей ...?

Спасибо заранее,

+0

Одним словом, вы не можете и не должны. – delnan

+0

С другой стороны, запись класса с атрибутом «значение», который вызывает зарегистрированные запреты, которые были изменены/прочитаны, тривиальна, читаема, поддерживается и проста. Вместо «x = 1» вы получите «x.value = 1», и вы получите все, что хотите, самыми простыми способами. – jsbueno

ответ

8

Объекты не знают, когда они привязаны к переменной. Написание x = a добавляет запись диктата (или запись местных жителей), которая указывает на a. Объект a не получает уведомления (хотя в CPython его счетчик ссылок увеличивается).

Часть, которая получает уведомление, представляет собой контейнер, в котором объект назначен. В случае глобального присвоения обновляется словарь модуля. В случае обновлений переменных экземпляра, таких как a.b = x, существует точечный поиск и сохранение в словаре экземпляра.

Вы можете заставить эти контейнеры вызывать произвольный код при поиске или хранении. Python's property предоставляет эту возможность классам нового стиля. Операции exec и eval позволяют указать пользовательский dict, который может обеспечить эту возможность для регулярных назначений. В обоих случаях вы полностью контролируете, что происходит при назначении.

Резюме: Взгляд вверх и магазин поведение управляемо через мишени задания, а не объекта быть назначено.

Пример:

from collections import namedtuple 

CallbackVar = namedtuple('CallbackVar', ['change_callback', 'read_callback']) 

class CallbackDict(dict): 
    'Variant of dict that does callbacks for instances of CallbackVar' 
    def __getitem__(self, key): 
     value = dict.__getitem__(self, key) 
     if isinstance(value, CallbackVar): 
      return value.read_callback(key) 
    def __setitem__(self, key, value): 
     try: 
      realvalue = dict.__getitem__(self, key) 
      if isinstance(realvalue, CallbackVar): 
       return realvalue.change_callback(key, value) 
     except KeyError: 
      pass 
     return dict.__setitem__(self, key, value) 

stmts = ''' 
x = CallbackVar(setter, getter)  # Attach getter() and setter() to "x" 
x = 1        # Invoke the setter() 
x         # Invoke the getter() 
''' 

def getter(key): 
    print 'Getting', key 
    return 42 

def setter(key, value): 
    print 'Setting', key, 'to', value 

exec stmts in globals(), CallbackDict() 
2

Вы не можете сделать это с помощью оператора = - поскольку, по определению, он предназначен, чтобы перезаписать левую, независимо от текущего содержимого, которые не получают никакого уведомления что такое происходит. (И оператор присваивания не переопределен в Python.)

Однако вы можете использовать методы .set() и .get() на вашем классе. Это не принесло бы вам совершенно такой же «магии», но, честно говоря, это, наверное, хорошо - это делает более ясным, что происходит.

1

Переменная - это ссылка на объект python. Неверно говорить, что «класс ведет себя как переменная». когда вы

x = [1, 2, 3] 

переменная x будет ссылка (перенаправлены) в новый список объектов [1, 2, 3], не старый объект был «изменен». Он просто потерял ссылку.

Чтобы делать то, что вы можете пожелать. Попробуйте позвонить change_callback в __init__ метод вашего класса.

Чтобы позвонить read_callback. Сделайте что-нибудь подобное

class A(object): 
    def __init__(self): 
     self._x = 3 
    @property 
    def x(): 
     read_callback() # called! 
     return self._x 
    .... 
a = A() 
print a.x 

Если это то, что вы что.