2012-03-30 94 views
3

这是一个处理数学/物理方程的类的设计原则问题,用户可以设置任何其余参数正在计算的参数。 在这个例子中,我希望能够设置频率,同时避免循环依赖。如何在设置属性时避免循环依赖?

例如:

from traits.api import HasTraits, Float, Property 
from scipy.constants import c, h 
class Photon(HasTraits): 
    wavelength = Float # would like to do Property, but that would be circular? 
    frequency = Property(depends_on = 'wavelength') 
    energy = Property(depends_on = ['wavelength, frequency']) 
    def _get_frequency(self): 
     return c/self.wavelength 
    def _get_energy(self): 
     return h*self.frequency 

我也知道这里的更新触发时机的问题,因为我不知道该序列的更新将被触发:

  1. 波长为已更改
  2. 触发两个相关实体的更新:频率和能量
  3. 但是,需要更新能量频率以便能量具有fitt值对新的波长!

(答案被接受还应该解决这个潜在的计时问题。)

那么,什么是最好的设计模式来解决这些相互依存的问题?最后,我希望用户能够更新波长,频率和频率/波长,并且能量应相应更新。

这种问题当然会出现在基本上所有试图处理方程的类中。

让比赛开始吧! ;)

+0

我认为traits.api可能会满足您的需求。是否有任何理由标准问题属性不适合你?特质主要用于建立课间,而不是从我在他们的页面上看到的课堂内依赖关系;你正在做类内依赖关系。 – 2012-03-30 21:52:18

+0

我不同意,因为类内依赖对设计GUI非常有帮助,相关的traits.ui库建立在特征之上。 – 2012-03-30 21:55:22

+0

即用于解决某些计算/数据分析任务的迷你GUI或小型应用程序。这些功能对于大规模GUI应用程序来说效果如何,我不知道,但至少对于GUI元素触发内容和其他内容的快速应用程序开发自动更新而言,这些属性是非常巧妙的。 – 2012-03-30 21:58:38

回答

2

感谢来自Enthought邮件列表的Adam Hughes和Warren Weckesser,我意识到我在理解中失去了什么。 属性并不真正作为属性存在。我现在将它们看作“虚拟”属性,完全取决于调用_getter或_setter时类的作者所做的事情。

所以当我希望能够由用户设置波长和频率时,我只需要了解频率本身不作为属性存在,而是在频率的设置时间,我需要更新'基本“属性波长,以便下次需要频率时,再次用新波长进行计算!

我也需要感谢用户sr2222谁让我想到了失踪的缓存。我意识到,只有在使用'cached_property'特征时,才需要使用关键字'depends_on'设置的依赖关系。如果计算成本不是很高或者不经常执行,_getters和_setters会照顾到所需的一切,而且不需要使用“depends_on”关键字。

这里现在精简解决方案,我一直在寻找的,可以让我设定任何波长或频率没有循环回路:

class Photon(HasTraits): 
    wavelength = Float 
    frequency = Property 
    energy = Property 

    def _wavelength_default(self): 
     return 1.0 
    def _get_frequency(self): 
     return c/self.wavelength 
    def _set_frequency(self, freq): 
     self.wavelength = c/freq 
    def _get_energy(self): 
     return h*self.frequency 

人们会使用这个类是这样的:

photon = Photon(wavelength = 1064) 

photon = Photon(frequency = 300e6) 

要设置初始值并获得能量现在,一个j乌斯直接使用它:

print(photon.energy) 

请注意,_wavelength_default方法采用了这样的情况,当用户没有提供一个初始值初始化光子实例。只有首次访问波长时,才会使用此方法来确定它。如果我不这样做,频率的第一次访问将导致1/0计算。

0

我会建议教你的应用程序什么可以从什么派生。例如,一个典型的情况是你有一组n个变量,其余任何一个都可以派生出来。 (当然,你也可以模拟更复杂的案例,但是直到你真正遇到这种情况时,我才会这样做)。

这可以仿照这样的:

# variable_derivations is a dictionary: variable_id -> function 
# each function produces this variable's value given all the other variables as kwargs 
class SimpleDependency: 
    _registry = {} 
    def __init__(self, variable_derivations): 
    unknown_variable_ids = variable_derivations.keys() - self._registry.keys(): 
     raise UnknownVariable(next(iter(unknown_variable_ids))) 
    self.variable_derivations = variable_derivations 

    def register_variable(self, variable, variable_id): 
    if variable_id in self._registry: 
     raise DuplicateVariable(variable_id) 
    self._registry[variable_id] = variable 

    def update(self, updated_variable_id, new_value): 
    if updated_variable_id not in self.variable_ids: 
     raise UnknownVariable(updated_variable_id) 
    self._registry[updated_variable_id].assign(new_value) 
    other_variable_ids = self.variable_ids.keys() - {updated_variable_id} 
    for variable_id in other_variable_ids: 
     function = self.variable_derivations[variable_id] 
     arguments = {var_id : self._registry[var_id] for var_id in other_variable_ids} 
     self._registry[variable_id].assign(function(**arguments)) 

class FloatVariable(numbers.Real): 
    def __init__(self, variable_id, variable_value = 0): 
    self.variable_id = variable_id 
    self.value = variable_value 
    def assign(self, value): 
    self.value = value 
    def __float__(self): 
    return self.value 

这只是一个小品,我没有测试或想通过各种可能的问题。