Аккуратный способ расширения атрибута класса в подклассах - PullRequest
16 голосов
/ 25 мая 2009

Допустим, у меня есть следующий класс

class Parent(object):
    Options = {
        'option1': 'value1',
        'option2': 'value2'
    }

И подкласс под названием Child

class Child(Parent):
   Options = Parent.Options.copy()
   Options.update({
        'option2': 'value2',
        'option3': 'value3'
   })

Я хочу иметь возможность переопределять или добавлять параметры в дочернем классе. Решение, которое я использую, работает. Но я уверен, что есть лучший способ сделать это.


EDIT

Я не хочу добавлять опции в качестве атрибутов класса, поскольку у меня есть другие атрибуты класса, которые не являются опциями, и я предпочитаю хранить все опции в одном месте. Это простой пример, реальный код более сложный, чем этот.

Ответы [ 6 ]

21 голосов
/ 25 мая 2009

Семантически эквивалентно вашему коду, но, возможно, аккуратнее:

class Child(Parent):
   Options = dict(Parent.Options,
      option2='value2',
      option3='value3')

Помните, что "жизнь лучше без фигурных скобок", и, вызывая dict явно, вы часто можете избежать фигурных скобок (и дополнительных кавычек вокруг ключей, которые являются константами в виде идентификаторов).

См. http://docs.python.org/library/stdtypes.html#dict для более подробной информации - бит ключа равен «Если ключ указан как в позиционном аргументе, так и в качестве аргумента ключевого слова, значение, связанное с ключевым словом, сохраняется», т.е. ключевое слово args переопределить ассоциации ключ-значение в позиционном аргументе, точно так же, как метод update позволяет их переопределить).

8 голосов
/ 25 мая 2009

Одним из способов будет использование аргументов ключевых слов для указания дополнительных ключей:

Parent.options = dict(
    option1='value1',
    option2='value2',
)

Child.options = dict(Parent.options,
    option2='value2a',
    option3='value3',
)

Если вы хотите стать более любопытным, то, используя протокол дескриптора, вы можете создать прокси-объект, который будет инкапсулировать поиск. (просто перейдите владелец .__ mro__ от атрибута владельца к методу __get __ (self, instance, owner)). Или, что еще интереснее, переход на территорию, которая, вероятно, не очень хорошая, декораторы метаклассов / классов.

6 голосов
/ 25 мая 2009

Подумав об этом больше, и благодаря предложению @SpliFF я пришел к следующему:

class Parent(object):
    class Options:
        option1 = 'value1'
        option2 = 'value2'


class Child(Parent):
    class Options(Parent.Options):
        option2 = 'value2'
        option3 = 'value3'

Я все еще открыт для лучших решений.

4 голосов
/ 25 мая 2009

Почему бы просто не использовать атрибуты класса?

class Parent(object):
    option1 = 'value1'
    option2 = 'value2'

class Child(Parent):
    option2 = 'value2'
    option3 = 'value3'
2 голосов
/ 10 июля 2010

Вот способ использования метакласса

class OptionMeta(type):
    @property
    def options(self):
        result = {}
        for d in self.__mro__[::-1]:
            result.update(getattr(d,'_options',{})) 
        return result

class Parent(object):
    __metaclass__ = OptionMeta
    _options = dict(
        option1='value1',
        option2='value2',
        )

class Child(Parent):
    _options = dict(
        option2='value2a',
        option3='value3',
        )

print Parent.options
print Child.options
2 голосов
/ 25 мая 2009
class ParentOptions:
  option1 = 'value1'
  option2 = 'value2'

class ChildOptions(ParentOptions):
  option2 = 'value2'
  option3 = 'value3'

class Parent(object):
  options = ParentOptions()

class Child(Parent):
  options = ChildOptions()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...