2010-01-06 32 views
1

我有一个对象,其中包含大量静态访问的ID。我想将它分解成另一个只保存这些id的对象,而不需要对已经存在的代码库进行修改。举个例子:污染班级的环境

class _CarType(object): 
    DIESEL_CAR_ENGINE = 0 
    GAS_CAR_ENGINE = 1 # lots of these ids 

class Car(object): 
    types = _CarType 

我希望能够通过调用Car.types.DIESEL_CAR_ENGINE向后兼容访问_CarType.DIESEL_CAR_ENGINE要么,要么通过Car.DIESEL_CAR_ENGINE与存在的代码。很明显,我不能使用__getattr__,所以我试图找到使这项工作的一种方式

+0

为什么你不能使用'__getattr__'?看起来像这里最简单的解决方案。 (检查isupper并执行'getattr(_CarType,key)') – kibitzer 2010-01-06 12:46:55

回答

3

喜欢的东西(也许元类?):

class Car(object): 
    for attr, value in _CarType.__dict__.items(): 
     it not attr.startswith('_'): 
      locals()[attr] = value 
    del attr, value 

或者你也可以做出来的类声明:

class Car(object): 
    # snip 

for attr, value in _CarType.__dict__.items(): 
    it not attr.startswith('_'): 
     setattr(Car, attr, value) 
del attr, value 
+0

它工作得很好,但我正在寻找更优雅的解决方案。如果什么都没有出现,你就是赢家!非常感谢你。 – hyperboreean 2010-01-06 12:05:22

+1

你不应该修改'locals()':http://docs.python.org/library/functions.html#locals – 2010-01-06 12:34:51

+1

setattr的这种用法是关于你要得到的最优雅和简单的解决方案。 – 2010-01-06 12:37:06

4

虽然这不完全是子类是为制造,它实现了你的描述:

class _CarType(object): 
    DIESEL_CAR_ENGINE = 0 
    GAS_CAR_ENGINE = 1 # lots of these ids 

class Car(_CarType): 
    types = _CarType 
+0

Thnaks balpha为你的答案,但我倾向于不喜欢它,因为我忘了提及汽车将继承一些其他类,我不喜欢多重继承。不过,这是个好主意! – hyperboreean 2010-01-06 12:23:56

+1

在这种情况下,多继承应该不会造成问题 - 只要'_CarType'只包含您的常量,并且它们不会出现在其他任何地方,我不会看到任何MRO头部划伤的风险。 – balpha 2010-01-06 12:27:35

+0

实际上,我回答了这个问题,不像我的尝试,这看起来像是最干净的方式。 – 2010-01-06 14:55:50

2

这是你可以如何与元类做到这一点:

class _CarType(type): 
    DIESEL_CAR_ENGINE = 0 
    GAS_CAR_ENGINE = 1 # lots of these ids 
    def __init__(self,name,bases,dct): 
     for key in dir(_CarType): 
      if key.isupper(): 
       setattr(self,key,getattr(_CarType,key)) 

class Car(object): 
    __metaclass__=_CarType 

print(Car.DIESEL_CAR_ENGINE) 
print(Car.GAS_CAR_ENGINE) 
+0

很好的解决方案!虽然元类编程被认为是某种黑暗的艺术...... – 2010-01-06 12:32:54

+0

我同意简单比复杂更好,元类在这里并不是真的需要。 然而,作为一个试图理解元类的人,我希望这可能对其他人走向同样道路有用。 – unutbu 2010-01-06 12:41:46

+0

-1不必要的元类。哎呀! – 2010-01-06 14:50:41

0
class _CarType(object): 
    DIESEL_CAR_ENGINE = 0 
    GAS_CAR_ENGINE = 1 # lots of these ids 

class Car: 
    types = _CarType 
    def __getattr__(self, name): 
    return getattr(self.types, name) 

如果未找到对象的属性,它定义了魔术方法__getattr__,被调用,试图找到它。

只适用于Car实例,不适用于课堂。

+0

你甚至试过这个解决方案吗? Car.DIESEL_CAR_ENGINE将如何工作? self指已经创建的对象... – hyperboreean 2010-01-06 14:09:51

+0

它适用于Car实例,但不适用于类。 – 2010-01-06 14:23:24

+0

哎呀,它应该......修正了错误。 – 2010-01-06 14:33:01

2

您的选项分为两大类可观:你要么从_CarType属性复制到Car,或者设置Car的元类自定义一个带有__getattr__方法委托给_CarType(所以它是不完全真实的,你不能使用__getattr__:你可以,你只需要输入Carmeta class而不是Car本身;-)。

第二种选择具有您可能会发现独特的(除非它们是专所需的)影响:该属性不上dir(Car)他们不能的Car实例访问仅显示出来,在Car本身。即:

>>> class MetaGetattr(type): 
... def __getattr__(cls, nm): 
...  return getattr(cls.types, nm) 
... 
>>> class Car: 
... __metaclass__ = MetaGetattr 
... types = _CarType 
... 
>>> Car.GAS_CAR_ENGINE 
1 
>>> Car().GAS_CAR_ENGINE 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'Car' object has no attribute 'GAS_CAR_ENGINE' 

您可以通过添加__getattr__Car修复“不是从一个实例”的问题:

>>> class Car: 
... __metaclass__ = MetaGetattr 
... types = _CarType 
... def __getattr__(self, nm): 
...  return getattr(self.types, nm) 
... 

,使两种类型的查询工作,如可能预计:

>>> Car.GAS_CAR_ENGINE 
1 
>>> Car().GAS_CAR_ENGINE 
1 

然而,限定,基本上相等__getattr__ s,看起来并不高雅。

所以我怀疑更简单的方法,“复制所有属性”,是可取的。在Python 2中。6或更高,这是一类装饰一个明显的候选人:

def typesfrom(typesclass): 
    def decorate(cls): 
    cls.types = typesclass 
    for n in dir(typesclass): 
     if n[0] == '_': continue 
     v = getattr(typesclass, n) 
     setattr(cls, n, v) 
    return cls 
    return decorate 

@typesfrom(_CarType) 
class Car(object): 
    pass 

在一般情况下,它的价值定义,如果你使用它不止一次装饰;如果您只需要为一个类执行此任务,那么将代码内联代替(在class语句后)可能会更好。

如果你坚持使用Python 2.5(或2.4甚至),你仍然可以定义typesfrom以同样的方式,你只要申请一个稍微不太优雅的事情,即Car定义变为:

class Car(object): 
    pass 
Car = typesfrom(_CarType)(Car) 

不要忘记装饰器语法(在2.2中为函数引入,在2.6中为类引入)仅仅是一种方便的方式来包装这些重要的和频繁重复的语义。