¿Por qué no se muestra __mro__ en dir (MyClass)?

class MyClass(object): pass print MyClass.__mro__ print dir(MyClass) 

Salida:

 (, ) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] 

¿Por qué no se enumera __mro__ con dir() ?

De la documentación de Python:

Debido a que dir () se proporciona principalmente como una conveniencia para el uso en una solicitud interactiva, trata de proporcionar un conjunto interesante de nombres más de lo que trata de proporcionar un conjunto de nombres definido de manera rigurosa o consistente, y su comportamiento detallado puede cambiar entre las versiones. Por ejemplo, los atributos de metaclase no están en la lista de resultados cuando el argumento es una clase.

__mro__ es un atributo de solo lectura que se usa para determinar la resolución del método en caso de que su clase herede de varias clases base. Si desea personalizar este comportamiento, debe usar una metaclase (un tipo especial de objeto cuyo propósito es crear instancias de clase) que invalida el método mro() . __mro__ se __mro__ sin cambios en cualquier caso.

Hace poco me preguntaba lo mismo. Estaba buscando una respuesta más en línea con “¿Qué mro la implementación de Python que hace que mro / __mro__ no __mro__ en la lista de __mro__ ? ¿Y en qué puedo confiar en la lista de __mro__ ?” que simplemente “¿Cómo justifica la documentación de Python la no inclusión de mro en dir ?” No me gusta cuando mi expectativa del comportamiento de un lenguaje de progtwigción no coincide con su comportamiento real porque significa que mi comprensión del lenguaje es incorrecta, a menos que sea un error como urllib2.escape . Así que cavé un poco, hasta que encontré la respuesta.

La línea adolfopa citada de la documentación en los comentarios anteriores es buena para explicar el comportamiento de dir.

“Si el objeto es un objeto de tipo o clase, la lista contiene los nombres de sus atributos y recursivamente los atributos de sus bases”.

¿Qué significa esto? dir recursivamente reúne atributos del __dict__ de una clase y cada una de sus superclases __dict__ .

 set(dir(object)) == set(dict(object.__dict__).keys() #True class A(object): ... class B(object): ... class C(B): ... class D(C,A): ... set(dir(D)) == set(D.__dict__.keys()) + set(C.__dict__.keys()) \ + set(B.__dict__.keys()) + set(A.__dict__.keys()) \ + set(object.__dict__.keys()) #True 

La razón dir(object) no enumera __mro__ / mro es que no son atributos de objeto. Son atributos de type . Cada clase que no define su propia __metaclass__ es una instancia de type . La mayoría de las metaclases type subclase. Las instancias de tales metaclases son también instancias de tipo. MyClass.__mro__ es el mismo que el type.__getattribute__(MyClass,'__mro__') .

La forma en que Python implementa las clases crea necesariamente una ligera anomalía con respecto a cómo funciona dir.

Normalmente, dir(MyClass) == dir(MyClass(*requiredparameters)) #True .

Sin embargo, dir(type) == dir(type(*requiredparameters)) #False , pero la única forma en que esto podría ser de otra manera sería si el type.__dict__ y dir fueran los mismos. Esto es, evidentemente, no es el propósito de dir .

¡Pero espera! dir es creado por una sum recursiva, ¿por qué no podemos simplemente cambiar la parte final de la misma para que dir(object) ya no sea solo object.__dict__.keys() sino que se convierta en object.__dict__.keys() + type.__dict__.keys() . De esa manera, tendría mro / __mro__ y todos los demás atributos que tiene el objeto de clase? Ah, pero esos serían atributos del objeto de clase, no de la clase. Bueno, ¿cuál es la diferencia?

Considerar

 list.__mro__ #(, ) 

Mientras

 [].__mro__ # Traceback (most recent call last): # File "", line 1, in  # AttributeError: 'list' object has no attribute '__mro__' 

Ahora, estamos en un buen lugar para responder qué podemos contar con dir para listar y con qué podemos contar dir para no listar. La respuesta simple es la que ya hemos cubierto. Enumera de forma __dict__ todas las claves en el __dict__ la clase más todas las claves en cada una de las superclases ‘ __dict__ ‘. Para una instancia, también incluye todos los parámetros en __dict__ la instancia. Para completar el espacio negativo, no muestra nada definido en __getattr__ o en nada en __getattribute__ si no está también en __dict__ . Tampoco enumera ningún atributo del tipo / metatipo.


Otra cosa que siento que debo señalar: la respuesta de Dan, que es la respuesta aceptada en el momento en que escribo esto, contiene información que es inexacta o, al menos, engañosa.

Los atributos de los objetos incorporados no se pueden establecer, por lo que, en cierto sentido, el type.__mro__ es de ‘solo lectura’, pero solo de la misma manera que list.append es o type.mro es, en type.mro .

MyClass.__mro__ = "Hello world!" no causa un error Simplemente no afecta el orden de resolución del método definido en el type . Por lo tanto, es posible que no tenga el efecto que esperaba si intentara modificar ese comportamiento. (Lo que hace es hacer que MyClass(*requiredparameters).__mro__ sea "Hello World!" Que debería haber sido lo que se esperaba, ya que así es como funciona la definición de atributos de las clases en python.) También puede anular __mro__ when usted es un tipo de subclasificación para crear una metaclase. Si no lo anula, se hereda, como cualquier otra cosa que no anula. (Si está creando una metaclase que no es una subclase de tipo y que no es una función que devuelve una instancia de tipo, probablemente ya sepa exactamente lo que está haciendo lo suficientemente bien como para no preocuparse por esto, pero __mro__ no se heredaría ya que no es un type subclasificación)

Según los documentos (describiendo el comportamiento de super):

El atributo __mro__ del tipo enumera el orden de búsqueda de resolución de método utilizado por getattr () y super (). El atributo es dynamic y puede cambiar siempre que se actualice la jerarquía de herencia.

Por lo tanto, la modificación directa de __mro__ debe comportarse como cabría esperar, de la misma manera que la modificación de mro . Sin embargo, normalmente es más fácil obtener el comportamiento que desea al anular la función. (Piense en lo que debe hacer para manejar las subclases correctamente).