Can return call functions? - python

I tried this code from a Python tutorial:
def func1(a):
return a ** a
def func2(a):
return func1(a) * func1(a)
print(func2(2))
It displays 16, and I am trying to understand how this works.
Does func1 get called when the return statement starts to run?
Can return call functions?
I tried to understand how it works by adding a print:
def func1(a):
print("Hello World")
return a ** a
def func2(a):
return func1(a) * func1(a)
print(func2(2))
I see that the Hello World message is printed two times, so I assume that func1 is getting called twice. How exactly does this work? Is the * in this line related to how it works?

Expressions
When code says something like func1(2), this is an expression. The term "expression" comes from mathematics. Specifically, this expression is a call of the function func1, passing it an argument 2. When the function is called, it will receive the value 2, assigning it to the a parameter (i.e., naming it a).
Expressions can be simpler than that. An individual variable name by itself is also an expression. It simply evaluates to whatever that variable is naming, in the current context. So is a literal value, like 2. It evaluates, unsurprisingly, to itself.
Expressions can also be more complex than that. They can be composed of other expressions. So we can write 1 + 2 * 3, and it is evaluated (after figuring out the operator precedence) by computing 2 * 3 to get 6, and then 1 + 6 to get 7. The result from evaluating each step, substitutes in to the next step. The same thing happens with calls to functions. We can write, for example, func1(func1(2)); the inner func1(2) is evaluated to 4, and so the next step is to call func1(4), resulting in 256. Or we can write something like func1(2) * func1(2); the calls happen first, evaluating to 4 each time, and then 4 * 4 results in 16.
But why did the calls evaluate that way? Because of the return statements in the functions they called. So now we also need to understand statements.
Statements
An expression is one kind of statement in Python. Statements are the individual steps in a running program - normally, a single line of code.
The point is, we can write an expression on a line by itself in Python, and that is one kind of statement. The result is computed, and then thrown away. (How is this useful? Because the expression could have side effects, for example, assigning to a global variable, or raising an exception. Python is so flexible that, most of the time, it would be necessary to actually evaluate an expression in order to figure out whether it could have a side effect. So, Python doesn't forbid writing code like that, and even "obviously" useless things like 1 + 1 on a line by itself are allowed. (There is, however, one special case: docstrings are given special treatment.))
Another important statement in this code is the return statement. This consists of the keyword return statement, optionally followed by... an expression.
The purpose of the return statement is to state the result of evaluating the call. return means: "evaluate this expression, and then exit the current function. The result of evaluating the expression is the result of evaluating the function call, so there is no more work to do in this function." (When it appears by itself, return in Python means the same thing as return None, which also happens if the function reaches the end without a return statement - that is to say, in those cases, the function call evaluates to special value None.)
So: in func2, return func1(a) * func1(a) means that func1(a) * func1(a) will be evaluated, resulting in 16 - because func1(a) was evaluated twice, and the result both times was 4, and 4 * 4 gives 16. Thus, that is the result of evaluating the function call.
(But why did func1(a) evaluate to 4? By the same reasoning. Each time, func1 was given a value of 2 - func2's value named a - for its own a. It computed a ** a, i.e., 2 ** 2, getting 4, and returned that.)
At the top level, print(func2(2)) will therefore call func2(2) first, evaluating to 16, and then pass 16 to print (in Python 3.x, print is a function). print will display the text as a side effect (remember the bit above about side effects?) and return None, which is then ignored (because this statement in the code is just an expression by itself). (In 2.x, print was a statement, with its own special syntax. This accomplished a similar goal, but with a lot of subtle differences.)
So, pedantically, no - return doesn't "call" other functions; functions are called by... calling them. return can be followed by an expression, and its job is to report (in other words... return) the result of that expression, from the current function, to the place where it was called. But the expression written after return is just an expression, following the normal rules; so it can contain more calls to other functions. (Or even the same function again - this is called recursion, and is the basis of some powerful techniques.)

return is executed last. The expression will be evaluated first.
func1(2) returns 2 ** 2, which is 4.
It is getting called twice, which is why Hello World is being printed twice.
4 * 4 is 16.
Now that the expression is evaluated, func2 will now return 16.
return is a keyword that stops the function(and returns the value you put after), so everything else in that line will have to be executed first.

Related

Calling variables in Python (local vs global)

The below works and gives 8:
def mypow(a,b):
c=a**b
print (c)
mypow(2,3)
But the below doesn't:
def mypow(a,b):
c=a**b
mypow(2,3)
I understand that in the former we print but since the latter doesn't give an error message (nor does it give a result), what is actually happening in the latter?
in
def mypow(a,b):
c=a**b
mypow(2,3)
after function executed; all data inside the function destroy. if you want to see anything on your screen after function is executed; either use print or return.
In the end of any function you have multiple choices.
1- return. return the output. when you return the output you receive it after function executed without error eg: d = func(any_value), if you specified the return value in func you will receive it and store it in d. if you don't specify the return statement in the function the function will return None.
2- print. print anything. this is what you did in your first function. in this case you get printed stuff in your screen, and you function return None, so you can't hold the output (a printed value) in any variable or use it anywhere else.
3- global. assign to global. when you need this option you create a variable outside of your function say my_variale=5, and in the first line of your function you write gloabl my_variable. say the function contain 5 lines, in line #3 you did my_variable=7. after function is executed and destroyed all values within the function; when you check my_variable you will see it contains value of 5.
You can replace print with return for your function and get the same result
def mypow(a,b):
c=a**b
return c
mypow(2,3)
Variables nested in functions are treated as local variables unless you add global before the variable name.

returning values of function in python

Why do I have to return function in the else case? Can't I just apply the defined function because I have to only return the value of b and store it?
def gcd_a(a,b):
if a==0:
return b
else:
gcd_a(b%a,a)
I think the main concept you are missing is that in order to get the result of a recursive function (or any function for that matter), the function that you access the result of must return the value*
Right now, when you call gcd_a, a recursive call to gcd_a will eventually return a value, however it will be lost, since the function that you are accessing the result of does not return a value.
To show you this is true, let's add in a log statement that prints when the function is returning:
def gcd_a(a,b):
if a==0:
print('finally returning!', b)
return b
else:
gcd_a(b%a,a)
Now if we call:
print(gcd_a(10, 99))
We get the following output:
finally returning! 1
None
Somewhere, a recursive call to gcd_a found your condition to be True, and returned 1, however, this result is not printed, because it is not returned by your call to gcd_a
* Unless you do something strange like updating a global variable, etc...
If your function finishes without returning anything, it will implicitly return None.
def double(val):
val * 2
value = double(10)
print(value)
Which is fine, except it doesn't have a return statement, and so all you get is None
The same applies to recursive functions: you can do all the recursive function calls you want, but if you don't actually return the result of the function, then it'll return None.
IMHO, the problem is in the way you are thinking about function which has to be cleared. The fact that you are having this doubt in a recursive function call is incidental because you are calling the same function again and again with different arguments causing different branches to be executed.
How will a function that doesn't return anything in any of its branches be helpful to its caller? In your case, what would happen if your function's caller calls it with an argument that hits your else block? It will return nothing and this won't help the caller!
Now in your recursive case, if your caller calls the function with an argument that hits the if block, then it would work as expected. However, if it hits the else block, then it would become a caller and call the same function again. For simplicity, let us assume that this time it hits the if condition and returns something to its caller. However, will that reach the original caller that initiated everything? The answer is no because you are not returning it to him!
Most of the times you would need a return for every branch in a function
unless you are doing it on purpose for a side effect.
In the else block, if you don't return the function call, the outer function returns None because python interpreter just runs whatever is returned by the gcd function.
Let's assume the following code:
def func():
10
In that function, it just runs 10, but it doesn't mean that there is some return from the function.

Unexecuted yield statement blocks function to run?

In the below simplified code, I would like to reuse a loop to do a preparation first and yield the result.
However, the preparation (bar()) function is never executed.
Is yield statement changing the flow of the function?
def bar(*args,**kwargs):
print("ENTER bar")
pass
def foo(prepare=False):
print("ENTER foo")
for x in range(1,10):
if prepare:
bar(x)
else:
yield x
foo(prepare=True)
r = foo(prepare=False)
for x in r:
pass
Because the foo definition contains a yield, it won't run like a normal function even if you call it like one (e.g. foo(prepare=True) ).
Running foo() with whatever arguments will return a generator object, suitable to be iterated through. The body of the definition won't be run until you try and iterate that generator object.
The new coroutine syntax puts a keyword at the start of the definition, so that the change in nature isn't hidden inside the body of the function.
The problem is that having a yield statement changes the function to returning a generator and alters the behavior of the function.
Basically this means that on the call of the .next function of the generator the function executes to the yield or termination of the function (in which case it raises StopIteration exception).
Consequently what you should have done is to ensure that you iterate over it even if the yield statement won't be reached. Like:
r = foo(prepare=True)
for x in r:
pass
In this case the loop will terminate immediately as no yield statement is being reached.
In my opinion, the actual explanation here is that:
Python evaluates if condition lazily!
And I'll explain:
When you call to
foo(prepare=True)
just like that, nothing happens, although you might expected that bar(x) will be executed 10 times. But what really happen is that 'no-one' demanding the return value of foo(prepare=True) call, so the if is not evaluated, but it might if you use the return value from foo.
In the second call to foo, iterating the return value r, python has to evaluate the return value,and it does, and I'll show that:
Case 1
r = foo(prepare=True)
for x in r:
pass
The output here is 'ENTER bar' 9 times. This means that bar is executed 9 times.
Case 2
r = foo(prepare=False)
for x in r:
pass
In this case no 'ENTER bar' is printed, as expected.
To sum everything up, I'll say that:
There are some cases where Python perform Lazy Evaluation, one of them is the if statement.
Not everything is evaluated lazily in Python,
for example:
# builds a big list and immediately discards it
sum([x*x for x in xrange(2000000)])
vs.
# only keeps one value at a time in memory
sum(x*x for x in xrange(2000000))
About lazy and eager evaluation in python, continue read here.

No return statement but still returning None

My function is just to modify a list.However, I don't want it to return anything not even 'None' and yet it returns 'None'. Is there a way to fix this?
TL;DR: No, there is not a meaningful way around default return of None in Python.
In Python, returning an implied None is the closest you can do to "return nothing". This is different to e.g. C where a void function declaration would prevent assignment to a variable. This behaviour is part of the Python language for consistency, because
x = MyFunc()
must define and store some value in x. Similarly, uses such as
OtherFunc( 1, 2, MyFunc() )
or
[ 1, 2, 3, MyFunc() ]
or
MyFunc() * 3
All need to have some behaviour. With an implied return value of None, these issues are solved in a particular way in Python.
It doesn't have to be this way - in principle if someone were to re-design this part of Python there could be a few different self-consistent approaches - for instance Python could have some special value that could only be returned from function calls and if it was found to be inside any other expression that could raise an error. Worth noting that in some cases, such as 3 * MyFunc(), a return value of None will raise an error in this way - but you cannot rely on that happening immediately in all possible unwanted scenarios based on the function definition.
However, Python returns implicit None. Changing this would affect any code/libraries that rely on the behaviour in a global way, or add awkward context-parsing requirements to the language because the feature necessarily would need to work both inside function definitions and where those functions were used.

'None' is not displayed as I expected in Python interactive mode

I thought the display in Python interactive mode was always equivalent to print(repr()), but this is not so for None. Is this a language feature or am I missing something? Thank you
>>> None
>>> print(repr(None))
None
>>>
It's a deliberate feature. If the python code that you run evaluates to exactly None then it is not displayed.
This is useful a lot of the time. For example, calling a function with a side effect may be useful, and such functions actually return None but you don't usually want to see the result.
For example, calling print() returns None, but you don't usually want to see it:
>>> print("hello")
hello
>>> y = print("hello")
hello
>>> y
>>> print(y)
None
Yes, this behaviour is intentional.
From the Python docs
7.1. Expression statements
Expression statements are used (mostly interactively) to compute and
write a value, or (usually) to call a procedure (a function that
returns no meaningful result; in Python, procedures return the value
None). Other uses of expression statements are allowed and
occasionally useful. The syntax for an expression statement is:
expression_stmt ::= starred_expression
An expression statement evaluates the expression list (which may be a
single expression).
In interactive mode, if the value is not None, it is converted to a
string using the built-in repr() function and the resulting string
is written to standard output on a line by itself (except if the
result is None, so that procedure calls do not cause any output.)
In Python, a function that does not return anything but is called only for its side effects actually returns None. As such functions are common enough, Python interactive interpreter does not print anything in that case. By extension, it does not print anything when the interactive expression evaluates to None, even if it is not a function call.
If can be misleading for beginners because you have
>>> a = 1
>>> a
1
>>>
but
>>> a = None
>>> a
>>>
but is is indeed by design
None represents the absence of a value, but that absence can be observed. Because it represents something in Python, its __repr__ cannot possibly return nothing; None is not nothing.
The outcome is deliberate. If for example a function returns None (similar to having no return statement), the return value of a call to such function does not get shown in the console, so for example print(None) does not print None twice, as the function print equally returns None.
On a side note, print(repr()) will raise a TypeError in Python.

Categories