Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/286.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/email/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 设置属性时如何避免循环依赖关系?_Python_Traits_Enthought - Fatal编程技术网

Python 设置属性时如何避免循环依赖关系?

Python 设置属性时如何避免循环依赖关系?,python,traits,enthought,Python,Traits,Enthought,这是一个设计原则问题,适用于处理数学/物理方程的类,其中允许用户设置任何参数,根据这些参数计算剩余的。 在本例中,我希望能够设置频率,同时避免循环依赖 例如: 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 w

这是一个设计原则问题,适用于处理数学/物理方程的类,其中允许用户设置任何参数,根据这些参数计算剩余的。 在本例中,我希望能够设置频率,同时避免循环依赖

例如:

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
我也知道这里有一个更新触发时间问题,因为我不知道更新触发的顺序:

  • 波长正在改变
  • 这会触发两个相关实体的更新:频率和能量
  • 但是能量需要更新频率,以便能量具有适合新波长的值 (要接受的答案还应解决这一潜在的时间问题。)

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

    当然,这类问题在所有试图处理方程的课程中都会出现


    让比赛开始吧!;)

    我建议教你的应用程序什么可以从什么中派生出来。例如,一个典型的情况是,您有一组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
    
    photon = Photon(wavelength = 1064)
    

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

    感谢热心邮件列表中的亚当·休斯和沃伦·韦克瑟,我意识到我的理解中遗漏了什么。 属性实际上并不作为属性存在。我现在把它们看作是一个“虚拟”属性,它完全取决于类的编写者在调用getter或setter时所做的事情

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

    我还需要感谢用户sr2222,他让我想到了丢失的缓存。我意识到,我使用关键字'dependens_-on'设置的依赖项只有在使用'cached_-property'特性时才需要。如果计算的成本没有那么高或者没有经常执行,那么_getter和_setter会处理所有需要的事情,而不需要使用'depens_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
    
    我们可以这样使用这个类:

    # 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
    
    photon = Photon(wavelength = 1064)
    

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

    print(photon.energy)
    

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

    我认为traits.api对于您的需求来说可能有些过分。是否有任何理由标准问题属性不适用于您?Traits主要用于设置类间依赖关系,而不是我在页面上看到的类内依赖关系;你在做类内依赖。我不同意,因为类内依赖对设计GUI非常有帮助,相关traits.ui库是基于traits.即用于解决某些计算/数据分析任务的迷你GUI或迷你应用程序构建的。我不知道这些属性在大规模GUI应用程序中的效果如何,但至少在快速应用程序开发中,GUI元素触发内容,其他内容自动更新,这些属性非常巧妙。但如果您的值是从一开始就派生出来的,则标准问题属性就可以正常工作。你有1个基本值,然后是2个常数。让属性getter都从基本值计算,然后setter都转换为基本值。使用traits.api(你甚至还没有)所得到的只是一种缓存结果的方法,这是一种过早的优化。