Difference between dir(…) and vars(…).keys() in Python?
Solution 1
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']
Solution 2
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.
Solution 3
Apart from Answers given, I would like to add that, using vars() with instances built-in types will give error, as instances builtin types do not have __dict__
attribute.
eg.
In [96]: vars([])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-96-a6cdd8d17b23> in <module>()
----> 1 vars([])
TypeError: vars() argument must have __dict__ attribute
Related videos on Youtube
Bruno Feroleto
Director, Head of Data Sourcing in an asset management firm. Open-source programmer. Piano music composer. Board game enthusiast. Former Science Advisor in an asset management firm. Former Chief Data Scientist of a large multinational. Former head of Data Science for a startup. Former physicist (quantum mechanics, gravitational waves). Former Science & IT consultant. See LinkedIn profile. Author of the error propagation program "uncertainties", of the real time annotation program realtime_annotate and of the Markdown conversion program md_to_bgg. Have been teaching Python to graduate, doctoral and post-doctoral students, engineers and researchers since 2009. Have been loving science since 1980, and practicing it since 1998 (PhD in quantum physics). Good code design is a major priority for me. Started programming in 1983. Worked with a dozen programming languages (imperative [Python, Fortran, Perl, Pascal, BASIC, C, C++,…], functional [Caml], mathematical (Mathematica, Maple, IDL), stack-based [Postscript, HP RPL], constraint-based [Prolog], flow-based [LabView], and assembly [Motorola 68000, HP Saturn]). Have been working with Unix since 1994. Started programming in Python in 2006, and still loving it!
Updated on July 05, 2022Comments
-
Bruno Feroleto 6 months
Is there a difference between
dir(…)
andvars(…).keys()
in Python?(I hope there is a difference, because otherwise this would break the "one way to do it" principle... :)
-
Ethan Furman about 11 yearsTo be clear, the principle is "One Obvious way to do it", not "only one way to do it".
-
-
Bruno Feroleto over 11 yearsI guess that "symbol table" is the key term, here. It is quite hard to find its definition in the official Python documentation (in fact, I have yet to find it :)).
-
Bruno Feroleto over 9 yearsI would add that
dir()
also returns slots, whereasvars()
doesn't. -
Mangu Singh Rajpurohit over 6 years@SiHa, It's actually the instance of builtin-types, which don't have
__dict__
attribute. Thanks for correcting me. I have updated the answer. -
Bakuriu over 6 yearsAlso the output of
dir
can be customized by implementing the__dir__
magic method:class A: def __dir__(self): return ['a']
and then you havedir(A()) == ['a']
whilevars(A()) == {}
. -
Scott H almost 6 yearsGood point. Also, instances of custom classes that implement __slots__ do not have a __dict__ attribute either and would similarly give an error.