Подтвердить что ты не робот

Правильный способ переопределить метод __dir__ в python

Этот вопрос должен быть больше о __dir__, чем о numpy.

У меня есть подкласс numpy.recarray (в python 2.7, numpy 1.6.2), и я заметил, что имена полей recarray не указаны, когда dir объект (и, следовательно, автозаполнение ipython не работает).

Попытка исправить это, я попытался переопределить __dir__ в моем подклассе, например:

def __dir__(self):
    return sorted(set(
               super(MyRecArray, self).__dir__() + \
               self.__dict__.keys() + self.dtype.fields.keys()))

что привело к: AttributeError: 'super' object has no attribute '__dir__'. (Я нашел здесь, это должно действительно работать в python 3.3...)

В качестве обходного пути я попытался:

def __dir__(self):
    return sorted(set(
                dir(type(self)) + \
                self.__dict__.keys() + self.dtype.fields.keys()))

Насколько я могу судить, это работает, но, конечно, не так элегантно.

Вопросы:

  • Является ли последнее решение правильным в моем случае, т.е. для подкласса recarray?
  • Есть ли способ заставить его работать в общем случае? Мне кажется, что это не сработало бы с множественным наследованием (нарушая цепочку super -call), и, конечно, для объектов без __dict__...
  • Знаете ли вы, почему recarray не поддерживает перечисление имен полей для начала? простой надзор?
4b9b3361

Ответ 1

Python 2.7+, 3.3+ mixin, который упрощает реализацию метода __dir__ в подклассах. Надеюсь, это поможет. Gist.

import six
class DirMixIn:
    """ Mix-in to make implementing __dir__ method in subclasses simpler
    """

    def __dir__(self):
        if six.PY3:
            return super(DirMixIn, self).__dir__()
        else:
            # code is based on
            # http://www.quora.com/How-dir-is-implemented-Is-there-any-PEP-related-to-that
            def get_attrs(obj):
                import types
                if not hasattr(obj, '__dict__'):
                    return []  # slots only
                if not isinstance(obj.__dict__, (dict, types.DictProxyType)):
                    raise TypeError("%s.__dict__ is not a dictionary"
                                    "" % obj.__name__)
                return obj.__dict__.keys()

            def dir2(obj):
                attrs = set()
                if not hasattr(obj, '__bases__'):
                    # obj is an instance
                    if not hasattr(obj, '__class__'):
                        # slots
                        return sorted(get_attrs(obj))
                    klass = obj.__class__
                    attrs.update(get_attrs(klass))
                else:
                    # obj is a class
                    klass = obj

                for cls in klass.__bases__:
                    attrs.update(get_attrs(cls))
                    attrs.update(dir2(cls))
                attrs.update(get_attrs(obj))
                return list(attrs)

            return dir2(self)

Ответ 2

  • и 3: Да, ваше решение правильно. recarray не определяет __dir__ просто потому, что реализация по умолчанию была в порядке, поэтому они не потрудились ее реализовать, а numpy разработчики не проектировали класс, который должен быть подклассом, поэтому я не понимаю, почему они должны иметь беспокоили.

    Часто бывает плохой подход к подклассам встроенных типов или классов, которые специально не предназначены для наследования, поэтому я предлагаю вам использовать делегирование/состав вместо наследования, за исключением случаев, когда есть определенная причина (например, вы хотите передать его функции numpy, которая с явной проверкой проверяется с помощью isinstance).

  • Нет. Как вы указали в python3, они изменили реализацию так, что есть object.__dir__, но в других версиях python я не вижу ничего, что вы можете сделать. Кроме того, использование recarray с множественным наследованием просто сумасшедшее, все сломается. Многократное наследование должно быть тщательно спроектировано, и обычно классы специально разработаны для его использования (например, микширования). Поэтому я бы не стал рассматривать этот случай, так как тот, кто его попробует, будет укушен другими проблемами.

    Я не понимаю, почему вы должны заботиться о классах, у которых нет __dict__... так как ваш подкласс имеет его, как он должен прерываться? Когда вы измените реализацию подкласса, например. используя __slots__, вы также можете легко изменить __dir__. Если вы хотите избежать переопределения __dir__, вы можете просто определить функцию, которая проверяет на __dict__, а затем на __slots__ и т.д. Однако обратите внимание, что атрибуты могут быть сгенерированы тонким образом с помощью __getattr__ и __getattribute__, и, таким образом, вы просто не может надежно уловить их всех.

Ответ 3

Вы пробовали:

def __dir__(self):
    return sorted(set(
               dir(super(MyRecArray, self)) + \
               self.__dict__.keys() + self.dtype.fields.keys()))