Why does calling dir() on a class return instance methods?

With respect to the variables listed by dir(), calling dir() on a class only returns class variables, which is intuitive.

However, with respect to the methods listed by dir(), calling dir() on a class also returns instance methods, which is counterintuitive.

Notice how my_instance_method is listed by the dir() call to the class. Why does it do this, and what does it mean?

class Examine:
    class_var = "This is a class variable"

    def __init__(self):
        self.inst_var = "This is an instance variable"
    
    def my_instance_method(self):
      return "This is a instance method"


myobj = Examine()
print(dir(Examine))
# ['__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__', 'class_var', 'my_instance_method']

print(dir(myobj))
# ['__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__', 'class_var', 'inst_var', 'my_instance_method']

https://www.codecademy.com/courses/learn-python-3/lessons/data-types/exercises/self

Is there something in particular you’re unsure about that the docs don’t cover? https://docs.python.org/3/library/functions.html#dir

Yes, I’m not understanding why dir() returns instance methods when called on a class, as opposed to returning only class methods. Seems to be inconsistent with the behavior of class variables, as explained above. Would love to know why = )

Ah right, I get you. I would be careful with dir though, it’s a convenience tool, not a robust method for accessing the associated attributes of an object.

The “methods” you mention are just functions bound to the class, like genuinely attached to the class object itself at definition time.

In your example above you can access Examine.my_instance_method directly as a function object bound to the class itself. Although you should rarely ever do so you can even “monkey patch” classes after definition by binding new functions to them that instances can then access. As a silly example-

myobj = Examine()

def func(self): print(3)
Examine.func = func  # bind new function to existing class

myobj.func()  # would output 3 to the current stdout

# use class function with the instance object-
Examine.func(myobj)  # also outputs 3
# this is how `self` works when not accessing it as a method...

Note that you bind to the class object not to the instance. It is only when accessing functions bound to a parent class through an instance that you actually use methods.


This goes into implementation details a little but it might help you separate the difference-

A new instance created from Examine doesn’t even have methods (method objects) bound to it following its instantiation. They’re only created at the point of their usage (this saves a whole lot of memory when you consider you can have millions of instances of a class instantiated at any one time). Doing so literally creates a new object, a method object

print(type(myobj.my_instance_method))  # <class 'method'>
print(type(Examine.my_instance_method))  # <class 'function'>

# the function bound to the class is simply referenced by this method:
print(myobj.my_instance_method.__func__ == Examine.my_instance_method)  # True

What’s more these method objects are normally temporary. If you reused the attribute lookup there’s no guarantee you’ll be using the same memory address. If you ran the example below the address you get likely be different (and may even be re-used) but the point is that “method” objects in Python are normally temporary and the actual functions are still bound to a parent class.

myobj = Examine()
id(Examine.my_instance_method)  # 140387230190016
id(Examine.my_instance_method)  # 140387230197056
# example of outputs, will vary

You might find people assigning methods to a name to ensure they’re not temporary, e.g. mcall = myobj.my_instance_method as a means of simplifying the call and improving performance when the same method has to be called multiple times.