2015-01-14 3 views
3

У меня есть два класса в моем коде. first является родителем, который наследует second.Замена по умолчанию __dict__ для объекта с OrderedDict

class first(object): 
    def __init(self,**kwargs): 
     pass 

    def __setattr__(self,name,value): 
     self.__dict__[name] = value 

class second(first): 
    def do_something(self): 
     self.a = 1 
     self.b = 2 
     self.c = 3 

, когда я печать класса второй (посредством, например, second.__dict__) Я получаю неупорядоченный словарь. Это очевидно. Я хочу изменить это поведение, чтобы получить упорядоченный словарь, используя класс OrderedDict, но он не работает. Я меняю реализацию first следующим образом:

class first(OrderedDict): 
    def __init__(self,**kwargs): 
     super(first,self).__init__(**kwargs) 
    def __setattr__(self,name_value): 
     super(first,self).__setattr__(name_value) 

Я хотел бы напечатать second с помощью __dict__ или __repr__, но я получил неупорядоченный словарь. Что я должен изменить?

+6

«__dict__» экземпляров 'OrderDict' сам по себе не является экземпляром' OrderedDict' ... –

+1

Не могли бы вы объяснить * почему * вы хотите это? Могут быть другие подходы, которые вы могли бы изучить. – jonrsharpe

+0

Почему вы _need_ '__dict__' заказываете? Обычные 'dict' более эффективны, и для печати вы можете просто отсортировать dict в своих '__repr__' или' __str__' методах. –

ответ

4

Вы можете просто перенаправить весь доступ атрибута к OrderedDict:

class first(object): 
    def __init__(self, *args, **kwargs): 
     self._attrs = OrderedDict(*args, **kwargs) 

    def __getattr__(self, name): 
     try: 
      return self._attrs[name] 
     except KeyError: 
      raise AttributeError(name) 

    def __setattr__(self, name, value): 
     if name == '_attrs': 
      return super(first, self).__setattr__(name, value) 
     self._attrs[name] = value 

Демо:

>>> from collections import OrderedDict 
>>> class first(object): 
...  def __init__(self, *args, **kwargs): 
...   self._attrs = OrderedDict(*args, **kwargs) 
...  def __getattr__(self, name): 
...   try: 
...    return self._attrs[name] 
...   except KeyError: 
...    raise AttributeError(name) 
...  def __setattr__(self, name, value): 
...   if name == '_attrs': 
...    return super(first, self).__setattr__(name, value) 
...   self._attrs[name] = value 
... 
>>> class second(first): 
...  def do_something(self): 
...   self.a = 1 
...   self.b = 2 
...   self.c = 3 
... 
>>> s = second() 
>>> s.do_something() 
>>> s._attrs 
OrderedDict([('a', 1), ('b', 2), ('c', 3)]) 
+0

Большое спасибо за эту демонстрацию. На самом деле он работает в этом простом примере, но после реализации этого в моем причудливом коде я получил ошибку: RuntimeError: превышена максимальная глубина рекурсии. Я ищу ошибку в коде, но тем временем, может быть, вы могли бы найти причину этой проблемы? Или предложите альтернативное решение, чтобы избежать такой проблемы. Я бы не хотел увеличивать глубину рекурсии. – Przemo

+0

@Przemo: ваш '__setattr__' сам звонит? Как вы используете 'super()' здесь?Убедитесь, что вы не устанавливаете '_attrs' в неправильный объект. –

+0

@Przemo: увеличение глубины рекурсии не решит эту проблему, у вас есть * бесконечный * цикл. –

-1

Другой вариант; вы также можете манипулировать если хотите.

from collections import OrderedDict 
class OrderedClassMeta(type): 
    @classmethod 
     def __prepare__(cls, name, bases, **kwds): 
     return OrderedDict() 
class OrderedClass(metaclass=OrderedClassMeta): 
    pass 

