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. 但是能量需要更新频率,以便能量具有适合新波长的值!

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

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

当然,基本上所有尝试处理方程式的课程都会出现这类问题。

让比赛开始吧!;)

4

2 回答 2

2

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

因此,当我希望能够由用户设置波长和频率时,我只需要了解频率本身并不作为属性存在,而是在频率的 _setting 时间我需要更新“基本”属性波长,这样下次需要频率的时候,用新的波长重新计算一次!

我还需要感谢用户 sr2222,他让我想到了丢失的缓存。我意识到我使用关键字“depends_on”设置的依赖项仅在使用“cached_property”特征时才需要。如果计算成本不是那么高或不经常执行,_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)

要设置初始值并立即获取能量,只需直接使用它:

print(photon.energy)

请注意,_wavelength_default 方法会处理用户在未提供初始值的情况下初始化 Photon 实例的情况。只有对波长的第一次访问才会使用这种方法来确定它。如果我不这样做,频率的第一次访问将导致 1/0 计算。

于 2012-04-07T19:13:43.093 回答
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

这只是一个草图,我没有测试或考虑所有可能的问题。

于 2012-04-04T20:32:45.947 回答