2011-12-08 87 views
3

我有以下情形:模块,类和命名空间在Python

我有抽象类ABA使用B执行一些任务。在这两个类中,都有一些“常量”参数(现在实现为类属性)由扩展这些抽象类的具体类设置,并且一些参数是共享的(它们应该在派生的“套件”中具有相同的值)类别SubASubB)。

我在这里面临的问题是如何在Python中组织命名空间。如果Python有动态范围的话,理想的解决方案是将这些参数声明为模块变量,然后当创建一套新的扩展类时,我可以将它们覆盖到它们的新模块中。但是(幸运的是,因为对大多数情况来说更安全,更方便),Python不能像那样工作。

为了把它放在一个更具体的方面(不是我的实际问题,当然不准确也不现实的),想象的东西就像一个nbody模拟器:

ATTRACTION_CONSTANT = NotImplemented # could be G or a Ke for example 

class NbodyGroup(object): 
    def __init__(self): 
     self.bodies = [] 

    def step(self): 
     for a in self.bodies: 
      for b in self.bodies: 
       f = ATTRACTION_CONSTANT * a.var * b.var/distance(a, b)**2 
       ... 

class Body(object): 
    def calculate_field_at_surface(self): 
     return ATTRACTION_CONSTANT * self.var/self.r**2 

那么其他模块可以实现一个PlanetarySystem(NBodyGroup)Planet(Body)设置ATTRACTION_CONSTANT6.67384E-11和其他模块可以实现MolecularAggregate(NBodyGroup)Particle(Body)并且设置ATTRACTION_CONSTANT8.987E9

简而言之:在模块级别模拟全局常量可以在派生模块(实现第一个模块中定义的抽象类的模块)中“覆盖”的替代方案是什么?

+0

你能更多的公关吗?珍惜你的实际问题?将一个全局ATTRACTION_CONSTANT定义放入包含派生类的模块中,并用这个全局变量“覆盖”两个子类中的值会出现什么问题? – Simon

回答

1

如何使用mixin?您可以定义(根据您的示例)PlanetarySystemConstantsMolecularAggregateConstants的类别,其中包含ATTRACTION_CONSTANT,然后使用class PlanetarySystem(NBodyGroup, PlanetarySystemConstants)class MolecularAggregate(NBodyGroup, MolecularAggregateConstants)来定义这些类别。

+0

+1迄今为止唯一的答案,改善目前的情况,但我宁愿远离混合(多继承是邪恶的!) – fortran

+1

嘿,我不得不不同意,我会说这更像是:“多继承**可以be ** evil“。如果你有一个特定的狭义理由,你使用它们,它们是有目的的。 – Glenn

+0

你是对的,但它就像是pringles:一旦你弹出... xD – fortran

0

你应该使用property这种情况下,

如。

class NbodyGroup(object): 
    @property 
    def ATTRACTION_CONSTANT(self): 
     return None 
    ... 
    def step(self): 
     for a in self.bodies: 
      for b in self.bodies: 
       f = self.ATTRACTION_CONSTANT * a.var * b.var/distance(a, b)**2 

class PlanetarySystem(NBodyGroup): 
    @property 
    def ATTRACTION_CONSTANT(self): 
     return 6.67384E-11 
+0

我不明白这是如何改善当前情况 – fortran

1

这里有几件事情我可以建议:

  1. 链接各机构的团体,使机体访问来自该组的一个常数,当它计算它的力量。例如:

    class NbodyGroup(object): 
        def __init__(self, constant): 
         self.bodies = [] 
         self.constant = constant 
    
        def step(self): 
         for a in self.bodies: 
          for b in self.bodies: 
           f = self.constant * a.var * b.var/distance(a, b)**2 
           ... 
    
    class Body(object): 
        def __init__(self, group): 
         self.group = group 
        def calculate_field_at_surface(self): 
         return self.group.constant * self.var/self.r**2 
    

    Pro:这会自动强制这样一个事实,即同一组中的物体应该施加同样的力量。 Con:在语义上,你可能会认为一个身体应该独立于任何可能存在的群体存在。

  2. 添加一个参数来指定力的类型。例如,这可能是一个枚举的值。

    class Force(object): 
        def __init__(self, constant): 
         self.constant = constant 
    GRAVITY = Force(6.67e-11) 
    ELECTRIC = Force(8.99e9) 
    
    class NbodyGroup(object): 
        def __init__(self, force): 
         self.bodies = [] 
         self.force = force 
    
        def step(self): 
         for a in self.bodies: 
          for b in self.bodies: 
           f = self.force.constant * a.charge(self.force) \ 
             * b.charge(self.force)/distance(a, b)**2 
           ... 
    
    class Body(object): 
        def __init__(self, charges, r): 
         # charges = {GRAVITY: mass_value, ELECTRIC: electric_charge_value} 
         self.charges = charges 
         ... 
        def charge(self, force): 
         return self.charges.get(force, 0) 
        def calculate_field_at_surface(self, force): 
         return force.constant * self.charge(force)/self.r**2 
    

    从概念上讲,我更喜欢这种方法,因为它封装了通常与该对象中给定对象(仅限于那些对象)关联的属性。如果执行速度是一个重要的目标,但是,这可能不是最好的设计。

希望你可以将这些翻译成你的实际应用。

+0

我会因为循环引用而忽略第一种方法...添加参数是一个不错的选择,但它可以很快得到非常详细:( – fortran

1

删除旧版本

你可以尝试继承__new__创建元类。然后在创建类时,可以通过使用python std的inspect模块查看以前的框架来获取子类模块,如果找到它,请在此处获取新常量,然后修补派生类的类属性。

我现在不会发布一个实现,因为它对我来说不是微不足道的,而且有点危险。

编辑:添加实施

A.py

import inspect 
MY_GLOBAL = 'base module' 
class BASE(object): 
    def __new__(cls, *args, **kwargs): 
     clsObj = super(BASE, cls).__new__(cls, *args, **kwargs) 
     clsObj.CLS_GLOBAL = inspect.stack()[-1][0].f_globals['MY_GLOBAL'] 
     return clsObj 

B.py

import A 
MY_GLOBAL = 'derived' 
print A.BASE().CLS_GLOBAL 

现在你可以有自己的作用域规则乐趣...

+0

这就是我目前正在做的我不喜欢它,这就是为什么我发布这个问题,因为我想删除重复的常量或跨类引用 – fortran

+0

不,ATTRACTION_CONSTANT变量是在类级别i我的版本。再次阅读你的问题和我的答案。 – Simon

+0

再次阅读完整的问题文本:*现在实施为类属性*;如果Python支持动态范围,示例场景中的代码就是我想要的。 – fortran