18

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?

Fabian
  • 4,160
  • 20
  • 32
  • 1
    See [`inspect.getmembers()` vs `__dict__.items()` vs `dir()`](http://stackoverflow.com/questions/6761106/inspect-getmembers-vs-dict-items-vs-dir) for some general info about `dir` and related functions. – agf Aug 17 '11 at 16:29

4 Answers4

11

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.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
6

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.
mgilson
  • 300,191
  • 65
  • 633
  • 696
3

According to the python documentation about the dir() method, you can implement a __dir__() method on your object that returns the list of the items you want to see when calling a dir on your object.

The __name__ member is part of some special attributes of python objects, and as says the documentation :

Some of these are not reported by the dir() built-in function.

Cédric Julien
  • 78,516
  • 15
  • 127
  • 132
0

I've encountered this problem when writing pytests. Although, people explain why, no work-around was suggested. The only work-around I found was to move .py files to another directory and then import them.

llincolnatal
  • 81
  • 1
  • 4