Wednesday, February 11th, 2026¶

On Monday, we wrote code that would determine whether a given value n was prime or not.

In [4]:
n = 113

n_is_prime = True

for d in range(2, n):
    if n % d == 0:
        n_is_prime = False
        break

if n_is_prime:
    print('{} is prime.'.format(n))
else:
    print('{} is not prime.'.format(n))
113 is prime.

We also discussed using while loops in situations where we don't know how long we will need to iterate before finding the desired data.

Exercise: Write code that will find the first $100$ cubes that have remainder $1$ after division by $4$.

In [2]:
cubes_with_remainder_1 = []

n = 1
while len(cubes_with_remainder_1) < 100:
    cube = n**3
    if cube % 4 == 1:
        cubes_with_remainder_1.append(cube)
    n += 1
In [4]:
print(cubes_with_remainder_1)
[1, 125, 729, 2197, 4913, 9261, 15625, 24389, 35937, 50653, 68921, 91125, 117649, 148877, 185193, 226981, 274625, 328509, 389017, 456533, 531441, 614125, 704969, 804357, 912673, 1030301, 1157625, 1295029, 1442897, 1601613, 1771561, 1953125, 2146689, 2352637, 2571353, 2803221, 3048625, 3307949, 3581577, 3869893, 4173281, 4492125, 4826809, 5177717, 5545233, 5929741, 6331625, 6751269, 7189057, 7645373, 8120601, 8615125, 9129329, 9663597, 10218313, 10793861, 11390625, 12008989, 12649337, 13312053, 13997521, 14706125, 15438249, 16194277, 16974593, 17779581, 18609625, 19465109, 20346417, 21253933, 22188041, 23149125, 24137569, 25153757, 26198073, 27270901, 28372625, 29503629, 30664297, 31855013, 33076161, 34328125, 35611289, 36926037, 38272753, 39651821, 41063625, 42508549, 43986977, 45499293, 47045881, 48627125, 50243409, 51895117, 53582633, 55306341, 57066625, 58863869, 60698457, 62570773]

Exercise: Write code that will find the first $10$ prime numbers.

In [9]:
primes = []

n = 2

while len(primes) < 100:
    n_is_prime = True
    
    for d in range(2, n):
        if n % d == 0:
            n_is_prime = False
            break

    if n_is_prime:
        primes.append(n)
    
    n += 1
In [11]:
print(primes)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541]

Notice that in the above cell we had to duplicate our code for testing whether a number was prime. It would be nice if we could somehow wrap up our prime-testing code into a single tool that we could then call upon whenever needed.

Functions in Python¶

Very often, we want to write code that can be applied to many different inputs. We can accomplish this by writing a Python function. The syntax for defining functions in Python is: def <function name>(<some inputs, separated by commas>): ...do something...

In [12]:
def f():
    print("Hello world!")

After defining a function, we can call upon it to execute the function's code. To call a function, we simply write <function name>(<whatever inputs that are necessary, separated by commas>).

In [13]:
f()
Hello world!
In [14]:
def g(x,y):
    print(x + y)
In [15]:
g(1,2)
3

Exercise: Write a function called is_prime that takes in a variable n and prints whether or not n is prime. Then test your function against the integers from $2$ to $40$.

In [16]:
def is_prime(n):
    n_is_prime = True

    for d in range(2, n):
        if n % d == 0:
            n_is_prime = False
            break
    
    if n_is_prime:
        print('{} is prime.'.format(n))
    else:
        print('{} is not prime.'.format(n))
In [19]:
is_prime(109)
109 is prime.
In [22]:
for n in range(2,41):
    is_prime(n)
2 is prime.
3 is prime.
4 is not prime.
5 is prime.
6 is not prime.
7 is prime.
8 is not prime.
9 is not prime.
10 is not prime.
11 is prime.
12 is not prime.
13 is prime.
14 is not prime.
15 is not prime.
16 is not prime.
17 is prime.
18 is not prime.
19 is prime.
20 is not prime.
21 is not prime.
22 is not prime.
23 is prime.
24 is not prime.
25 is not prime.
26 is not prime.
27 is not prime.
28 is not prime.
29 is prime.
30 is not prime.
31 is prime.
32 is not prime.
33 is not prime.
34 is not prime.
35 is not prime.
36 is not prime.
37 is prime.
38 is not prime.
39 is not prime.
40 is not prime.

