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.