Difference between dir(…) and vars(…).keys() in Python?

23,200

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
Share:
23,200

Related videos on Youtube

Author by

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, 2022

Comments

  • Bruno Feroleto 6 months

    Is there a difference between dir(…) and vars(…).keys() in Python?

    (I hope there is a difference, because otherwise this would break the "one way to do it" principle... :)

    • Ethan Furman
      Ethan Furman about 11 years
      To be clear, the principle is "One Obvious way to do it", not "only one way to do it".
  • Bruno Feroleto over 11 years
    I 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 years
    I would add that dir() also returns slots, whereas vars() doesn't.
  • Mangu Singh Rajpurohit
    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
    Bakuriu over 6 years
    Also the output of dir can be customized by implementing the __dir__ magic method: class A: def __dir__(self): return ['a'] and then you have dir(A()) == ['a'] while vars(A()) == {}.
  • Scott H
    Scott H almost 6 years
    Good point. Also, instances of custom classes that implement __slots__ do not have a __dict__ attribute either and would similarly give an error.