Загрязнение окружающей среды класса - PullRequest
1 голос
/ 06 января 2010

У меня есть объект, который содержит множество идентификаторов, которые доступны статически. Я хочу разделить это на другой объект, который содержит только те идентификаторы без необходимости внесения изменений в уже существующую базу кода. Возьмем для примера:

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

class Car(object):
    types = _CarType

Я хочу получить доступ к _CarType.DIESEL_CAR_ENGINE, либо позвонив Car.types.DIESEL_CAR_ENGINE, либо Car.DIESEL_CAR_ENGINE для обратной совместимости с существующим кодом. Ясно, что я не могу использовать __getattr__, поэтому я пытаюсь найти способ сделать эту работу (может быть, метаклассы?)

Ответы [ 5 ]

4 голосов
/ 06 января 2010

Хотя это не совсем то, для чего создается подкласс, оно выполняет то, что вы описываете:

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

class Car(_CarType):
    types = _CarType
3 голосов
/ 06 января 2010

Что-то вроде:

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
2 голосов
/ 06 января 2010

Ваши параметры делятся на две существенные категории: вы либо копируете атрибуты из _CarType в Car, либо устанавливаете метакласс Car в пользовательский с помощью метода __getattr__, который делегирует _CarType ( так что это не совсем верно, что вы не можете использовать __getattr__: вы можете, вам просто нужно вставить в Car ' meta класс, а не в Car сам ;-) .

Второй вариант имеет последствия, которые могут показаться вам странными (если только они не требуются специально): атрибуты не отображаются на dir(Car), и , к ним нельзя получить доступ на экземпляр из Car, только на самом Car. I.e.:

>>> 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__ с, не выглядит элегантным.

Так что я подозреваю, что более простой подход, "копировать все атрибуты", предпочтительнее. В 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 для классов) - это просто удобный способ обернуть эту важную и часто повторяющуюся семантику.

2 голосов
/ 06 января 2010

Вот как вы могли бы сделать это с метаклассом:

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 голосов
/ 06 января 2010
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, но не на классе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...