The return command¶

It is often more useful to have functions like the above return either a True or a False Boolean depending on whether n is prime or not.

More generally, we often want functions to return some object (e.g. a list, a float, an integer, a string, etc) to the caller. This can be done using a return statement. The syntax is: def <function name>(<some inputs, separated by commas>): ...do something... return <some object to be returned>

When calling a function that returns something, we can store the output as a new variable. For example, output = <function name>(<whatever inputs that are necessary>) will store the returned object as the variable output.

In [23]:
def f():
    print('Hello world!')
    return 'Goodbye'
In [25]:
output = f()
Hello world!
In [26]:
print(output)
Goodbye
In [27]:
def g(x,y):
    return x + y
In [29]:
output = g(1,2)
print(output)
3

Exercise: Rewrite the is_prime function to return a Boolean True if the input is prime and False if not.

In [30]:
def is_prime(n):
    n_is_prime = True

    for d in range(2, n):
        if n % d == 0:
            n_is_prime = False
            break
    
    if n_is_prime:
        return True
    else:
        return False
In [33]:
def is_prime(n):
    n_is_prime = True

    for d in range(2, n):
        if n % d == 0:
            n_is_prime = False
            break
    
    return n_is_prime
In [34]:
is_prime(7)
Out[34]:
True
In [35]:
is_prime(9)
Out[35]:
False

Note: Whenever a function hits a return statement, it will immediately terminate after returning the corresponding object. That is, none of the code in the function will be run after it hits a return statement.

In [36]:
def f(x,y,z):
    prod = x*y*z
    return prod
    print('Nooooooooooo!')
In [37]:
f(1,2,3)
Out[37]:
6
In [ ]:
def is_prime(n):
    for d in range(2, n):
        if n % d == 0:
            return False
    return True

Exercise: Write a function get_primes that takes in an integer $n$ and returns a list of all prime numbers between $2$ and $n$ (inclusively).

In [ ]:
 
In [ ]:
 

Functions an namespaces in Python¶

In Python, there are various levels at which variables can be defined. If we define a variable directly within a cell (or within a loop or if block), then that variable is globally available. We say that it is part of the global namespace. Any other piece of code can call upon this variable as needed.

For example, when writing a function, we can make use of this variable within the function. If a function calls upon a variable that has not been defined within that function, it will look outside to see if it has been defined globally, and if so, use the globally defined value.

In [38]:
variable = 1

def f():
    print('variable =', variable)
In [39]:
f()
variable = 1

On the other hand, variables that are defined within a function only exist locally within that function. Consider the following code snippets that illustrate this point.

In [40]:
def g(x):
    new_variable = x**2
    print('new_variable =', new_variable)
In [41]:
g(5)
print(new_variable)
new_variable = 25
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[41], line 2
      1 g(5)
----> 2 print(new_variable)

NameError: name 'new_variable' is not defined

Notice that, while the function g has internally defined new_variable, that definition does not extend outside of the function. When trying to access new_variable from outside of the function, we get an error.

Similarly, if we redefine a globally defined variable within a function, that new definition only holds within the function.

In [42]:
def h(x):
    variable = x
    print('variable =', variable)
In [43]:
variable = 1

print('variable =', variable)
h(5)
print('variable =', variable)
variable = 1
variable = 5
variable = 1

Notice that, while the function f has internally redefined variable to take on a new value, that has not changed the value of the globally defined variable.

When calling upon functions, they have their own local namespace. That is, they have their own collection of defined variables that is separate from the global namespace. However, as we've seen, these local namespaces can inherit variables from the global namespace if they have not been locally defined.

Example: Write a function that takes in an integer n and returns the first n prime numbers.

In [57]:
def get_n_primes(n):
    primes = []
    m = 2
    while len(primes) < n:
        if is_prime(m):
            primes.append(m)
        m += 1
    return primes
In [58]:
primes = get_n_primes(10)

print(primes)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

In-class exercise: Write a function get_prime_factors that takes in a positive integer n and return a list of all prime numbers that divide n.

In [1]:
def is_prime(n):
    n_is_prime = True

    for d in range(2, n):
        if n % d == 0:
            n_is_prime = False
            break
    
    return n_is_prime
In [ ]: