pythonpython-object

What is the value of <object name>.<method name> in Python?


Consider the following code.

class Foo:
    def bar(self, x):
        pass
    
foo1 = Foo()
foo2 = Foo()
foobar1 = foo1.bar
foobar2 = foo2.bar          #What are foobar1 and foobar2?

print(foobar1 is foobar2)   #prints False. Why?

On the one hand, I can see why foobar1 is not foobar2–if I call foobar1(some_arg) , the call should in general be independent of foo2 and vice versa. But don't foobar1 and foobar2 both refer to function object defined inside the class Foo?


Solution

  • If method_name is actually defined in the class object of object's class, then <object>.<method_name> is a "bound method" (or closure), which effectively wraps the function from the class method in order to provide <object> as the self argument. If method_name is actually an attribute of <object>, or if it has been marked with the @staticmethod decorator, then <object>.<method_name> just the function itself.

    That's a bit of an oversimplification. There's lots more information in the Python reference manual (for example, in the "Callable objects" section of Section 3.2, The standard type hierarchy, and, iirc, in the Python tutorial. I hope this is a reasonable starting point, but for the nitty-gritty details and a full understanding of all the corner cases, you should probably read the authentic docs.


    When you call foobar1 or foobar2, you supply one argument and the function gets called with two arguments. How do you suppose that happens?

    More specifically, when you call foobar1, the method bar will be passed foo1 as the self argument, while when you call foobar2, the method bar will be passed foo2. That seems like magic, but it has a simple cause: when you extracted the bar attribute from foo1 (foo1.bar), Python noticed that it didn't come from foo1 itself; rather, it came from the class Foo. Since it came from the class object, Python created a new function closure on the fly in which self is bound to foo1. Thus, when you later call the closure, the self argument is filled in from the closure.

    You don't even need foo2 to see that. Every time you ask for the bar attribute of an instance of class Foo, you get a new closure. But that doesn't happen when you ask for the bar attribute of the class itself. In that case, the attribute bar is directly available, and no closure is constructed:

    >>> foo1.bar is foo1.bar
    False
    >>> Foo.bar is Foo.bar
    True
    

    You can see that these are different things:

    >>> foo1.bar
    <bound method Foo.bar of <__main__.Foo object at 0x7f9c8d8f27b8>>
    >>> Foo.bar
    <function Foo.bar at 0x7f9c8d8e5c80>
    

    The first one is a "bound method" (where self has been bound to a value), and the second one is just a garden variety function.

    If you try to call Foo.bar, you'll notice that you need to supply the self argument yourself, precisely because you're calling a function and not a closure:

    >>> Foo.bar(42)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: bar() missing 1 required positional argument: 'x'
    >>> Foo.bar(foo1, 42)
    >>>