Difference between dir(…) and vars(…).keys() in Python?
Python objects usually store their instance variables in a dictionary that belongs to the object (except for slots). vars(x)
returns this dictionary (as does x.__dict__
). dir(x)
, on the other hand, returns a dictionary of x
's "attributes, its class's attributes, and recursively the attributes of its class's base classes."
When you access an object's attribute using the dot operator, Python does a lot more than just look up the attribute in that objects dictionary. A common case is when x
is an instance of class C
and you call its method m
:
class C:
def m(self):
print("m")
x = C()
x.m()
The method m
is not stored in x.__dict__
. It is an attribute of the class C
.
When you call x.m()
, Python will begin by looking for m
in x.__dict__
, but it won't find it. However, it knows that x
is an instance of C
, so it will next look in C.__dict__
, find it there, and call m
with x
as the first argument.
So the difference between vars(x)
and dir(x)
is that dir(x)
does the extra work of looking in x
's class (and its bases) for attributes that are accessible from it, not just those attributes that are stored in x
's own symbol table. In the above example, vars(x)
returns an empty dictionary, because x
has no instance variables. However, dir(x)
returns
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__le__', '__lt__', '__module__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__',
'm']
The documentation has this to say about dir
:
Without arguments, return the list of names in the current local scope. With an argument, attempt to return a list of valid attributes for that object.
And this about vars
:
Without arguments, return a dictionary corresponding to the current local symbol table. With a module, class or class instance object as argument (or anything else that has a
__dict__
attribute), returns a dictionary corresponding to the object’s symbol table.
If you don't see the difference, maybe this will show you more (grouped for easier reading):
>>> dir(list)
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__',
'__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__',
'__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__',
'__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__',
'__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend',
'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> vars(list).keys()
dict_keys(['__repr__',
'__hash__',
'__getattribute__',
'__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__',
'__iter__',
'__init__',
'__len__',
'__getitem__', '__setitem__', '__delitem__',
'__add__', '__mul__', '__rmul__', '__contains__', '__iadd__', '__imul__',
'__new__',
'__reversed__', '__sizeof__',
'clear', 'copy', 'append', 'insert', 'extend', 'pop', 'remove', 'index', 'count', 'reverse', 'sort',
'__class_getitem__',
'__doc__'])
If you don't feel like reading through that, dir
includes these attributes while vars
does not:
>>> set(dir(list)) - vars(list).keys()
{'__class__', '__delattr__', '__dir__', '__format__', '__init_subclass__',
'__reduce__', '__reduce_ex__', '__setattr__', '__str__', '__subclasshook__'}
Note also that dir()
's output is sorted alphabetically, whereas vars()
's output is sorted by the order the attributes were defined in.