# The cost of nested definitions

**URL:** <https://discuss.python.org/t/the-cost-of-nested-definitions/31203>\
**Category:** Python Help\
**Created:** [August 7, 2023, 6:58pm UTC](https://discuss.python.org/t/the-cost-of-nested-definitions/31203 "2023-08-07T18:58:37Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![e-dreyer](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/e-dreyer/32/14055_2.png) [@e-dreyer](https://discuss.python.org/u/e-dreyer)\
**Post date:** [August 7, 2023, 6:58pm UTC](https://discuss.python.org/t/the-cost-of-nested-definitions/31203/1 "2023-08-07T18:58:37Z")

</div>

My problem is advanced and out of the scope of the question. So I’ll keep it simple. Imagine the following:

This is a set of helper functions, they only serve to make other functions more readable.

```auto
def functionA(*args, **kwargs):
        ...

def functionB(*args, **kwargs):
        ...

def functionC(*args, **kwargs):
        ...

```

But then I have my main functions:

```auto
def mainA(*args, **kwargs):
        ...
        a = functionA(*args, **kwargs)
        ...

def mainB(*args, **kwargs):
        ...
        b = functionB(*args, **kwargs)
        ...

def mainC(*args, **kwargs):
        ...
        c = functionC(*args, **kwargs)
        ...

```

Thus functionA is only used by mainA ever! Etc. For functionB, C and mainB and C.

Would it be mode optimized to do this then?

```auto
def mainA(*args, **kwargs):

       def functionA(*args, **kwargs):
        ...

        ...
        a = functionA(*args, **kwargs)
        ...

def mainB(*args, **kwargs):

        def functionB(*args, **kwargs):
        ...

        ...
        b = functionB(*args, **kwargs)
        ...

def mainC(*args, **kwargs):

       def functionC(*args, **kwargs):
        ...

        ...
        c = functionC(*args, **kwargs)
        ...

```

My experience from C says, when the file is analysised, the functions would be created and called once. But if they are nested in the functions, they will be created on every call. Thus reduce performance.

Nesting them is purely for readability and keeping everything together. But am I impacting performace here?

Edit: similarly this can be used in classes, does that also possibly affect performance?

---

<div class="post-metadata">

**Author:** ![TeamSpen210](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/teamspen210/32/1004_2.png) [@TeamSpen210](https://discuss.python.org/u/TeamSpen210)\
**Post date:** [August 7, 2023, 8:45pm UTC](https://discuss.python.org/t/the-cost-of-nested-definitions/31203/2 "2023-08-07T20:45:43Z")

</div>

It would have a performance cost, but incredibly minor - some brief testing suggests it’s similar in cost to making a dict. Functions are split into two different objects - the code object which contains all immutable data, and a function object which is mutable. During parsing/compilation of the module, the code objects are all created. Then when you define a function, it just takes that constant and creates the function object.

The bigger cost is something else really - since they’re nested, these functions are inaccessible, so you can’t do things like call them from testing code. You might be fine with that, it depends.

---

<div class="post-metadata">

**Author:** ![barry-scott](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@barry-scott](https://discuss.python.org/u/barry-scott)\
**Post date:** [August 7, 2023, 8:48pm UTC](https://discuss.python.org/t/the-cost-of-nested-definitions/31203/3 "2023-08-07T20:48:41Z")

</div>

If you want to know that the performance impact of a code change or alternative implementations are you can use the timeit module to benchmark the code.

If you are interested the seeing the code that is being compiled for the python you can use the dis module to disasemble it, see dis.dis().

---

<div class="post-metadata">

**Author:** ![kknechtel](https://avatars.discourse-cdn.com/v4/letter/k/e47c2d/32.png) [@kknechtel](https://discuss.python.org/u/kknechtel)\
**Post date:** [August 7, 2023, 11:22pm UTC](https://discuss.python.org/t/the-cost-of-nested-definitions/31203/4 "2023-08-07T23:22:47Z")

</div>

> [@e-dreyer](#):
>
> My experience from C says, when the file is analysised, the functions would be created and called once. But if they are nested in the functions, they will be created on every call.

This is actually wrong. C doesn’t re-create anything for a nested function (which it does support), because there is nothing to create. Functions aren’t first-class objects in C; there’s just the compiled code. You can make pointers to functions, but the only thing you can really do with them - portably - is store the pointer and use it to call the function. In particular, C doesn’t natively support closures; if you try to `return` a pointer to the local function, you can’t expect it to work right for the caller (the outer function’s locals are gone from the stack by that point).

Python does create objects that represent functions, and which support closures; and for a nested function it will need to do this every time. However, it does not recompile the code or anything like that. It already has compiled bytecode stored, that it can use to create the new function object. (It may also need to create some “cell” objects that support the closure: because it is _late-binding_, it can’t just assign the current values of the outer function’s locals.)

The creation does not take a lot of time:

```auto
>>> def outer():
... def inner():
... pass
... 
>>> def simple():
... pass
... 
>>> import timeit
>>> timeit.timeit(outer)
0.10839796392247081
>>> timeit.timeit(simple)
0.04258948704227805

```

For comparison:

```auto
>>> def noloop():
... for i in range(0):
... pass
... 
>>> timeit.timeit(noloop)
0.12932194117456675

```

Creating the function object took less time than even setting up a trivial loop that doesn’t even run. (The default compiler really does not try to optimize much of anything at the bytecode level; historical speed improvements in Python, as far as I’m aware, have focused on the interpreter and a bit on bytecode design.)

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [August 7, 2023, 11:50pm UTC](https://discuss.python.org/t/the-cost-of-nested-definitions/31203/5 "2023-08-07T23:50:15Z")

</div>

There is potentially some additional cost, since nonlocal lookups are required for some things that might otherwise be local or global (both of which are faster, moreso with locals). But (a) this cost is probably insignificant, so spend more time thinking about how it looks in source code than how it runs; and (b) if it really worries you, **time it**. Measure. Don’t just hope for the best - try it on your actual code. You can easily do microbenchmarks to find out how long it takes to construct a function or look up a variable, but to know how much that will actually affect _your_ code, measure _your_ code! There’s no substitute for data. 🙂
