5

Is there a way, given a simple Class, to output all the possible attributes for it? Standard attributes like __class__ and __doc__ and special read only attributes like __mro__, __bases__ et al. Generally, all present attributes?

Considering the most simplistic case for a Class:

class myClass:
    pass

The dir(), vars() and inspect.getmembers() all exclude certain builtin attributes. The most complete list is offered by using myClass.__dir__(MyClass) which, while adding built in attributes, excludes user defined attributes of MyClass, for example:

In [3]: set(MyClass.__dir__(MyClass)) - set(dir(MyClass))
Out[3]: 
{'__abstractmethods__', '__base__', '__bases__',
 '__basicsize__', '__call__', '__dictoffset__',
 '__flags__', '__instancecheck__', '__itemsize__',
 '__mro__', '__name__', '__prepare__', '__qualname__',
 '__subclasscheck__', '__subclasses__', '__text_signature__',
 '__weakrefoffset__', 'mro'}

According to one of the added similar questions, this is not possible. If presently still not possible, what is the rationale behind "hiding" certain attributes like __bases__ (from standard calls to dir(), vars() & inspect and not ones like __name__?


Similar Questions:

Community
  • 1
  • 1
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • 3
    Possible duplicate of [How to get a complete list of object's methods and attributes?](http://stackoverflow.com/questions/191010/how-to-get-a-complete-list-of-objects-methods-and-attributes) – ArtOfWarfare Oct 12 '15 at 18:30
  • You didn't even read my question, did you? – Dimitris Fasarakis Hilliard Oct 12 '15 at 18:31
  • The 2.x answer is not outdated. You can write `__getattr__` to do literally anything. I have several classes where `__getattr__` says that a URL needs to be queried - there's no way to know in advance what that URL will return or not, so there's no way of generating an exhaustive list of attributes. `__dir__` will return most attributes most of the time, but because of the dynamic nature of getting and setting attributes, there's really no way to get the complete list all of the time. As for why `__bases__` is hidden but not `__name__`, I couldn't tell you. – ArtOfWarfare Oct 12 '15 at 18:32
  • This is why the question has been tagged as `Python 3.x` and not `Python 2.x`. – Dimitris Fasarakis Hilliard Oct 12 '15 at 18:33
  • 1
    That other question was generally about Python. It applies to 2.x and 3.x. This question is a sub-question of that. The answer is already there. – ArtOfWarfare Oct 12 '15 at 18:35

1 Answers1

4

dir() is basically a convenience method, it is not supposed to return everything, what it basically does is that it recursively returns everything found in the dictionary of a class and its bases.

PyPy's implementation of dir() is quite easy to understand:

def dir(*args):
    ...
    elif isinstance(obj, (types.TypeType, types.ClassType)):
        # Don't look at __class__, as metaclass methods would be confusing.
        return sorted(_classdir(obj))
    ...

def _classdir(klass):
    """Return a set of the accessible attributes of class/type klass.

    This includes all attributes of klass and all of the base classes
    recursively.
    """
    names = set()
    ns = getattr(klass, '__dict__', None)
    if ns is not None:
        names.update(ns)
    bases = getattr(klass, '__bases__', None)
    if bases is not None:
        # Note that since we are only interested in the keys, the order
        # we merge classes is unimportant
        for base in bases:
            names.update(_classdir(base))
    return names

As each class basically inherits from object you will see some dunder methods included because they are actually part of object's dictionary:

>>> class A(object):
    pass
...
>>> set(dir(A)) == set(list(object.__dict__) + list(A.__dict__))
True

Now what about __bases__ and other missing items?

First of all object itself is an instance of something, well it's bit of a mess actually:

>>> isinstance(type, object)
True
>>> isinstance(object, type)
True
>>> issubclass(type, object)
True
>>> issubclass(object, type)
False
>>> type.mro(object)
[<type 'object'>]
>>> type.mro(type)
[<type 'type'>, <type 'object'>]

So, all of the attributes like __bases__, __ge__ etc are actually part of type:

>>> list(type.__dict__)
['__module__', '__abstractmethods__', '__getattribute__', '__weakrefoffset__', '__dict__', '__lt__', '__init__', '__setattr__', '__subclasses__', '__new__', '__base__', '__mro__', 'mro', '__dictoffset__', '__call__', '__itemsize__', '__ne__', '__instancecheck__', '__subclasscheck__', '__gt__', '__name__', '__eq__', '__basicsize__', '__bases__', '__flags__', '__doc__', '__delattr__', '__le__', '__repr__', '__hash__', '__ge__']

Hence when we do A.__bases__ we are actually looking up a descriptor on type of A, i.e type:

>>> A.__bases__
(<type 'object'>,)
>>> type(A).__dict__['__bases__'].__get__(A, type)
(<type 'object'>,)

So, as A is an instance of type these methods are not part of its own dictionary but its type's dictionary.

>> class A(object):
...     spam = 'eggs'
...
>>> a = A()
>>> a.foo = 100
>>> a.bar = 200
>>> a.__dict__
{'foo': 100, 'bar': 200}
>>> A.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'spam': 'eggs'})

As, type is a subclass of object, the dir() call on type will contain some items from object:

>>> set(dir(type)) - set(type.__dict__)
set(['__reduce_ex__', '__str__', '__format__', '__reduce__', '__class__', '__subclasshook__', '__sizeof__'])
Community
  • 1
  • 1
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • I see, so my initial assumption that they are hidden is pretty wrong, attributes like `__bases__` are just inherited from `type` and so calling a function like `dir()` on a subclass of `type` just won't return the inherited attributes. Also, regarding the existence of a function that fetches everything, I'm guessing the short answer is that if I want a function like that, better implement it myself because it doesn't exist, right? – Dimitris Fasarakis Hilliard Oct 12 '15 at 21:02
  • @DimitrisJim Almost correct, but "`dir()` on a subclass of type" should be "`dir()` on an instance of `type`", for example if you create a metaclass that has some attributes then its instances(aka classes) won't have those attributes visible to us but the attribute access will still work. No, there's no such function available, at least not in standard library. – Ashwini Chaudhary Oct 13 '15 at 05:42
  • O.k, gotcha with that. The only thing I still don't get (which maybe I should ask as another question) is, given again an empty class definition, why certain inherited attributes from `type` like `__ ge__` are visible in `__dict__` and or `__dir__` and others like `__bases__` are not. – Dimitris Fasarakis Hilliard Oct 13 '15 at 07:22
  • 1
    @DimitrisJim Because they are part of object's `__dict__`, and other properties like `__bases__` are not: `set(dir(A)) == set(list(object.__dict__) + list(A.__dict__))`. **Python 2:** http://ideone.com/TR0YPh ; **Python 3:** http://ideone.com/tmlHfW – Ashwini Chaudhary Oct 13 '15 at 08:51
  • Thanks, Ashwini. I think I've got it. I'll ask a related question if I get lost again. Good day! – Dimitris Fasarakis Hilliard Oct 13 '15 at 10:19