Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How many function calls does it take to create a class instance? [closed]

Tags:

python

Knowing that calling a function in python is expensive, the answer to this question has some bearing on optimization decisions, e.g. in comparing a straight one-function numeric approach to an object-oriented one. So I'd like to know

  • What's the typical number of function calls required?
  • What's the minimum number of function calls required?
  • What increases the number of calls?
  • How does user-created classes compare to built-in classes?
  • What about object deletion (including garbage collection)?

My google-fu was not up to the task of finding an answer to this question.

EDIT: So to summarize the comments and forestall more close votes, here's some clarifications:

  • I'm interested in the time-complexity of python instance creation compared to calling a normal python function
  • For the purposes of this question, let's limit ourselves to the newest CPython versions.
like image 923
Lauritz V. Thaulow Avatar asked Jul 30 '12 11:07

Lauritz V. Thaulow


People also ask

How do you create an instance of a class in Python?

To create instances of a class, you call the class using class name and pass in whatever arguments its __init__ method accepts.

How do you call a function inside a class in Python?

To call a predefined static function, simply type the class name followed by a dot and a function name. To call a user-defined or predefined non-static java function, create a class object and then follow the dot “.” syntax to call the function.

How are classes different than functions?

Functions do specific things, classes are specific things. Classes often have methods, which are functions that are associated with a particular class, and do things associated with the thing that the class is - but if all you want is to do something, a function is all you need.

Is function a class in Python?

In Python, functions behave like any other object, such as an int or a list. That means that you can use functions as arguments to other functions, store functions as dictionary values, or return a function from another function.


2 Answers

See Python Object Creation by Eli Bendersky.

Quoting at length the conclusion:

Lest we lose the forest for the trees, let’s revisit the question this article began with. What happens when CPython executes j = Joe()?

  • Since Joe has no explicit metaclass, type is its type. So the tp_call slot of type, which is type_call, is called.
  • type_call starts by calling the tp_new slot of Joe:
    • Since Joe has no explicit base clase, its base is object. Therefore, object_new is called.
    • Since Joe is a Python-defined class, it has no custom tp_alloc slot. Therefore, object_new calls PyType_GenericAlloc.
    • PyType_GenericAlloc allocates and initializes a chunk of memory big enough to contain Joe.
  • type_call then goes on and calls Joe.__init__ on the newly created object.
    • Since Joe does not define __init__, its base’s __init__ is called, which is object_init.
    • object_init does nothing.
  • The new object is returned from type_call and is bound to the name j.
like image 95
Steven Rumbalski Avatar answered Nov 14 '22 23:11

Steven Rumbalski


I've done as suggested in the comments and used timeit on these test cases:

def a():
    pass

class A(object):
    pass

class B(object):
    def __init__(self):
        pass

class NOPType(type):
    pass

class C(object):
    __metaclass__ = NOPType
    def __init__(self):
        pass

class D(object):
    def __new__(cls, *args, **kwargs):
        return super(D, cls).__new__(cls)

    def __init__(self):
        pass

class E(A):
    def __init__(self):
        super(E, self).__init__()

Test results:

$ python -m timeit -s "import tst" "tst.a()"
10000000 loops, best of 3: 0.149 usec per loop
$ python -m timeit -s "import tst" "tst.A()"
10000000 loops, best of 3: 0.169 usec per loop
$ python -m timeit -s "import tst" "tst.B()"
1000000 loops, best of 3: 0.384 usec per loop
$ python -m timeit -s "import tst" "tst.C()"
1000000 loops, best of 3: 0.397 usec per loop
$ python -m timeit -s "import tst" "tst.D()"
1000000 loops, best of 3: 1.09 usec per loop
$ python -m timeit -s "import tst" "tst.E()"
1000000 loops, best of 3: 0.827 usec per loop

Using a function call as a baseline, these are the results:

  • a basic instantiation takes 1.1 times more time.
  • adding an __init__ method increases the factor to 2.6
  • adding a no-op metaclass is just a tiny bit more expensive, at 2.7
  • instead adding a basic __new__, it's equivalent to 7.3 function calls
  • a class with a single subclass is equivalent to 5.6 function calls

For the last two result you can subtract about 2 if the call to super is replaced with its return value.

This should give a rough estimate of how time-expensive python classes are compared to python functions, in CPython 2.7.

like image 23
Lauritz V. Thaulow Avatar answered Nov 14 '22 22:11

Lauritz V. Thaulow