我意识到这个问题与事件处理有关,我读过有关调度程序的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方法。对不起,感到困惑。
答案 0 :(得分:66)
您需要使用Observer Pattern。 在以下代码中,一个人订阅接收来自全球财富实体的更新。当全球财富发生变化时,该实体会向其所有订户(观察员)发出变更警报。然后人自我更新。
我在这个例子中使用了属性,但它们不是必需的。一个小警告:属性仅适用于新样式类,因此类声明之后的(对象)是必须的。
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)
答案 1 :(得分:12)
如果要在更改属性时执行代码,则可以使用属性。警惕改变属性时出现的大的副作用或显着的开销对使用您的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)
答案 2 :(得分:8)
你在寻找什么称为 (Functional) Reactive Programming。对于Common Lisp,有单元格 - 请参阅Cells project和Cells manifesto,对于python,有{{} 3}}
电子表格也使用相同的范例。对于跟踪多个相互关联的参数非常有用 - 例如在GUI编程中。
反应式编程类似于观察者模式,但有一个重要的区别:
与观察者模式的相似性然而,将数据流概念集成到编程语言中会使表达它们变得更容易,因此可以增加数据流图的粒度。例如,观察者模式通常描述整个对象/类之间的数据流,而面向对象的反应式编程可以针对对象/类的成员。
答案 3 :(得分:3)
您需要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方法。
答案 4 :(得分:1)
您可以尝试这样的事情:
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)
如果需要参数,只需使用lambda函数即可。鉴于此(以及我最近更深入研究的公认答案),这里有一个更复杂的版本,包括评论,更多功能和示例:
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's value and call any bound functions
self.v=v
for x in self.commands:
x()
def get(self): #Get the variable's 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.
"""
无论如何,您还可以使用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()
是另一种替代方法,它会导致调用函数暂停而不会暂停GUI,直到您指定的变量发生更改。还有其他类似的方法。