0

following the tutorial on https://docs.python.org/3/tutorial/classes.html (see below) to understand the matter deeper I would have expected, that

class Foo:
    def bar(self, spam='spam'):
        i = 3**33

x = Foo()
meth = x.bar
func = Foo.bar

%timeit x.bar('spam')      # 1
%timeit meth('spam')       # 2
%timeit func(x, 'spam')    # 3

should be in the order slow to fast. But it's not, the call to meth() takes 100ns, the call to func 110ns (#1 is slower as expected).

from the explanation below I would have thought, that in #2 the method f has to be unpacked into the function and the class instance, then the argument list prepended with the instance, and the function called. Why is it not slower then #3 where you have only the plain function call.

What is wrong?


If you still don’t understand how methods work, a look at the implementation can perhaps clarify matters. When a non-data attribute of an instance is referenced, the instance’s class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.

6
  • 2
    Did you run this multiple times and take the average time or are you just assuming run times from single runs? Commented May 28, 2019 at 13:46
  • %timeit chooses this on its own (it did 1e7 loops and showed the best of 3 runs), but anyway, yes, this was reproducible, doesn't seem to be a caching issue Commented May 28, 2019 at 13:50
  • "When a non-data attribute of an instance is referenced" Calling meth doesn't do that. Commented May 28, 2019 at 13:53
  • yes, that's why #1 is slower, I get this :) But read on, last sentence, abount constructing a new argument list - this should not be quicker then the case #3, where this is not done Commented May 28, 2019 at 14:01
  • Why do you think that constructing the argument list works differently for #2 and #3? Commented May 28, 2019 at 14:04

1 Answer 1

4

If you disassemble the bytecode that comes out of the three calls you get the following:

1         0 LOAD_NAME                0 (x)
          2 LOAD_METHOD              1 (bar)
          4 LOAD_CONST               0 ('spam')
          6 CALL_METHOD              1
          8 RETURN_VALUE


1         0 LOAD_NAME                0 (f)
          2 LOAD_CONST               0 ('spam')
          4 CALL_FUNCTION            1
          6 RETURN_VALUE

1         0 LOAD_NAME                0 (Foo)
          2 LOAD_METHOD              1 (bar)
          4 LOAD_NAME                2 (x)
          6 LOAD_CONST               0 ('spam')
          8 CALL_METHOD              2
         10 RETURN_VALUE

You can see that the first and last ones actually end up doing more work. The middle one already has the function in hand so it can call it immediately (because you've already pulled it out of the class with f = x.bar) wheras the other ones still have to do a dict lookup in the class.

Sign up to request clarification or add additional context in comments.

7 Comments

thank you for the quick answer - what tool does do such a disassemble? Anyway, this is a valid point, but no, even extracting the method into a local name does not make it faster then #2, i gets 110ns compared to 100
Can you explain what you mean by your last sentence? What did you change? And disassembling is done using the Standard Library's dis module
I edited the question to avoid the time for dict lookup in the comparison. +1 for the dis module - though this does not answer the original question ;)
So you're wondering why even after your change, #3 is still slower? You still have one more LOAD_NAME call in #3 as both the function and the object you want to call it on have to be loaded.
right ... ah well, but the text sais, a method object is constructed by packing func and x together, and then unpacking it again when the method is called. Well, i'm not so sure anymore that my observation is impossible ;) ... but it's still strange...
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.