class A(OrderedClass): 
    def __init__(self): 
     self.b=1 
     self.a=2 
    def do(self): 
     print('do') 
class B(OrderedClass): 
    def __init__(self): 
     self.a=1 
     self.b=2 
    def do(self): 
     print('do') 

a=A() 
print(a.__dict__) 
b=B() 
print(b.__dict__) 
+0

Пожалуйста, попробуйте свой ответ перед публикацией, это не сработает. Возвращаемое значение '__prepare__' копируется в стандартный dict в C. См. Https://mail.python.org/pipermail/python-list/2012-January/618121.html. –

+0

Был отпечаток с отступом оригинального сообщения (для «def __prepare__»). Если вы очистите отступы и используете Python 3.6, результат выглядит следующим образом: >>> a=A() >>> print(a.__dict__) {'b': 1, 'a': 2} >>> b=B() >>> print(b.__dict__) {'a': 1, 'b': 2} >>>

0

Вы можете попробовать на самом деле замену __dict__ с OrderedDict:

from collections import OrderedDict 

class Test(object): 
    def __init__(self): 
     self.__dict__ = OrderedDict() 
     self.__dict__['a'] = 0 
     self.__dict__['b'] = 1 
     self.__dict__['c'] = 2 

test = Test() 
print test.__dict__ 
test.a, test.b, test.c = 'a', 'b', 'c' 
print test.__dict__ 

Это должно распечатаем:

OrderedDict([('a', 0), ('b', 1), ('c', 2)]) 
OrderedDict([('a', 'a'), ('b', 'b'), ('c', 'c')]) 
+0

Это похоже на плохую идею. Я не знаю точно, что происходит, но я думаю, что 'OrderedDict' заканчивается в' '__dict __ '' слоте реального '__dict__', и происходит всякая непредсказуемая ерунда. Попробуйте еще раз без 'self .__ dict __ ['a'] = 0' линий и посмотрите. – mdaoust

+1

@mdaoust: Замена экземпляра '__dict__' другого объекта сопоставления в порядке. См. [Документация] (https://docs.python.org/2/library/stdtypes.html#object.__dict__). – martineau

1

Я думаю, что решения в этой теме слишком много внимания на использование OrderedDict, как если бы это необходимость. Класс уже имеет встроенный метод __dict__, единственная проблема заключается в заказе ключей. Вот как я извлечение (key, value) пары из моего класса в порядке, они вошли:

class MyClass: 

    def __init__(self, arg1, arg2, arg3): 
     self._keys = [] 
     self.arg1 = arg1 
     self.arg2 = arg2 
     self.arg3 = arg3 

    def __setattr__(self, key, value): 
     # store new attribute (key, value) pairs in builtin __dict__ 
     self.__dict__[key] = value 
     # store the keys in self._keys in the order that they are initialized 
     # do not store '_keys' itelf and don't enter any key more than once 
     if key not in ['_keys'] + self._keys: 
      self._keys.append(key) 

    def items(self): 
     # retrieve (key, value) pairs in the order they were initialized using _keys 
     return [(k, self.__dict__[k]) for k in self._keys] 

>>> x = MyClass('apple', 'orange', 'banana') 
>>> print x.items() 
[('arg1', 'apple'), ('arg2', 'orange'), ('arg3', 'banana')] 
>>> x.arg1 = 'pear' 
>>> print x.items() 
[('arg1', 'pear'), ('arg2', 'orange'), ('arg3', 'banana')] 

Я использую класс для хранения около 70 переменных, используемых для настройки и запуска гораздо большой программы. Я сохраняю текстовую копию исходных пар (key, value), которые могут использоваться для инициализации новых экземпляров класса. Я также сохраняю текстовую копию пар (key, value) после запуска программы, потому что некоторые из них установлены или изменены во время запуска программы. Наличие пар (key, value) просто улучшает читаемость текстового файла, когда я хочу просмотреть результаты.

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