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 return
ed 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.)