Search code examples
pythonbindingcpythondynamic-binding

Is there anything static about python function / method invocations?


In asking a question about reflection I asked:

Nice answer. But there is a difference between saying myobject.foo() and x = getattr(myobject, "foo"); x();. Even if it is only cosmetic. In the first the foo() is compiled in statically. In the second, the string could be produced any number of ways. – Joe 1 hour ago

Which got the answer:

Eh, potato / solanum tuberosum... in python, niether is statically compiled, so they are more or less equivalent. – SWeko 1 hour ago

I know that Python objects' members are stored in a dictionary and that everything is dynamic, but I assumed that given the following code:

class Thing():
  def m(self):
    pass

t = Thing()

The following code would somehow get statically compiled when the .pyc is generated:

t.m()

i.e. the compiler knows the address of m(), so no point binding at runtime. That or the runtime would cache subsequent lookups.

Whereas this would always involve hitting the dictionary:

meth = getattr(t, "m")
meth()

Are all invocations treated as string lookups in dictionaries? Or are the two examples actually identical?


Solution

  • They're not entirely identical, but they are both dictionary lookups, as can be shown with the disassembler dis.dis.

    In particular, note the LOAD_ATTR instruction with dynamically looks up the attribute by name. According to the docs, it "replaces TOS [top of stack] with getattr(TOS, co_names[namei])".

    >>> from dis import dis
    >>> dis(lambda: t.m())
      1           0 LOAD_GLOBAL              0 (t)
                  3 LOAD_ATTR                1 (m)
                  6 CALL_FUNCTION            0
                  9 RETURN_VALUE        
    >>> dis(lambda: getattr(t, 'm')())
      1           0 LOAD_GLOBAL              0 (getattr)
                  3 LOAD_GLOBAL              1 (t)
                  6 LOAD_CONST               0 ('m')
                  9 CALL_FUNCTION            2
                 12 CALL_FUNCTION            0
                 15 RETURN_VALUE