Obtenga una lista completa de todos los atributos de clase posibles

¿Hay alguna forma, dada una clase simple, de generar todos los atributos posibles para ella? Atributos estándar como __class__ y __doc__ y atributos especiales de solo lectura como __mro__ , __bases__ et al. En general, todos los atributos presentes?

Teniendo en cuenta el caso más simplista para una clase:

 class myClass: pass 

El dir() , vars() y inspect.getmembers() todos excluyen ciertos atributos builtin . La lista más completa se ofrece utilizando myClass.__dir__(MyClass) que, al agregar atributos integrados, excluye los atributos definidos por el usuario de MyClass , por ejemplo:

 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'} 

Según una de las preguntas similares agregadas, esto no es posible. Si actualmente no es posible, ¿cuál es la razón detrás de “ocultar” ciertos atributos como __bases__ (desde llamadas estándar a dir(), vars() & inspect y no unos como __name__ ?


Preguntas similares:

dir() es básicamente un método conveniente, no se supone que devuelva todo, lo que básicamente hace es que recursivamente devuelve todo lo que se encuentra en el diccionario de una clase y sus bases.

La implementación de PyPy de dir() es bastante fácil de entender:

 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 

Como cada clase básicamente hereda del object , verá que se incluyen algunos métodos de desagregación porque en realidad son parte del diccionario del object :

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

Ahora, ¿qué hay de __bases__ y otros artículos que faltan?

En primer lugar, el object sí es una instancia de algo, bueno , en realidad es un desastre :

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

Entonces, todos los atributos como __bases__ , __ge__ etc son en realidad parte del 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__'] 

Por lo tanto, cuando hacemos A.__bases__ estamos buscando un descriptor en el tipo de A , es decir, type :

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

Entonces, como A es una instancia de type estos métodos no forman parte de su propio diccionario sino del tipo de diccionario.

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

Como, el type es una subclase de object , la llamada dir() en type contendrá algunos elementos del object :

 >>> set(dir(type)) - set(type.__dict__) set(['__reduce_ex__', '__str__', '__format__', '__reduce__', '__class__', '__subclasshook__', '__sizeof__'])