覆盖__dir__方法的正确方法是什么?

时间:2013-03-19 18:39:57

标签: python inheritance python-2.7 introspection

此问题更多地是关于__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__'。 (我发现here这实际上应该在python 3.3中工作......)

作为解决方法,我尝试了:

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

据我所知,这个有效,但当然不是那么优雅。

问题:

  1. 后一种解决方案在我的情况下是正确的,即对于recarray
  2. 的子类
  3. 有没有办法让它在一般情况下有效?在我看来它不适用于多重继承(打破super - 调用链),当然,对于没有__dict__的对象...
  4. 您知道为什么recarray不支持列出其字段名称吗?只是疏忽?

3 个答案:

答案 0 :(得分:4)

Python 2.7 +,3.3 + class 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)

答案 1 :(得分:3)

  1. 和3:是的,你的解决方案是正确的。 recarray没有定义__dir__只是因为默认实现没问题,所以他们没有理由实现它,numpy的开发人员没有将类设计为子类,所以我不明白为什么他们应该打扰。

    继承内部类型或类并非专门为继承而设计通常是一个坏主意,因此我建议你使用委托/组合而不是继承,除非有特殊原因(例如你想要的)将其传递给numpy函数,该函数使用isinstance进行明确检查。

  2. 没有。正如你在python3中指出的那样,他们改变了实现,以便有一个object.__dir__,但在其他python版本上,我看不到你可以做的任何事情。此外,再次使用具有多重继承的recarray只是疯狂,打破。应仔细设计多重继承,并且通常专门设计类以与其一起使用(例如混合)。所以我不打算处理这个案子,因为无论谁尝试它都会被其他问题所困扰。

    我不明白为什么你应该关心没有__dict__的类...因为你的子类有它应该如何破坏?当您更改子类实现时,例如使用__slots__您也可以轻松更改__dir__。如果您想避免重新定义__dir__,您只需定义一个检查__dict__然后检查__slots__等的函数。请注意,可以使用__getattr__以微妙的方式生成属性和__getattribute__因此你只是不能可靠地捕获所有这些。

答案 2 :(得分:1)

你试过了吗?

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