Understanding Functions in Python (Part 2)

1 Introduction
In Understanding Functions in Python (Part 1), we started from the most basic function and worked through functions with parameters and functions with return values, along the way explaining Python’s None and NoneType concepts.
This article carries on to three concepts you’ll use every day in practice: default arguments, keyword arguments, and scope. The first two determine “how you can pass arguments when calling a function”, while the last determines “how long a variable created inside a function lives, and who can see it”.
2 Default Arguments in Python Functions
Let’s start with a simple example reviewing the previous article:
def say_hello(name):
print(f'hello, {name}')The code above defines a say_hello function that accepts one parameter. When calling it, we pass in a string:
say_hello('Tom')The string “Tom” then corresponds to the name parameter.
Now here’s the question: what if we call say_hello without passing anything?
say_hello()The computer displays an error message:
TypeError: say_hello() missing 1 required positional argument: 'name'The reason is simple: when defining say_hello we stated that it accepts one parameter, but nothing was passed in at call time, so the computer doesn’t know what value to substitute for name and has no choice but to raise an error.
Following that line of thought, if we want calling say_hello not to error out even when nothing is passed, we can give the name parameter a default value:
def say_hello(name="Johnny"):
print(f'hello, {name}')
say_hello()Now, even if nothing is passed at call time, the computer knows to treat name as “Johnny”. Of course, if a value is passed in, that value overrides the default.
Put plainly, a “default argument” is simply a parameter that carries a “default value”.
3 Mixing Parameters With and Without Default Values
When defining a function, if some parameters have default values and some don’t, you must make sure that parameters without default values go on the left.
For example:
def say_hello(age, name="Johnny"):
print(f'I am {name}')
print(f'I am {age} years old')say_hello accepts two parameters, age (no default value) and name (with a default value), so age must be written to the left of name. If you put them on the wrong side:
def say_hello(name="Johnny", age):
print(f'I am {name}')
print(f'I am {age} years old')the program errors out at the moment of definition — it never even gets as far as being called:
SyntaxError: non-default argument follows default argument4 Keyword Arguments in Python Functions
When calling a function, the information we pass in is matched to the function’s parameters by “position”.
Take the say_hello function defined above:
def say_hello(age, name="Johnny"):
print(f'I am {name}')
print(f'I am {age} years old')Because name has a default value, you can pass either one argument or two when calling it. If you pass just one:
say_hello(100)that argument (100) corresponds to say_hello’s age parameter, while name falls back to its default of “Johnny”. When we pass two arguments:
say_hello(100, Tom)the first argument passed in (100) corresponds to say_hello’s first parameter, age; the second argument passed in (Tom) corresponds to the second parameter, name. Get the order muddled and the values land on the wrong parameters.
Besides matching by “position”, we can also use a “keyword” to specify directly which parameter a value goes to:
say_hello(age=100, name="Tom")Once specified by keyword, the order of the arguments can be changed freely — written like this the result is exactly the same:
say_hello(name="Tom", age=100)When there are a lot of parameters, this style is far more readable: the calling line alone tells you what each value is for, without having to go back and look up the function’s definition.
5 Keyword Arguments of the print( ) Function
Keyword arguments aren’t exclusive to functions you write yourself — the print() we use every day actually takes several parameters. For example, after print() displays a string it appends a “newline character” at the end by default, which is why whatever the next print() outputs ends up on the next line:
print('Hello')
print('Johnny')After running this, Hello is on the first line and Johnny on the second:
Hello
JohnnyIf we want Johnny not to go on a new line but to follow directly after Hello, we can use the end keyword argument to specify the characters appended to the end of the string.
For example, appending nothing at all to the end of the Hello string:
print('Hello', end="")
print('Johnny')The displayed result is now:
HelloJohnnyOr appending “a single space” to the end of the Hello string:
print('Hello', end=" ")
print('Johnny')The displayed result is now:
Hello JohnnyIn other words, the line break you normally see is just end’s default value happening to be a newline character — it isn’t behaviour hard-coded into print().
6 The Concept of Scope in Python
In the previous article we mentioned that accessing a variable from “inside” a function while “outside” that function makes the computer raise an error. For example:
def say_hello(age, name="Johnny"):
print(f'I am {name}')
print(f'I am {age} years old')
print(age)NameError: name 'age' is not definedBecause the variable age exists only inside the say_hello function, it cannot be accessed outside it. A function has its own “range”, its own “boundary” — and that is the concept of scope in programming languages.
7 Local Scope and Global Scope
Scope divides into local scope and global scope: the inside of a function forms a local scope, while everything outside functions belongs to the global scope. A variable created in a local scope is called a local variable; a variable created in the global scope is called a global variable. A variable can only have one identity — it is either a local variable or a global variable, never both.
a = 5
b = 10
def example():
c = 15
d = 20
e = 25Taking the code above as an example, the variables a, b and e are all in the global scope and are global variables; the variables c and d are written inside a function, are in a local scope, and are local variables.
Clever reader that you are, you’ve surely noticed: variables inside a function are local variables, and variables outside functions are global variables. A program can have many local scopes (potentially as many as it has functions), but it will only ever have one global scope.
8 The Lifetime of Variables in Python
Now that we know variables divide into local and global, the next thing to look at is how long each of these two kinds “lives”.
When a “function” runs, the local scope belonging to that function is created too, and the variables created in the function are all stored in that local scope. When the function finishes running, that local scope is destroyed along with it, and the local variables stored inside it naturally disappear too. This also explains why print(age) earlier came up empty: once the function has run, age no longer exists.
When a complete “program” (.ipynb or .py) starts running, the global scope belonging to that program is created too, and the variables created in the program are all stored in that global scope. When the program finishes running, that global scope is destroyed along with it, and the global variables stored inside it disappear too.
9 Important Properties of Scope in Python
Having understood local scope and global scope, we can lay out four important properties of scope:
- Code in the global scope may not access variables in a local scope (local variables)
- Code in a local scope may access variables in the global scope (global variables)
- Code in a local scope may not access variables in another local scope (local variables)
- If two variables are in different scopes, those two variables may use the same name
10 Conclusion
This article introduced default arguments and keyword arguments in Python functions, which give us more flexibility when defining and calling functions. It also explained the concept of scope, along with the respective lifetimes of local variables and global variables.
The four properties of scope listed at the end are the key to understanding “why a variable is sometimes readable and sometimes not”. The next article (Understanding Functions in Python Part 3) explains what each of these four properties means, one at a time.




