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」特性を使用する場合にのみ必要であることに気付きました。計算のコストがそれほど高くないか、それほど頻繁に実行されない場合は、_getter と _setter が必要なすべてを処理し、'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 個の変数のセットがあり、残りの変数から任意の 1 つを導出できるというのが典型的なケースです。(もちろん、もっと複雑なケースをモデル化することもできますが、実際にそのようなケースに出くわすまではしません)。

これは次のようにモデル化できます。

# 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 に答える