Python class members scanning - Why __bases__ recursive scan is required in get members?

StackOverflow https://stackoverflow.com/questions/13154226

سؤال

I found the following function, but I have no idea why additional __bases__ scan is required:

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

The following function is faster and gives same results - so why is the additional __bases__ scan needed at al?

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

Some test code with both new- and old-style classes:

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)

Result:

<type 'type'>
True
<type 'classobj'>
True
هل كانت مفيدة؟

المحلول

Indeed, the dir() function, for classes, already includes all the classes listed in __bases__:

If the object is a type or class object, the list contains the names of its attributes, and recursively of the attributes of its bases.

However, it is possible for a class to override that behaviour by specifying a __dir__ method (via a metaclass, a classmethod is not enough):

If the object has a method named __dir__(), this method will be called and must return the list of attributes.

When a __dir__() method is present, __bases__ is not recursed over:

>>> 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__'])

Thus, the explicit recursion over __bases__ in the getMembersWithBases() class method could be an attempt at bypassing any custom __dir__() implementations.

Otherwise, the recursion over __bases__ is completely redundant.

In my personal opinion, the recursion over __bases__ is redundant even if there are __dir__() methods present in the class hierarchy. In such cases, the __dir__() method override is at fault as that method should recurse over the classes listed in __bases__ to correctly mimic the behaviour of the dir() function.

To be honest, I suspect that the author of the function that you found was not aware of the recursive nature of dir(), and added the __bases__ recursion needlesly, not as a means to bypass custom __dir__() methods.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top