2017-02-13 4 views
1

У меня есть классы, как это:Обновить родительский ДИКТ в подклассе

class Parent(object): 
    _defaults = { 
     'key': 'value', 
     'key2': 'value', 
    } 

class Child(Parent): 
    _defaults = { 
     'key2': 'value2', 
     'key3': 'value2', 
    } 

Когда я создаю класс ребенка, я хочу _defaults быть установлен в родительский класс _defaults, а затем обновляются с ребенком _defaults. Таким образом, фактический разрешен класс должен выглядеть следующим образом:

class Child(object): 
    _defaults = { 
     'key': 'value', 
     'key2': 'value2', 
     'key3': 'value2', 
    } 

Возможно ли это с __metaclass__ конструктором или что-то?

ответ

1

Использование метакласса вам нужно сделать, это __new__ метод для того, чтобы применить изменения перед конкретизации. Поскольку метод __new__ принимает аргумент cls, name, bases, namespace, **kwds, вы можете просто получить доступ к родительскому объекту, используя аргумент bases, который является кортежем всех базовых классов, тогда вы можете просто обновить ожидаемый атрибут.

class MyMetaClass(type): 

    def __new__(cls, name, bases, namespace, **kwds): 
     bases[0]._defaults.update(namespace['_defaults']) 
     result = type.__new__(cls, name, bases, dict(namespace)) 
     return result 


class Parent(object): 
    _defaults = { 
     'key': 'value', 
     'key2': 'value', 
    } 

class Child(Parent, metaclass=MyMetaClass): 
    _defaults = { 
     'key2': 'value2', 
     'key3': 'value2', 
    } 

Демо:

c = Child()  
print(type(c).__bases__[0]._defaults) 
{'key2': 'value2', 'key3': 'value2', 'key': 'value'} 

Обратите внимание, что, как упоминалось @jsbueno этот метод имеет большую проблему, которая обновляет атрибут родительского класса _defaults на каждом конкретизации. Одним из способов отказа от этой проблемы может быть предоставление метакласса с флагом для обновления родителя только один раз.

+0

В идеале вы должны использовать 'ChainMap', как указано в другом ответе, но это работает достаточно хорошо. – xaav

+1

@xaav Да, это хороший подход. Но я опубликовал этот ответ, поскольку я, хотя OP ищет решение на основе метакласса для образовательной цели. – Kasramvd

+0

Я тот, кто разместил вопрос! – xaav

2

Вы можете сделать это с помощью collections.ChainMap

import collections 

class Child(Parent): 
    _defaults = collections.ChainMap({ 
     'key2': 'value2', 
     'key3': 'value2', 
    }, Parent._defaults) 

Конечно, вы могли написать метакласса объединить эти вещи для вас, но это было бы сложно понять (в целом), которая отображает должен быть объединен с картами на суперклассах и которые должны переопределяться ...

1

Прочитать статьи словаря напрямую, а затем обновить?

class Parent(object): 
    _defaults = { 
     'key': 'value', 
     'key2': 'value', 
    } 


class Child(Parent): 
    _defaults = {k: v for k, v in Parent._defaults.items()} 
    _defaults.update({'key2': 'value2', 
         'key3': 'value2'}) 


testpar = Parent() 
test = Child() 

print(testpar._defaults) 
print(test._defaults) 

печатает:

{'key2': 'value', 'key': 'value'} 
{'key3': 'value2', 'key2': 'value2', 'key': 'value'} 

как в 2,7 и 3,5

+0

Почему бы не '_defaults = Parent._defaults.copy()'? – mgilson

Смежные вопросы