Сканирование членов класса Python - Почему __bases__ рекурсивное сканирование требуется для получения членов?

Я нашел следующую функцию, но я понятия не имею, почему дополнительные __bases__ требуется сканирование:

def getMembersWithBases(classType):
    members = set(dir(classType))
    # recursive bases scan
    for baseClassType in classType.__bases__:
        members.update(getMembersWithBases(baseClassType))
    return members

Следующая функция быстрее и дает те же результаты - так почему же дополнительная __bases__ сканирование нужно на все?

def getMembers(classType):
    members = set(dir(classType))
    return members

Некоторый тестовый код с классами как нового, так и старого стиля:

class Father(object):
    def testFather():
        pass

class Mother(object):
    def testMother():
        pass

class Child(Father, Mother):
    def testChild():
        pass

print type(Child)
print getMembers(Child) == getMembersWithBases(Child)                                             

class Father:
    def testFather():
        pass

class Mother:
    def testMother():
        pass

class Child(Father, Mother):
    def testChild():
        pass

print type(Child)
print getMembers(Child) == getMembersWithBases(Child)

Результат:

<type 'type'>
True
<type 'classobj'>
True

1 ответ

Решение

Действительно, dir() Функция для классов уже включает в себя все классы, перечисленные в __bases__:

Если объект является объектом типа или класса, список содержит имена его атрибутов и рекурсивные атрибуты его баз.

Однако класс может переопределить это поведение, указав __dir__ Метод (через метакласс метод класса не достаточно):

Если у объекта есть метод с именем __dir__(), этот метод будет вызван и должен вернуть список атрибутов.

Когда __dir__() метод присутствует, __bases__ не повторяется:

>>> class Foo(object):
...     def spam(self): pass
... 
>>> class Bar(object):
...     def ham(self): pass
... 
>>> class CustomDirMetaclass(type):
...     def __dir__(cls):
...         return ['eggs']
... 
>>> class Baz(Foo, Bar):
...     __metaclass__ = CustomDirMetaclass
...     def eggs(self): pass
... 
>>> dir(Baz)
['eggs']
>>> getMembers(Baz)
set(['eggs'])
>>> getMembersWithBases(Baz)
set(['__module__', '__getattribute__', 'eggs', '__reduce__', '__subclasshook__', '__dict__', '__sizeof__', '__weakref__', '__init__', 'ham', '__setattr__', '__reduce_ex__', '__new__', 'spam', '__format__', '__class__', '__doc__', '__delattr__', '__repr__', '__hash__', '__str__'])

Таким образом, явная рекурсия над __bases__ в getMembersWithBases() Метод класса может быть попыткой обойти любой пользовательский __dir__() Реализации.

В противном случае рекурсия закончена __bases__ полностью излишним.

По моему личному мнению, рекурсия закончилась __bases__ является избыточным, даже если есть __dir__() методы, присутствующие в иерархии классов. В таких случаях __dir__() переопределение метода является ошибкой, так как этот метод должен проходить через классы, перечисленные в __bases__ правильно имитировать поведение dir() функция.

Если честно, я подозреваю, что автору функции, которую вы обнаружили, не было известно о рекурсивной природе dir() и добавил __bases__ рекурсия иголки, а не как способ обойти обычай __dir__() методы.

Другие вопросы по тегам