Подтвердить что ты не робот

Как вызвать функцию изменения значения?

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

Я хочу, чтобы метод m() объекта A запускался при изменении значения v:

Например (если деньги становятся счастливыми):

global_wealth = 0

class Person()
    def __init__(self):
        self.wealth = 0
        global global_wealth
        # here is where attribute should be
        # bound to changes in 'global_wealth'
        self.happiness = bind_to(global_wealth, how_happy)

    def how_happy(self, global_wealth):
        return self.wealth / global_wealth

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

NB: Мне пришлось отредактировать вопрос, поскольку первая версия показала, что мне нужны методы getter и setter. Извините за путаницу.

4b9b3361

Ответ 1

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

Я использую свойства в этом примере, но они не нужны. Небольшое предупреждение: свойства работают только в новых классах стилей, поэтому (объект) после деклараций классов является обязательным для работы.

class GlobalWealth(object):
    def __init__(self):
        self._global_wealth = 10.0
        self._observers = []

    @property
    def global_wealth(self):
        return self._global_wealth

    @global_wealth.setter
    def global_wealth(self, value):
        self._global_wealth = value
        for callback in self._observers:
            print('announcing change')
            callback(self._global_wealth)

    def bind_to(self, callback):
        print('bound')
        self._observers.append(callback)


class Person(object):
    def __init__(self, data):
        self.wealth = 1.0
        self.data = data
        self.data.bind_to(self.update_how_happy)
        self.happiness = self.wealth / self.data.global_wealth

    def update_how_happy(self, global_wealth):
        self.happiness = self.wealth / global_wealth


if __name__ == '__main__':
    data = GlobalWealth()
    p = Person(data)
    print(p.happiness)
    data.global_wealth = 1.0
    print(p.happiness)

Ответ 2

Вы можете использовать свойства, если вы хотите выполнить код при изменении атрибутов. Будьте осторожны, что большие побочные эффекты или значительные накладные расходы, возникающие при изменении атрибута, немного удивительны для любого, кто использует ваш API, поэтому в некоторых случаях вы, вероятно, захотите избежать этого, используя вместо этого методы.

class A(object):

    def m(self, p_value):
         print p_value

    @property
    def p(self):
        return self._p 

    @p.setter
    def p(self, value)
        self._p = value
        self.m(value)

Ответ 3

Что вы ищете, называется (функциональное) реактивное программирование. Для Common Lisp есть ячейки - см. Проект ячеек и манифест ячеек, а для python есть библиотека Trellis.

В таблицах также используется одна и та же парадигма. Очень полезно для отслеживания нескольких взаимосвязанных параметров - например, в графическом интерфейсе.

Реактивное программирование похоже на шаблон Observer, но с важным отличием:

Сходство с шаблоном Observer. Однако интеграция концепций потока данных в язык программирования упростит их выражение и, следовательно, может увеличить гранулярность графика потока данных. Например, шаблон наблюдателя обычно описывает потоки данных между целыми объектами/классами, тогда как объектно-ориентированное реактивное программирование может быть нацелено на членов объектов/классов.

Ответ 4

Вам понадобится property

class MyClass(object):
    def __init__(self):
        self._x = None

    def x_setter(self, value):
        self._x = value

    def x_getter(self):
        return self._x

    x = property(x_getter, x_setter)

Здесь, когда вы хотите установить x MyClass().x = "foo", вы будете использовать метод x_getter и всякий раз, когда вы хотите получить x print MyClass().x, вы будете использовать метод x_setter.

Ответ 5

Вы можете попробовать что-то вроде этого:

class Variable:
    def __init__(self, v):
        self.v=v
        self.command=None
    def set(self, v):
        self.v=v
        if self.command!=None:
            self.command()
    def get(self):
        return self.v
    def trace(self, command):
        self.command=command

x=Variable(0)

def money():
    amount="{:.2f}".format(x.get())
    print("You have $"+amount+".")

x.trace(money)

x.set(5.55)
x.set(15.14)

Если вам нужны аргументы, просто используйте лямбда-функцию. В свете этого (и принятого ответа я недавно рассмотрел более подробно), здесь более сложная версия с комментариями, больше функциональности и примеров:

class Variable: #This is a class for the variable you want to bind something to
    def __init__(self, v):
        self.v=v
        self.commands=[]
    def set(self, v): #Set the variable value and call any bound functions
        self.v=v
        for x in self.commands:
            x()
    def get(self): #Get the variable value
        return self.v
    def trace(self, *commands): #Bind one or more functions to the variable
        for x in commands:
            if x in self.commands:
                raise ValueError("You can’t add the same command object twice. If you need to, use another lambda function that calls the same function with the same parameters.")
        self.commands.extend(commands)
    def untrace(self, *commands): #Unbind one or more functions from the variable
        for x in commands:
            if x not in self.commands:
                raise ValueError(str(x)+" is not a traced command.")
        for x in commands:
            if x in self.commands:
                self.commands.remove(x)
    def clear_traces(self): #Removes all functions bound to the variable
        self.commands.clear()

x=Variable(0) #Make the variable, starting with a value of 0

def money(name): #Define the method to bind
    amount="{:.2f}".format(x.get())
    print(name+" has $"+amount+".")

sam=lambda : money("Sam") #We're making a new method to bind that calls the old one with the argument "Sam"
sally=lambda : money("Sally") #Another one (Sally and Sam will always have the same amount of money while they are both bound to the variable.)

#Bind them both to the value (not that this is practical, but we're doing both for demonstration)
x.trace(sam)
x.trace(sally)

#Set the value
x.set(5.55)
#Unbind the sam lambda function and set the value again
x.untrace(sam)
x.set(15.14)

"""
This prints the following:
> Sam has $5.55.
> Sally has $5.55.
> Sally has $15.14.
"""

Alternative

В любом случае вы также можете использовать встроенную функциональность, поставляемую с Tkinter, с такими, как DoubleVar.trace() или someWidget.wait_variable().

Метод trace() позволяет связать метод с StringVar, IntVar, FloatVar, DoubleVar, BooleanVar или такими переменными. Здесь приведен полный рабочий пример Python 3.x:

from tkinter import *

tk=Tk()
tk.withdraw()

d=DoubleVar(master=tk, value=0)

def my_event_handler(*args):
    amount="{:.2f}".format(d.get())
    print("$"+amount)

d.trace(mode="w", callback=my_event_handler)

d.set(5.55)
d.set(15.12)

"""
This prints the following:
> You have $5.55.
> You have $15.12.
"""

Возможно, вы захотите уничтожить объект Tk в конце программы. Тем не менее, он, кажется, выходит из строя без него в моем примере.

wait_variable() - еще одна альтернатива, которая заставляет функцию вызова останавливаться без остановки вашего графического интерфейса до тех пор, пока переменная, указанная вами, не изменится. Существуют и другие аналогичные методы.