¿Por qué cls .__ name__ no aparece en dir ()?

Digamos que tengo una clase simple:

class Foobar(object): pass 

Si uso dir(Foobar) , obtendré el siguiente resultado:

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

Aunque no aparece en la salida de dir() , puedo acceder a __name__ :

 Foobar.__name__ 

y consigue Foobar .

¿Por qué Python se comporta de esa manera?

No se garantiza que dir devuelva todos los atributos posibles. De los documentos :

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.

De acuerdo con la documentación de python sobre el método dir () , puede implementar un método __dir __ () en su objeto que devuelve la lista de los elementos que desea ver al llamar a un directorio en su objeto.

El miembro __name__ es parte de algunos atributos especiales de objetos python, y como dice la documentación:

Algunos de estos no son reportados por la función incorporada dir ().

Las respuestas a un duplicado reciente de esta pregunta me han llevado a querer dar más detalles sobre esta respuesta. Primero, la respuesta aceptada es correcta. dir simplemente llama al método __dir__ hook y el método predeterminado __dir__ hook por lo general solo devuelve las claves de __dict__ del objeto.

Segundo, __name__ no está en el object.__dict__ y tampoco se incluye en el __dict__ de las subclases. Si no está en __dict__ , ¿dónde está y cómo se busca?

Como entiendo el código fuente , hay una serie de descriptores que se establecen por type durante la creación de la clase. Uno de estos descriptores es __name__ . El __name__ getter llama a type_name y el setter llama a type_set_name . Estos captadores / definidores realmente obtienen / establecen el nombre en la ranura tp_name de la instancia de objeto de tipo (es decir, la clase). Dado que esto es en realidad una ranura especial en el objeto de tipo, en realidad no vive en la clase __dict__ y, por lo tanto, no se informa mediante vars o dir .

Tenga en cuenta que dir para las instancias de objetos de tipo (es decir, clases) no agrega específicamente miembros del atributo __class__ porque “los métodos que pertenecen a la metaclase probablemente serían más confusos que útiles” .

Entonces, pongamos todo esto junto.

  • object no tiene un atributo __name__ pero el type sí.
  • El atributo __name__ type es en realidad un descriptor.
  • Como la metaclase del object es de type , cuando se solicita el object.__name__ , se invoca el descriptor __name__ tipo.
  • El descriptor de name busca el nombre en la ranura type->tp_name . La ranura type->tp_name se llena con el type.__new__ cuando se crea la clase.
  • object.__dir__ está escrito específicamente para no informar las propiedades en la metaclase de la clase, ya que los desarrolladores de python creen que haría más daño que bien.