Nested Function in Python

Call Nested Function in Python

I assume do_this and do_that are actually dependent on some argument of foo, since otherwise you could just move them out of foo and call them directly.

I suggest reworking the whole thing as a class. Something like this:

class Foo(object):
def __init__(self, x, y):
self.x = x
self.y = y

def do_this(self):
pass

def do_that(self):
pass

def __call__(self):
self.do_this()
self.do_that()

foo = Foo(x, y)
foo()
foo.do_this()

Understanding Nested functions in python

First of all you need to realise res_1 is simply the in_sum() function.

Therefore as per your code:

nested_sum(2) puts x = 2 and then returns the in_sum() function.
res_2 = res_1(10) = in_sum(10)

Therefore x = 2 and y = 10, so thus

x + y = 2 + 10 = 12

Nested functions in python

Pass "data" as a parameter to your make_calculation function:

def make_dic():
data=dict()
code...
return data
def make_calculation(data):
for x,y in data.items()
code....
def main():
data=make_dic()
make_calculation(data)

The reason you need to do this is due to Python scoping rules (i.e. the location within a program , or "scope" of where a parameter is or is not defined).

Parameter "data" is not a global variable (using global variables is rarely recommended and should be a last resort) , and thus make_calculation(data) needs to receive the element as a parameter, which was defined elsewhere -ultimately the parameter created in make_dic()

@chepner said as much, more formally, in their comment (i.e. Python is lexically scoped). For much more on Python scoping rules see this old, but still useful ,answer : Short description of the scoping rules?

Python - Proper way to call nested functions?

Python inner functions (or nested functions) have many use cases but in non of them, accessing through the nested function from outside is an option. A nested function is created to have access to the parent function variables and calling it from outside of the parent function is in conflict with principle.

You can call the function from the parent function or make it a decorator:

Calling the nested function from the parent would be:

def run_code(filename):
def fileCommand():
print("the file type is\n")
os.system("file " + filename).read().strip()
fileCommand()

If you describe more about your use case and the reason why you want to run the code like this, I can give more details about how you can implement your code.

Python Nested Function with Variables vs. Class?

The comments on your question are already a good answer.

A Python method is just a function which have self as first argument, which is an instance of (an inheriting class of) the class that defines the method.

Indeed, for your very simple example, using a class is mostly useless. But you may be familiar with Python strings which are of the class str :

>>> type("hello")

And a string has many methods : capitalize, casefold, center, count, encode, endswith, ...

It is convenient because I don't need to create the same string each time for each method I want to call, I can reuse the same instance :

>>> s = "hello"
>>> s.capitalize()
"Hello"
>>> s.replace("h", "G")
'Gello'
>>> s.count("l")
2

What is even more powerfull is to use class instances to store state. Python strings are immutable, but lists are not.

>>> temperatures = [14, 15, 10, 33]
>>> temperatures[1]
15
>>> temperatures[1] = 0
>>> temperatures
[14, 0, 10, 33]
>>> temperatures.sort()
>>> [0, 10, 14, 33]

Object-orientation as it is implemented in Python is mostly about coupling statefull data with the functions to operate on it (named methods). It is aimed at simplifying these constructs which often useful.

Hope it clarifies !

Can a Python nested function variable be used in other nested functions?

You just need to return them in func1 and pass the results to func2

def func1():
a = float(input("Enter first number: "))
b = float(input("Enter second number: "))
return a, b

Then,

def func2(a, b):
print(f"{a}, {b}")

And then when you call the functions you assign variables a and b to the result of func1 and pass them as parameters to func2,

a,b = func1()
func2(a, b)

You could initialize the variables in the scope of func but it is better to handle them as the return of the function, it easier to keep track of the algorithm that way.

Memory efficiency of nested functions in python

Regarding memory, both of them have almost the same memory footprint.

A function is comprised of a code object, containing the actual compiled code, and a function object containing the closure, the name and other dynamic variables.

The code object is compiled for all functions, inner and outer, before the code is run. It is what resides in the .pyc file.

The difference between an inner and an outer function is the creation of the function object. An outer function will create the function only once, while the inner function will load the same constant code object and create the function every run.

As the code object is equivalent, _inner and _outer's memory footprint is equivalent:

  • In both cases you have the name of the function as a constant. In the functionA the name will be used to construct the inner function object on each run, while in functionB the name will be used to refer to the global module and search for the outer function.
  • In both cases you need to hold a code object, either in the global module or in functionA.
  • In both cases you have the same parameters and same space saved for variables.

Runtime however is not equivalent: functionB needs to call a global function which is slightly slower than an inner function, but functionA needs to create a new function object on each run which is significantly slower.


In order to prove how equivalent they are, let's check the code itself:

>>> functionA.__code__.co_consts
(None, <code object _innerFunction at 0x00000296F0B6A600, file "<stdin>", line 2>, 'functionA.<locals>._innerFunction')

We can see the code object as a const stored inside functionA. Let's extract the actual compiled bytecode:

>>> functionA.__code__.co_consts[1].co_code
b'|\x00|\x01\x17\x00S\x00'

Now let's extract the bytecode for the outer function:

>>> _outerFunction.__code__.co_code
b'|\x00|\x01\x17\x00S\x00'

It's exactly the same code!

The local variable positions are the same, the code is written the same, and so the actual compiled code is exactly the same.

>>> functionA.__code__.co_names
()
>>> functionB.__code__.co_names
('_outerFunction',)

