重写dir方法的正确方法是什么?

2024-10-05 15:24:34 发布

您现在位置:Python中文网/ 问答频道 /正文

这个问题更多的是关于__dir__,而不是关于numpy

我有一个numpy.recarray(在python 2.7,numpy 1.6.2中)的子类,我注意到当dir处理对象时,recarray的字段名没有列出(因此I python的自动完成不起作用)。

为了修复它,我尝试重写子类中的__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. 有没有办法让它在一般情况下起作用?在我看来,它不适用于多重继承(破坏super-调用链),当然,对于没有__dict__的对象。。。
  3. 你知道为什么recarray不支持以列出其字段名开头吗?仅仅是疏忽?

Tags: 对象selfnumpyreturndefdirkeys子类
3条回答
  1. 3:是的,你的解决方案是正确的。recarray没有定义__dir__仅仅是因为默认实现是好的,所以他们没有麻烦实现它,而且numpy的开发人员没有设计要子类化的类,所以我不明白他们为什么要麻烦。

    对不是专门为继承而设计的内置类型或类进行子类划分通常是一个坏主意,因此我建议您使用委托/组合而不是继承,除非有特定的原因(例如,您希望将其传递给一个numpy函数,该函数使用isinstance进行异常检查)。

  2. 不,正如您在python3中指出的那样,他们更改了实现,因此有一个object.__dir__,但是在其他python版本上,我看不到您能做的任何事情。同样,将recarray与多重继承一起使用简直是疯了,事情会破裂。多重继承应该仔细设计,通常类是专门设计用于它的(例如mix-ins)。所以我不会费心处理这个案子,因为不管是谁尝试,都会被其他问题所困扰。

    我不明白为什么你应该关心那些没有__dict__的类。。。既然你的子类有了它,它该怎么破?当您要更改子类实现时,例如使用__slots__,您也可以轻松地更改__dir__。如果您想避免重新定义__dir__,可以简单地定义一个函数来检查__dict__,然后检查__slots__等。但是,请注意,属性可以通过__getattr____getattribute__以微妙的方式生成,因此您不能可靠地捕获所有属性。

你试过了吗:

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

Python 2.7+,3.3+类mixin,它简化了子类中方法的实现。希望能有所帮助。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)

相关问题 更多 >