2013-03-19 1 views
21

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

У меня есть подкласс numpy.recarray (в Python 2.7, Numpy 1.6.2), и я заметил recarray «s имена полей не отображаются, когда 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__'. (я нашел here это должно реально работать в питона 3.3 ...)

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

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

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

Вопросы:

  1. Является ли последнее решение правильно в моем случае, то есть для подкласса recarray?
  2. Есть ли способ заставить его работать в общем случае? Мне кажется, что это не сработает с множественным наследованием (разрывая цепочку super), и, конечно же, для объектов без __dict__ ...
  3. Вы знаете, почему recarray не поддерживает перечисление имен своих полей, чтобы начать с? простой надзор?
+0

Я попытался подкласс 'recarray' и' dir' ** делает ** показать 'recarray' атрибуты. Можете ли вы объяснить, что, по-вашему, отсутствует? – Bakuriu

+0

'self.dtype.fields.keys()' – shx2

ответ

3

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) 
3
  1. и 3: Да ваше решение является правильным. recarray не определяет __dir__ просто потому, что реализация по умолчанию была в порядке, поэтому разработчики не пытались ее реализовать, а разработчики numpy не проектировали класс, подлежащий подклассу, поэтому я не понимаю, почему они должны были беспокоиться.

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

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

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

+1

Спасибо за подробный ответ. Я принимаю вашу точку зрения о множественном наследовании. В моем вопросе №2 я спрашиваю о лучшем способе, в общем случае, реализовать следующий псевдокод: 'return dir_listing (my_super_object) + specific_dir_listing (self)'. Если мой суперкласс переопределяет '__getattr__', я полагаюсь на него, чтобы правильно переопределить' __dir__'. Если мой подкласс делает, я включаю эту часть в 'specific_dir_listing (self)'. Вопрос в том, как их получить. – shx2

1

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

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