Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does cls.__name__ not appear in dir()?

Tags:

python

Let's say I have a simple class:

class Foobar(object):
    pass

If I use dir(Foobar), I'll get the following output:

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

Even though it does not appear in the output of dir(), I can access __name__:

Foobar.__name__

and get Foobar.

Why does Python behave that way?

like image 423
Fabian Avatar asked Aug 17 '11 16:08

Fabian


2 Answers

dir is not guaranteed to return all possible attributes. From the docs:

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.

like image 156
unutbu Avatar answered Sep 21 '22 18:09

unutbu


Answers on a recent dupe of this question have lead me to want to elaborate on this answer a little bit more. First, the accepted answer is correct. dir simply calls the __dir__ hook method and the default __dir__ hook method usually just returns the keys of the object's __dict__.

Second, __name__ isn't in object.__dict__ and it doesn't get put in the __dict__ of subclasses either. If it isn't in __dict__, where is it and how does it get looked up?

As I understand the source code, there are a number of descriptors which are set by type during class creation. One of these descriptors is __name__. The __name__ getter calls type_name and the setter calls type_set_name. These getters/setters actually get/set the name in the tp_name slot of the type object instance (i.e. the class). Since this is actually a special slot on the type object, it doesn't actually live in the class __dict__ and therefore it doesn't get reported by vars or dir.

Note that dir for type object instances (i.e. classes) specifically does not add members from the from the __class__ attribute because "methods belonging to the metaclass would probably be more confusing than helpful".

So, let's put this all together.

  • object doesn't have a __name__ attribute but type does.
  • type's __name__ attribute is actually a descriptor.
  • Since object's metaclass is type, when object.__name__ is requested, type's __name__ descriptor is invoked.
  • The name descriptor looks up the name in the type->tp_name slot. The type->tp_name slot is populated by type.__new__ when the class is created.
  • object.__dir__ is specifically written to not report properties on the class's metaclass since the python devs believe that would do more harm than good.
like image 31
mgilson Avatar answered Sep 18 '22 18:09

mgilson