Python中的模块,类和命名空间

时间:2011-12-08 10:37:21

标签: python class module namespaces

我有以下情况:

我有抽象类AB,而A使用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

简而言之:在模块级别模拟全局常量的哪些好方法可以在派生模块中实现“覆盖”(实现第一个模块中定义的抽象类的模块)?

4 个答案:

答案 0 :(得分: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:这会自动强制执行同一组中的实体应施加相同类型的力量这一事实。骗局:从语义上讲,你可以争辩说一个团体应该独立于任何一个团体而存在。

  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
    

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

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

答案 1 :(得分:1)

使用mixin怎么样?您可以定义(基于您的示例)包含PlanetarySystemConstants的{​​{1}}和MolecularAggregateConstants的类,然后使用ATTRACTION_CONSTANTclass PlanetarySystem(NBodyGroup, PlanetarySystemConstants)来定义这些类。< / p>

答案 2 :(得分:1)

删除旧版本

您可以尝试子类化__new__来创建元类。然后在类创建中,您可以通过使用python std的inspect模块查看以前的帧来获取子类模块,如果找到一个,则在此处获取新常量,并修补派生类的class属性。 / p>

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

修改:添加了实施

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

现在你可以享受自己的范围规则......

答案 3 :(得分: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