Hello, QTM151!
Lecture 07 - Custom Functions and Variable Scope
def
and return
print()
, np.mean()
, plt.hist()
, type()
, etcnp.random.normal()
expects two parameters: the mean (loc
) and the standard deviation (scale
). Size is an optional parameternp.random.normal(0, 1)
takes two arguments and returns a random number from a normal distribution with mean 0 and standard deviation 1np.random.normal()
will provide a sample of 1 number with mean 0 and standard deviation 1 if you don’t provide any argumentsHello, QTM151!
# First Argument: np.pi (a numeric value)
# Second Argument: 10 (number of decimals)
# Return: Round the first argument,
# given the number of decimals in the second argument
import numpy as np
round(np.pi, 10)
3.1415926536
list_fruits = ["Apple","Orange","Pear"]
# Argument: list_fruits
# Return: number of elements in the list
len(list_fruits)
3
So far, so good? 😊
# A function that generates a random number
vec_x = np.random.chisquare(df = 2, size = 10)
print(vec_x)
[2.91356908 5.53039839 4.14904763 2.71572947 0.51700164 1.36033337
4.37985205 1.79457948 0.07138228 0.21360768]
[-0.0937139 2.93770939 0.52126872 0.63748718 3.00102606 2.90859019
3.09095035 3.37943664 1.54414445 1.73580673]
[6.65244788 3.00263768 6.60447886 2.74974441 0.25115862 7.78657883
2.05190411 5.76036847 4.44663225 3.16819163]
What are the parameters, arguments, and return values in these examples? 🤓
def
keyworddef
keywordparameter
(duh! 😅)None
\[V=P\left(1+{\frac {r}{n}}\right)^{nt}\]
To know what each parameter means, click here: Appendix 01
# You can know compute the formula with different values
# Let's see how much one can gain by investing 50k and 100k
# Earning 10% a year for 10 years
V1 = fn_compound_interest(P = 50000, r = 0.10, n = 12, t = 10)
V2 = fn_compound_interest(100000, 0.10, 12, 10)
V3 = fn_compound_interest(r = 0.10, P = 100000, t = 10, n = 12)
print(V1)
print(V2)
print(V3)
135352.0745431122
270704.1490862244
270704.1490862244
\(f(x) = x^2 + 2x + 1\)
numeric_grade
status = pass
status = fail
status
my_function = lambda parameters: expression
fn_squared = lambda x: x**2
fn_iseligible_vote
For
loop + Functionslist_ages = [18,29,15,32,6]
Most variables we have seen so far are in the global scope
They are stored in the global namespace and are accessible from anywhere in the code
Let’s create a function that sums 3 numbers
\(f(x,y,z) = x + y + z\)
We will pass the numbers as arguments to the function
NameError
x
is a local variable to the function print_x()
# This is an example where we define a quadratic function
# (x,y) are both local variables of the function
#
# When we call the function, only the arguments matter.
# any intermediate value inside the function
def fn_square(x):
y = x**2
return(y)
x = 5
y = -5
print(fn_square(x = 1))
print(x)
print(y)
1
5
-5
global
keywordglobal
keyword tells Python that you want to use the global variable, not create a new local variableglobal
?
global
sparingly, if at all! 😉modify_x()
again?global y
inside fn_square
?print()
, len()
, sum()
, etc['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BaseExceptionGroup', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EncodingWarning', 'EnvironmentError', 'Exception', 'ExceptionGroup', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'PythonFinalizationError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_IncompleteInputError', '__IPYTHON__', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'display', 'divmod', 'enumerate', 'eval', 'exec', 'execfile', 'filter', 'float', 'format', 'frozenset', 'get_ipython', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'runfile', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
Here’s how an inner function can access a variable from its enclosing (outer) function:
def outer_function():
outer_variable = "I'm from the outer function!" # Variable in outer_function's local scope
def inner_function():
# inner_function can 'see' and use outer_variable
# because outer_variable is in its enclosing scope.
print(f"Inner function says: {outer_variable}")
print("Calling inner_function from outer_function...")
inner_function() # Call the inner function
# Now, let's call the outer_function to see it in action
outer_function()
Calling inner_function from outer_function...
Inner function says: I'm from the outer function!
In this example, outer_variable
is “enclosing” for inner_function
.
If the inner function defines a variable with the same name as one in the outer function, the inner function will use its own local variable. This is called “shadowing”.
def outer_function_shadow():
message = "This is the OUTER message." # Variable in outer_function_shadow's scope
def inner_function_shadow():
message = "This is the INNER message!" # This is a NEW, LOCAL variable for inner_function_shadow
# It "shadows" the outer 'message'
print(f"Inside inner_function_shadow: {message}")
print(f"Before calling inner, outer message is: {message}")
inner_function_shadow()
print(f"After calling inner, outer message is still: {message}") # Unchanged
outer_function_shadow()
Before calling inner, outer message is: This is the OUTER message.
Inside inner_function_shadow: This is the INNER message!
After calling inner, outer message is still: This is the OUTER message.
The message
inside inner_function_shadow
is a completely separate variable from the message
in outer_function_shadow
.
Consider the following code:
#| echo: true
#| eval: false # For students to predict first
a = 10 # Global variable
def func_one():
b = 20 # Local to func_one
print(f"Inside func_one: a = {a}, b = {b}")
def func_two():
c = 30 # Local to func_two
# 'a' is global, 'b' is from enclosing scope of func_one
print(f"Inside func_two: a = {a}, b = {b}, c = {c}")
func_two()
# print(f"Inside func_one, trying to print c: {c}") # What would happen here?
func_one()
# print(f"Outside all functions: a = {a}")
# print(f"Outside all functions, trying to print b: {b}") # What would happen here?
# print(f"Outside all functions, trying to print c: {c}") # What would happen here?
print
statement will output, or if it will cause an error.b
and c
outside their scopes) and observe the errors. Why do they occur?a
, b
, and c
?a = 10 # Global variable
def func_one():
b = 20 # Local to func_one
print(f"Inside func_one: a = {a}, b = {b}")
def func_two():
c = 30 # Local to func_two
# 'a' is global, 'b' is from enclosing scope of func_one
print(f"Inside func_two: a = {a}, b = {b}, c = {c}")
func_two()
# print(f"Inside func_one, trying to print c: {c}") # What would happen here?
func_one()
print(f"Outside all functions: a = {a}")
Inside func_one: a = 10, b = 20
Inside func_two: a = 10, b = 20, c = 30
Outside all functions: a = 10