Accessing attributes of a class

How do I access the attributes of a class, such as, __bases__, __name__, __qualname__?

When I do this,

class C:
  pass
dir(C)

then it gives me,

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
 '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
 '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
 '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
 '__str__', '__subclasshook__', '__weakref__']

how do I find __bases__, __name__, __qualname__?

See the warning at

Note

Because dir() is supplied primarily as a convenience for use at an interactive prompt, it tries to supply an interesting set of names more than it tries to supply a rigorously or consistently defined set of names, and its detailed behavior may change across releases. For example, metaclass attributes are not in the result list when the argument is a class.

I’m not sure why these exactly are not included, but the general answer is that dir() doesn’t give you everything and this is expected. In what context is this a problem for you? I suspect an XY question.

I wanted to access the base classes of a class in a class decorator.

def no_multiple_inheritance(cls):
  if(len(cls.__bases__)>1):
    raise Exception('no multiple inheritance allowed')
  return cls
@no_multiple_inheritance
class C(A, B):
  pass

but when I listed dir(cls) in the class decorator, I could not find __bases__ and had to search for how to find the base classes of a class.

dir() avoids confusion by focusing on the attributes of the class that are applicable to its instances. For example, a class has a __call__() method that’s implemented by its metaclass, and it may also implement a __call__() method for its instances.

The __bases__ attribute is a descriptor that’s provided by the metaclass type, which applies only to classes. For example:

>>> C = type('C', (), {})
>>> D = type('D', (C,), {})
>>> type(D) is type
True
>>> '__bases__' in dir(type)
True
>>> type(type.__dict__['__bases__'])
<class 'getset_descriptor'>
>>> type.__dict__['__bases__'].__get__(D)
(<class '__main__.C'>,)
>>> type.__dict__['__bases__'].__get__(type)
(<class 'object'>,)
>>> type.__dict__['__bases__'].__get__(D())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor '__bases__' for 'type' objects doesn't apply to a 'D' object

If you want the bases of a class, you should inspect it’s Method Resolution Order which shows the full sequence of all its parents, grandparents, great-grandparents etc.

MyClass.mro()

If you want just the immediate parents:

MyClass.__bases__    

What have you got against multiple inheritance? MI is fine when used correctly.

You have:

@no_multiple_inheritance
class C(A, B):
    pass

That’s very odd. If you don’t want MI, just don’t use it: just don’t
write class C(A, B).

I think so using dir(type(cls)), gives the required list of attributes

def deco(cls):
  print(dir(type(cls)))
  print(cls.__flags__, cls.__name__)
  return cls
@deco
class C:
  pass
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__',
 '__class__', '__delattr__', '__dict__', '__dictoffset__', '__dir__', '__doc__',
 '__eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__',
 '__hash__', '__init__', '__init_subclass__', '__instancecheck__',
 '__itemsize__', '__le__', '__lt__', '__module__', '__mro__', '__name__',
 '__ne__', '__new__', '__prepare__', '__qualname__', '__reduce__',
 '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
 '__subclasscheck__', '__subclasses__', '__subclasshook__',
 '__text_signature__', '__weakrefoffset__', 'mro']
284161 C

Why do you care so much about dir()?

You already know what attribute to look at to get the base classes of a class: cls.__bases__. Why do you keep going back to dir()?

dir() is designed as a convenience method to be used at the interactive interpreter, and frankly it is a flawed tool. But flawed or not, it does the job it is meant to do, and you don’t need it to access __bases__.

for __bases__, maybe one could say, that I could have thought of it without using dir; but for other attributes like __qualname__, __flags__, __text_signature__, there is less chance for me to know that they exist, and I would have to use dir to check for the existence of such attributes.

You can’t rely on dir() to show all attributes of an object. Maybe it will; maybe it won’t.

Again, it would be confusing if dir() on a class reported the methods and properties that its metaclass defines for the class object in addition to the methods and properties that the class defines for its instances. The dir() result is just a list of names, so there’s no way to know which is a method or property of the class itself versus a definition for its instances, or if in fact the same name exists for both.

If you need to list the methods and properties of class SomeClass that apply to just the class, and which aren’t accessible to its instances, use dir(type(SomeClass)). For example:

class SomeMetaClass(type):
    def method(self):
        pass
 
class SomeClass(metaclass=SomeMetaClass):
    pass
>>> ['method' in dir(SomeClass)]
[False]
>>> ['method' in dir(type(SomeClass))]
[True]

Keep in mind that __dir__() can be overridden to change the default behavior. For example, it might be overridden to support dynamic attributes. The following example uses a metaclass of a metaclass in order to integrate a dynamic attribute of the class seamlessly into existing dir() behavior of the class.

class SomeMetaMetaClass(type):
    def __dir__(self):
        return super().__dir__() + ['spam']

class SomeMetaClass(type, metaclass=SomeMetaMetaClass):
    def __getattr__(self, name):
        if name == 'spam':
            return 'eggs'
        raise AttributeError

class SomeClass(metaclass=SomeMetaClass):
    pass
>>> SomeClass.spam
'eggs'
>>> ['spam' in dir(SomeClass)]
[False]
>>> ['spam' in dir(type(SomeClass))]
[True]

Here is the solution. You need to retrieve names of special attributes from the __dict__ of class type see below dir(type(cls)), then with these names, you can iterate and retrieve the values.

def deco(cls):
    special_attrs = dir(type(cls))
 
    for attr_name in special_attrs:
        if hasattr(cls, attr_name):
            try:
                attr_value = getattr(cls, attr_name)
                print(f"{attr_name}: {attr_value}")
            except AttributeError:
                print(f"{attr_name}: unable to retrieve")
 
    return cls

class Foo:
    a = 11
 
# I change the __name__ in the class Foo
Foo.__name__ = 'Bar'
 
@deco
class Foo(Foo):
    pass
 
foo_instance = Foo()

In the example as above I was focused on getting __name__ attribute with the correct value (here ‘Bar’).