2012-03-30 111 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 

這只是一個小品,我沒有測試或想通過各種可能的問題。