создание класса, который ведет себя как любая переменная, но имеет обратный вызов при изменении / чтении - PullRequest
4 голосов
/ 24 марта 2012

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

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

x=myClass(change_callback, read_callback)

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

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

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 () ...) в один прием, сохраняя исходное поведение (метод базового класса) и добавляя обратный вызов ...

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

Заранее спасибо,

Ответы [ 4 ]

8 голосов
/ 24 марта 2012

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

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

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

Сводка : Поведение поиска и сохранения управляется через target назначения, а не объект назначается.

Пример :

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()
4 голосов
/ 24 марта 2012

Вы можете посмотреть на дескрипторы: http://docs.python.org/howto/descriptor.html

Это будет работать только для свойств объекта, но этого, вероятно, достаточно.

2 голосов
/ 24 марта 2012

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

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

1 голос
/ 24 марта 2012

Переменная является ссылкой на объект 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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...