In functionB, instead of saving the name in the consts, the name is saved in a co_names which is later used for calling the global function.

The only difference in memory footprint is thus the code of functionA and functionB:

>>> functionA.__code__.co_code
b'd\x01d\x02\x84\x00}\x02|\x02|\x00|\x01\x83\x02S\x00'
>>> functionB.__code__.co_code
b't\x00|\x00|\x01\x83\x02S\x00'

functionA needs to create a function object on each run, and the name for the inner function includes functionA.<locals>., which entails a few extra bytes (which is negligible) and a slower run.


In terms of runtime, if you're calling the inner function multiple times, functionA is slightly faster:

def functionA(b, c):
def _innerFunction():
return b + c
for i in range(10_000_000):
_innerFunction() # Faster

def _outerFunction(b, c):
return b + c

def functionB(b, c):
for i in range(10_000_000):
_outerFunction(b, c) # Slower

def functionC(b, c):
outerFunction = _outerFunction
for i in range(10_000_000):
outerFunction(b, c) # Almost same as A but still slower.

py -m timeit -s "import temp;" "temp.functionA(1,2)"
1 loop, best of 5: 2.45 sec per loop

py -m timeit -s "import temp;" "temp.functionB(1,2)"
1 loop, best of 5: 3.21 sec per loop

py -m timeit -s "import temp;" "temp.functionC(1,2)"
1 loop, best of 5: 2.66 sec per loop

If you're calling the outer function multiple times, functionB is significantly faster as you avoid creating the function object:

def functionA(b, c):  # Significantly slower
def _innerFunction():
return b + c
return _innerFunction()

def _outerFunction(b, c):
return b + c

def functionB(b, c): # Significantly faster
return _outerFunction(b, c)

py -m timeit -s "import temp;" "for i in range(10_000_000): temp.functionA(1,2)"
1 loop, best of 5: 9.46 sec per loop

py -m timeit -s "import temp;" "for i in range(10_000_000): temp.functionB(1,2)"
1 loop, best of 5: 5.48 sec per loop

@KellyBundy: What about recursion?

My answer is only true for sequential runs.

If the inner function recurses inside both A and B, there's no real difference in runtime or memory consumption, other than A being slightly faster. If function A and B recurse themselves, B will not allow a deeper recursion but will be significantly faster and will require less memory.

On a sequential run, there is no difference in memory as there is one function object either stored in the global module, or stored as a local variable that is constantly recreated. In case of outside (A | B) recursion there is a memory difference: The local variable where the _innerFunction object is stored is not cleared, meaning there is an additional function object created for every recursion inwards. In this specific example, we can see an important distinction between Python and other languages - Python does not have a tail-call optimization, meaning the frames aren't reused and the variables aren't removed when we recurse inwards, even though no one will reference them anymore.

You're welcome to play with the following visualization.

I guess stack space is exactly identical, all differences are on the heap?

When you're working with Python, it's hard to divide things into a stack and heap. The C-stack is irrelevant as Python uses its own virtual stack. Python's stack is actually linked by the function's currently running frame, and is loaded, or created when a function is invoked. It is the reason they're also called stack frames - there's a linked list or "stack" of frames, and each frame has its own mini-stack called a value-stack. Both the stack and the frame are stored on the heap.

There are plenty of benefits for using this approach, and a nice anecdote would be generator functions. I've actually written an article about the subject, but in short, being able to load and unload the stack at will, allows us to pause a function in the middle of its execution, and is the basis for both generators and asyncio.

Call a nested function in Python

If you want, you can return several functions that way:

class Box(object):
def __init__(self,**kw): vars(self).update(kw)
def first(x):
def second():
print(x)
def third(y): return x+y
return Box(second=second,third=third)

first(10).second() # prints 10
x=first(15)
x.second() # prints 15
print(x.third(10)) # prints 25

Any resemblance to reputation scores for answers is coincidental.

call a nested function (function inside another function)

THIS IS NOT SOMETHING YOU SHOULD DO, but you could create a nested function attribute like so:

def foo():
# for closures or strictly local function
# then this is useful!
# ugly hack other wise to try and create methods.
def bar():
print('bar')

# if there are multiple function, return a container...
return bar

Then:

foo.bar=foo()
foo.bar()
# prints 'bar'

BUT, this is far easier with a class:

class Foo:
# class can hold related methods, setters and getters,
# protected variables, etc.
# Python is DESIGNED to do this.
def bar(self):
print('bar')

Then:

f=Foo()
f.bar()
# prints 'bar'

Why is it easier?

  1. Because Python is designed to use the second example, a class, but the first example is a hack. The ways to use classes are well documented and tested.
  2. It does not scale well. Each function attribute needs to be added from the OUTSIDE of the function.
  3. You will run into local vs global namespace issues if you try and change variables. Classes gracefully support both instance and class data.

It is horrible -- don't do it. Use a class or a module to hold methods.


After a morning coffee, you can potentially do something like this if you really want to add function attributes -- Use a decorator to monkey patch a function:

def add_func_to(function):
# decorator to add function attributes
def attr_f(new_func):
setattr(function, new_func.__name__, new_func)
return new_func
return attr_f

# some function to add an attribute function to:
def foo():
pass

@add_func_to(foo)
# added attribute function below
def attr_f(string):
print(string)

Test it:

>>> foo.attr_f('test')
test

This is still limited vs a class or module since you will be limited to using global or passed variables. It is more applicable if you just want to add a function attribute to an existing function and be done with it...



Related Topics



Leave a reply



Submit