Welcome to CS 41! In the first two weeks of CS 41, we're going to learn how to "translate" basic programming concepts into Python and learn a bit about Python's philosophy along the way. You should already know words like "variable", "string", and "function" so we won't explain what they are in these notes — instead, we'll focus on how those concepts are represented in Python.
The interactive interpreter is one way to execute Python code. Once you've installed Python, you can start the interactive interpreter in a few ways.
- Open the Terminal and start the interactive interpreter in it. On Windows, you can access this by clicking Start, typing "cmd" into the search box, and clicking on "Command Prompt". On Mac, the Terminal application is in Applications > Utilities > Terminal. You should be greeted with a blinking cursor in front of a dollar sign. Type
py
if you're on Windows orpython3
if you're on Mac. The screen should look like:
The interactive interpreter in a terminal on my Mac computer. - We can also run the interactive interpreter through VS Code. Open VS Code and go to the menu. On Windows, click View > Terminal and on Mac click Terminal > New Terminal. A terminal should appear at the bottom of the screen. Type in
py
on Windows orpython3
on Mac.
In both of these methods, notice that just below the line where I typed python3
starts with "Python 3.8.1 (default, ..." — it's important that you're running Python 3. If you see Python 2 on that line, you should revisit the install instructions to make sure that you have access to the latest version of Python.
For the rest of this course, we'll use screenshots from the terminal version of the intereactive interpreter, but the content should be the same as the VS Code version.
You can type any Python code into the interactive interpreter and it will execute in realtime. This is actually a big deal! If you wanted to execute code in C++, you'd have to write it, compile it, and then execute all of the commands at once. The interactive interpreter lets you execute small portions of Python code more quickly. This is especially beneficial when you're introducing a new feature to a large project and don't want to compile the entire project to test the new feature.
After the >>>
sign, you can type any Python code, press enter, and it'll get executed. The interpreter will display the result of the code on the line below where you typed it in. For example,
You can add numbers in Python and in the interactive interpreter!
In other languages like Java and C++, you need to tell the language a variable's type when it's declared.
int x = 41;
The keyword int
in the above line tells the compiler that x
will hold an integer. It would be illegal to reassign x = "Hello world"
because x
can only hold an integer for its entire lifetime.
That's not true in Python. Variables are just references to objects and the type information is stored on the object. In practical terms, this means that a variable can be reassigned from one object to another even if those objects have different types. Consequently, you don't need to say what the type of a variable will be when its declared in Python.
x = 41
x = "Unicorns rock!"
The type
function will return information about the type of an object.
type(41) # => int
type("Unicorns rock") # => str
You'll rarely use type
in production Python code, but it's useful in the interactive interpreter if you need to check what the type of some object is.
There are three numeric types in Python: int
, float
, and complex
. Let's talk about the first two. Integers are whole numbers without decimal places, just like in other languages. Floating point numbers can have decimal places.
Python supports many math operations. You'll probably use all of these at some point in your Python journey:
5. # => 5 (int)
5.0 # => 5.0 (float)
1 + 8 # => 9 (int)
8 + 5.1 # => 13.1 (float)
1 - 8 # => -7 (int)
2 * 4 # => 8 (int)
2 / 3 # => 0.666666 (float)
7 // 3 # => 2 (int; integer division)
7 % 3 # => 1 (int; modulo/remainder operator)
3 ** 3 # => 27 (int; exponential operator)
If you have a variable x = 40
and want to add one to the value of that variable, there are two main ways to do that.
x = x + 1
The line x = x + 1
might seem strange. If you read this line like it's a mathematical equation, it seems like a contradiction. The Python interpreter, though, reads this differently: Python will execute the right hand side of this expression first (x + 1
is 40 + 1
which is 41
) and then reassign x
to be equal to that value. Altogether, this adds one to the value of x
.
There's a shorter way to do this in Python:
x += 1
In fact, you can combine any mathematical operator with the equals sign, and Python will update the variable accordingly.
x += 5 # => x = x + 5
x -= 3 # => x = x – 3
x *= 2 # => x = x * 2
x /= 2 # => x = x / 2
x //= 4 # => x = x // 4
x %= 3 # => x = x % 3
x **= 3 # => x = x ** 3
A boolean object is either True
or False
. Python uses the words True
and False
to represent these concepts and, under the hood, they are secretly the numbers 1
and 0
respectively.
The not
keyword will flip a boolean from True
to False
or vice versa. The and
and or
keywords can be placed between two boolean expressions and work like you'd expect from other languages. Here's an example of how these work:
not True # => False
True and False # => False
True and True # => True
True or False # => True
False or False # => False
Finally, you can compare two objects in several ways.
a == b
ifa
is equal tob
.a != b
ifa
is not equal tob
.a > b
ifa
is strictly greater thanb
.a >= b
ifa
is greater than or equal tob
.
You can also chain multiple comparators together and they are combined using and
. For example,
a > b > c
means(a > b) and (b > c)
a < b > c
means(a < b) and (b > c)
a == b > c != d
means(a == b) and (b > c) and (c != d)
Many of the above examples that involve chaining operators can be confusing to read, so you should try to avoid them when possible. For example, it's probably cleaner to write (a < b) and (b > c)
instead of a < b > c
.
Here are some examples of comparing numbers in Python:
5 == 5 # => True
1 != 100 # => True
3 > 6 # => False
4 >= 9 # => False
3 > 2 > 1 # => True (3 > 2 and 2 > 1)
In Python, a sequence of characters between a pair of double quotes ("..."
) or single quotes ('...'
) is a string. Unlike other languages, Python doesn't distinguish between strings of length one (char
s in Java and C++) and longer strings.
Use a backslash to escape characters within a string. For example,
'If you hadn\'t nailed it to the perch, it\'d be pushing up the daisies!'
"Palin made a good point: \"Nobody expects the Spanish Inquisition\""
'"Life\'s like a movie. Write your own ending." - Kermit the Frog'
There's no stylistic preference in Python about using double quotes or single quotes – just use whichever is most convenient!
There are two string operations that are commonly used in Python: adding strings together, which concatenates them, and multiplying a string by an integer, which repeats the string that many times.
For example,
"Unicorns are " + "the best" # => 'Unicorns are the best'
"I love unicorns! " * 3 # => 'I love unicorns! I love unicorns! I love unicorns! '
In Python, like most other languages, you can access the i
th character of a string s
with s[i]
. Python is also zero-indexed, meaning that s[0]
is the first character of s
.
For example,
course = "hap.py code"
course[2] # => 'p'
You can do more with brackets in Python! The bracket notation is used for string slicing, which allows you to pull out subsets of the string. If you take a string s
, and write s[start:stop:step]
where start
, stop
, and step
are integers, that will evaluate as a "slice" of s
that starts at index start
, ends at index stop
, and steps by the value of step
. step
is an optional parameter.
That's a bit confusing, so let's continue the above example to see how this works:
course[1:3] # => 'ap'
course[1:8] # => 'ap.py c'
course[1:8:2] # => 'a.yc'
There are a few things to note about the slice notation.
- String slices include the start index and exclude the stop index. In the above example,
course[1:3]
includescourse[1]
('a'
) andcourse[2]
('p'
) but does not includecourse[3]
('.'
). This is designed so that the number of characters returned isstop - start
. - The step size tells Python to skip characters in the string. A step size of
2
means that Python will put every other character in the string slice, a step size of3
means that Python will put every third character in the string slice, etc.
Additionally, the step size in a slice can be negative! When you slice with a negative step, you need to reverse the start and end indices of the slice because the slice will start at the later index, work backwards, and end at the earlier index. However, Python will still include the character at the start index. This means that course[1:8]
is not the reverse of course[8:1:-1]
because the second string includes the course[8]
but the first string does not. For example,
course[8:1:-1] # => 'oc yp.p'
Finally, if you omit any of the indices, but leave the colons and specify others, Python will try to guess what you mean. If you omit the start or end index, Python will slice as far as possible in that direction. If you omit the step size, Python will interpret that as one.
course[:3] # => 'hap'
course[5:] # => 'y code'
course[5::-1] # => 'yp.pah'
To be explicit, course[5::-1]
tells Python to start at the fifth character (the "y" at the end of "hap.py") and step by negative one. Python includes that "y" and steps backwards to the beginning of the string.
This gives a really nice way to reverse a string:
course[::-1] # => 'edoc yp.pah'
Lists are Python's version of Java's ArrayList
or C++'s vector
. They're ordered collections of objects.
You declare a list using square brackets ([]
) with optional items inside. For example,
empty = []
letters = ["a", "b", "c"]
numbers = [1, 2, 3]
Lists can contain elements of different types (even other lists!). For example,
many_types = ["a", 2, 3, [4, 5]]
The most common operations that are performed on a list are appending and extending a list.
Appending to a list allows you to add a single element to the end of the list. To continue the previous example,
numbers.append(4)
numbers # => [1, 2, 3, 4]
Extending a list allows you to add several elements to the end of the list.
numbers.extend([5, 6, 7, 8])
numbers # => [1, 2, 3, 4, 5, 6, 7, 8]
Note that when you extend a list, you have to provide a collection of numbers to add on.
Strings and lists are both collections: strings are collections of characters and lists are collections of arbitrary objects. There are a few operations that will show up repeatedly in Python world which apply to collections.
First, you can get the number of elements in a collection using the len
function. For strings, this is the number of characters. For lists, this is the number of elements in the list.
len([]) # => 0
len("CS41") # => 4
len([1, 2, 3, "Parth"]) # => 4
You can also check if a specific element is in your collection using the in
keyword. This checks if a string is a subset of another string or if an object is in a list.
0 in [2, 3, 4] # => False
"5" in [3, 4, 5, "CS41"] # => False
"tho" in "Python" # => True
Control flow refers to a program's ability to do different things based on the program state or inputs. In Python, this is often implemented with an if
statement. Here's what that looks like:
if some_condition:
# do something...
In the above example, notice that you don't need parentheses around the condition, we place a colon at the end of the line, and the next line is indented with four spaces. Indentation is very important in Python – where other languages use squiggly braces ({}
) to denote nesting, Python relies on indentation.
some_condition
in the above example, will be evaluated as a boolean. For example,
if 8294 % 3 == 0:
print("8294 is divisible by three.")
Here, 8294 % 3 == 0
is a boolean expression, and the if statement will execute if it is true.
You can extend the if
statement by introducing elif
and else
. For example,
if 8294 % 3 == 0:
print("8294 is divisible by three.")
elif 8294 % 2 == 0:
print("8294 is divisible by two but not three.")
else:
print("8294 is neither divisible by two, nor three.")
As the print statements suggest, Python will read an if/elif/else
block from top to bottom. It first checks the if
statement, then any elif
statements in the order they're written, then the else
statement. Python wil execute the code in the first block whose condition is true and skip the remaining blocks.
The condition of an if
statement does not need to be a boolean expression! It will, however, be interpreted as a boolean value. All objects in Python are truthy or falsy – they behave like True
or False
when cast as booleans.
To see what this means, we can use the bool
function to convert objects into booleans. Zero values and empty data structures are falsy:
bool(0) # => False
bool(0.0) # => False
bool("") # => False
bool([]) # => False
Most other things are truthy:
bool(42) # => True
bool("Sam Redmond") # => True
bool([1, 2, 3]) # => True
This is most often used to check whether some operation can be performed on a data structure where that operation wouldn't work if the data structure is empty. For example,
students = []
if students:
print(students[0] + " is the first student.")
else:
print("There are no students!")
For this reason, we rarely ever check if len(collection) == 0:
in Python. It's usually the same as checking if collection:
.
Imagine if you wanted to insert the value of a variable into a string. Maybe you'd like to print out the value of a number with some words. The best way to do this in Python is to create a template string and use the .format
method.
In its most basic form you can do this by adding a pair of squiggly braces into a string and listing the arguments you'd like to substitute into the string:
'{} {} 💜'.format('beautiful', 'unicorn') # => 'beautiful unicorn 💜'
You can write indices inside the squiggly braces and Python will replace them with the corresponding argument.
'{0} can be {1} {0}, even in summer!'.format('snowmen', 'frozen')
# => 'snowmen can be frozen snowmen, even in summer!'
You can also provide placeholders and specify their value in the call to .format
.
'{name} loves {food}'.format(name='Michael', food='sno-cones')
# => 'Michael loves sno-cones' (he does)
And finally, values passed to .format
will automatically be converted to strings.
'{} squared is {}'.format(5, 5**2) # => '5 squared is 25'
In all of the above examples, we've passed explicit strings into the .format
function but in practice you'd often use variables. For example,
name = 'Parth'
fav_animal = 'unicorn'
'{} loves {}s'.format(name, fav_animal) # => 'Parth loves unicorns'
Another option for string formatting that's less supported is to use f-strings. F-strings were introduced in Python 3.6 and because they're are fairly new, we recommend using .format
.
An f-string is a string that's declared with the character f
before the opening quotation. Any value placed inside squiggly braces will be directly evaluated and converted to a string. For example,
f'5 squared is {5 ** 2}' # => '5 squared is 25'
name = 'Parth'
fav_animal = 'unicorn'
f'{name} loves {fav_animal}s' # => 'Parth loves unicorns'
You can specify padding and numeric specifiers within the a format string as well. For example,
'{:.1%} of the population loves unicorns'.format(0.9876)
# => '98.8% of the population loves unicorns'
'{:10}'.format('left') # => 'left '
'{:*^12}'.format('CS41') # => '****CS41****'
See https://pyformat.info/ for a detailed guide to string formatting style specification.
At a basic level, string formatting can be completed using the +
operator, since Python strings overload the +
operator to perform string concatenation. (This method of string formatting generally more difficult to read, less fun to write, slower, and doesn't allow for styling - for these reasons reason, using +
for string formatting is generally to be avoided).
score = 10
print("Parth is a " + str(score) + "/10 person.") # => Parth is a 10/10 person.
Python also supports C-style string formatting - this is definitely an improvement from using the +
operator for everything, but can get somewhat unwieldy, especially since, relative to most of the rest of Python, the C-string formatting operators are less human-readable, and less intuitive to many students.
print("Parth is a %d/10 person" % (10)) # => Parth is a 10/10 person.
print("Parth enjoys apple %.2f. (He does)." % (3.14159)) # => Parth enjoys apple 3.14. (He does).
But say, for example, that we wanted to print out the lecture schedule for Parth and Michael in CS 41. Since we're alternating lectures, we would need to print out something like, "Parth, then Michael, then Parth...
, which, in C-style string formatting, reads as follows:
print("%s then %s then %s then %s then %s." % ("Parth", "Michael", "Parth", "Michael", "Parth"))
Does it work? Sure. But is it really any better than just writing out the string in standard form? Not much. This is where Python's format
method shines: it presents complex string formatting techniques for a diverse array of cases.
Beyond the format
method, in Python 3.6, Python introduced f-strings, which bring the power of the format
method into a more natural syntax. Rather than needing to pass in arguments as parameters to the format
function, using an f-string (denoted by an f
or F
immediately preceding the string), one can instead place the argument name between the curly braces within the string, as below:
name = "Parth"
adjective = "best"
print(f"{name} is the {adjective}!") # => Parth is the best!
As with the format
method, f-strings also perform automatic type conversion.
score = 10
print(f"Parth is a {score}/{score} person.") # => Parth is a 10/10 person.
Like other languages, the two main types of loops in Python are for
loops and while
loops.
In most other languages, for
loops are done with an index variable. In C++ for example, you might print out the square numbers from one to ten with the following code:
for (int i = 1; i <= 10; i++) {
std::cout << i * i << std::endl;
}
Python, however, does not support index-based iteration. Instead, a for loop in Python looks roughly like:
for item in collection:
... # do something with item
Here, collection
is some collection of objects (like a string (collection of characters) or a list (collection of arbitrary objects)). We'll make this notion more precise later in the course!
So, for example, here's a fully functional for loop:
for num in [8, 6, 7, 5, 3, 0, 9]:
print(num)
# 8
# 6
# 7
# 5
# 3
# 0
# 9
You can also iterate through the characters of a string:
for ch in "CS41":
print("{} (yeah!)".format(ch))
# C (yeah!)
# S (yeah!)
# 4 (yeah!)
# 1 (yeah!)
Suppose you'd like to have an index iterate through a sequence of numbers like you might in C++. There is a way to do this in Python – it uses the range
function.
The range
function generates a sequence of numbers and it's called as range(start, stop, step)
. Look familiar? These numbers have the same significance as string slicing! The end and step values are optional, start is included in the range, and stop isn't included in the range.
For example, here's what's generated by a few range
calls:
range(3) # => <0, 1, 2>
range(5, 10) # => <5, 6, 7, 8, 9>
range(2, 12, 3) # => <2, 5, 8, 11>
range(-7, -30, -5) # => <-7, -12, -17, -22, -27>
And here's how you might use this in a for
loop:
for i in range(40, 42):
print(i)
# 40
# 41
Like other languages, Python while
loops instruct your computer to keep repeating a block of code while some condition is true.
Continuing the indentation paradigm, a while loop in Python generally looks like:
while condition:
... # do something
For example, we might print the powers of three under 10,000 with the following code:
n = 1
while n < 10000:
print(n)
n *= 3
# 1
# 3
# 9
# 27
# 81
# 243
# 729
# 2187
# 6561
break
and continue
are statements that can be put into loops which instruct the computer to skip some of the code it was going to execute. If Python is running a loop and encounters a break
statement, it stops executing the loop. If Python encounters a continue
statement, it proceeds to the next iteration of the loop without executing the rest of the code in the loop.
Here's an example where we cut the execution of a for
loop short using a break
statement:
for i in range(2, 10):
if n == 6:
break
print(n)
# 2
# 3
# 4
# 5
And here's an example of using the continue
statement to jump to the next iteration of a for
loop:
for letter in "UNICORN":
if letter in "UN":
continue
print(letter)
# I
# C
# O
# R
Python uses the def
keyword to define a function:
def function_name(param1, param2):
# do something
return some_value
Let's unpack that definition a bit more:
function_name
is the name of the function we're defining. To call this function, you'd invoke it usingfunction_name(...)
.param1
andparam2
are two parameters that must be passed into this function. When calling the function, you'd provide those parameters, separated by commas, in the appropriate order, between parentheses. For example, to call the function withparam1
equal to 41 andparam2
equal to 42, we'd writefunction_name(41, 42)
.- The function will return some value – that means it'll send the value to the place that the function is being called from. To capture the value that a function returns, you can assign it to a variable. For example,
def add(a, b):
return a + b
three_and_five = add(3, 5)
print(three_and_five)
# 8
In the above example, the add
function returns the sum of its two parameters and after it is defined, we use it to add three and five. The resulting value (8) is stored in the variable three_and_five
.
In Python, every function returns some value. If the function doesn't explicitly have a return
statement, it'll return the value None
(which is like nullptr
in C++).
Python strings, as we have seen previously, are defined by either double quotes ("
) or single quotes ('
). If a string is defined by double quotes, single quotes may be used within the string without terminating the string; the converse also holds, that if the string is defined by single quotes, double quotes may be used without terminating the string.
print("I'm dancing.") # => I'm dancing.
print('"This is fun!" he cried.') # => "This is fun!"
However, if a string is defined by single quotes, and it is desired to use single quotes within the string, the quotation character must be escaped with a forward slash immediately prior to the character so that Python knows not to read the quotation character as a string terminator. The same applies to double quotes. Below, we've written some examples.
print('I\'m dancing.') # => I'm dancing.
print("\"This is fun!\" he cried.") # => "This is fun!" he cried.
Methods are functions which can be applied to an object of a given class. As it turns out, Python strings have a number of builtin methods which are useful for string processing operations. First, we examine a small collecton of functions built for case conversion:
print("Elephants are the best.".lower()) # => elephants are the best.
print("Elephants are the best.".upper()) # => ELEPHANTS ARE THE BEST.
print("Elephants are the best.".title()) # => Elephants Are The Best.
Python also provides methods to check whether a string is written in a given case:
print("elephants are the best.".islower()) # => True
print("ELEPHANTS ARE THE BEST.".isupper()) # => True
print("Elephants Are The Best.".istitle()) # => True
Python strings also contain a strip
method - as an argument, it takes in a string of leading and trailing characters specifying the characters to be stripped from the string; if none are included, it defaults to stripping leading and trailing whitespace.
print(" Elephant brains weigh up to 5.4kg. ".strip()) # => Elephant brains weigh up to 5.4kg.
print("Elephant brains weigh up to 5.4kg.".strip("Eg.l")) # => ephant brains weigh up to 5.4k
Python strings also include find/replace methods. The find
method takes in a substring and returns the lowest index where the substring is found within the string on which the method was called. The replace
method takes in an old
string and a new
string, and returns a string with all occurrences of the old
argument replaced by the new
argument.
print("Elephants are the best.".find("are")) # => 10
print("Mice are the best.".replace("Mice", "Elephants")) # => Elephants are the best.
In Python, a single-line comment is denoted with the hash symbol.
# This is a single-line comment.
Multi-line comments are placed between three single-or-double quotation marks.
"""
Multi-line comments
Lie between quotations marks
This is a haiku
"""
Console I/O in Python is provided through the input
function. The input
function takes in a string as a parameter, which is a prompt which it will present to the user. The user is then prompted to type in a string as a response, which the input
function will then return. Below, we provide an example of leveraging the input function to gather input about the user's favourite animal:
favourite_animal = input("What is your favourite animal? ")
When run, this code will prompt the user, as follows:
What is your favourite animal?
At which point, the user can enter in a response at the prompt, as follows:
What is your favourite animal? <b><i>Elephants.</b></i>
After the user presses return
, the favourite_animal
variable will now be set to the string "Elephants."
.
favourite_animal == "Elephants." # => True
Python's approach to File I/O revolves around the file object. In Python, a file object can be obtained through a call to the open
function, as follows:
f = open(filename, 'r')
The open function takes in a file name (a string representing a path, which can either be absolute or relative), and a mode in which to open the file (this can either be 'r'
for read, 'w'
for write, b
for binary mode; and +
can be used to open files in multiple modes, where r+
and w+
both indicate to open a file for reading and writing). It returns a file object, which can be read from and written to using the following methods.
Python files can be read from using any of the following methods - note that a line refers to each string of text in the file, delimited by teh newline character \n
.
file_text = f.read(size) # Reads size bytes from the file; if size is omitted, the entire file is read.
first_line = f.readline() # Reads the file one line at a time.
all_lines = f.readlines() # Returns a list with all lines of the file.
Alternatively, Python file objects support looping, and so the following code is valid to loop over each line of the file in succession:
for line in f:
# Do something with each line.
To write to a file object, Python has implemented the write
method. It takes in a string as an argument, writes the string to the file (overwriting any data which previously existed in the file), and returns an integer of the number of bytes which have been written to the file.
f.write("Contrary to popular belief, elephants are not scared of mice.")
After opening a file and reading from or writing to it, it's important that the file be closed to free up system resources that the open file object is using. This can be done using the close
method, as follows:
f.close()
To check if a file is open or closed,closed
method returns a boolean depending on whether the file object is open or closed.
f.closed # => True if file object is closed, False otherwise.
An alternative to the open/close
file paradigm is using a context manager - the advantage of using a context manager over the open/close
paradigm is that, if an error is thrown during execution, the context manager will ensure that the file is automatically closed. (In contrast, in the open/close
paradigm, an error thrown before execution reaches the call to close
will cease execution and will exit the program, potentially leaving the file open). Though it is possible to obtain the same functionality with exception handlers, context managers provide a streamlined interface to safely ensure a file is closed if an error is thrown. Context managers are created using the with
keyword, as follows:
with open(filename, 'r'):
# Do something with the file, like read its contents - but safely this time. :)
file_text = f.read()
It's almost always a better idea to use the paradigm of the context manager when performing File I/O in Python. Cases where it is superior to use the open/close
paradiem are significantly fewer - only use it if you have a clear, good reason to do so.
Up until this point, we have seen Python code being run in the interactive interpreter - and though the interactive interpreter is great, it's less great for code reusability. If there's a Python function which we desire to use again and again and again, what is the most effective way to do so?
Python's answer to this question is the module. By default, every Python file is a module. A module, broadly, consists of two parts - depending on the use case, either may be omitted without consequence beyond losing the local functionality of the omitted segment. The structure of a Python file is as follows - going forward, let's assume this file is called hello.py
. Python files can be written in any text editor of choice - I (Michael) personally am a fan of Sublime Text and Visual Studio Code as my go-to text editors for writing .py
files.
#!/usr/bin/env python3
def do_stuff():
# A function which does some stuff.
print("Hello!")
return
if __name__ == "__main__":
# __name__ == "__main__" when a function is executed as a script.
do_stuff()
The first line is called a shebang, and it makes sure that the file can be executed as a script - we'll revisit this more shortly.
There are three ways, then, in which modules (saved as .py
files) can be run.
We can run modules in the interactive interpreter using the -i
flag when we enter the interactive interpreter from our command line, as follows (type the command after the $
at the command prompt in our Terminal):
$ python3 -i hello.py
Python 3.8.0 (v3.8.0:fa919fdf25, Oct 14 2019, 10:23:27)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
This has dropped us into an interactive interpreter in which we have access to the functions we have defined in the file hello.py
. If, for example, we wish to call the do_stuff()
function, we may do so without re-defining it in our interactive interpreter, as follows:
>>> do_stuff()
Hello!
Earlier, we saw the if __name__ == "__main__":
condition statement (this is often called an if main); this code block is entered into if the module in question is run as a script. We can run a file as a script by marking it as executable through the chmod +x <filename>
command, then, assuming the file has a properly-defined shebang (the fancy commented line we see at the top of the Python file we wrote in the prior section), the file will execute the code within the if main as a script on the command line.
$ chmod +x hello.py
$ ./hello.py
Hello!
$
Here, we see that once we executed the file hello.py
as a script, Hello!
was printed to the command line, since we call the do_stuff()
function within the if main of our file.
Python files can also be imported into other Python files, in much the same way as when running modules in the interactive interpreter. The import
statement can be used to pull functionality from one Python file into another, and can be entered both within a Python file, or at the interactive interpreter. Below, we will go through one example of each.
First, we provide an example of importing functionality at the interactive interpreter - here, it is crucial to start the interactive interpreter from the same directory as that which contains the desired import file (or, alternatively, define the file path in the import statement as a relative path from the directory in which the interactive interpreter is being run).
$ python3
Python 3.8.0 (v3.8.0:fa919fdf25, Oct 14 2019, 10:23:27)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
Here, we have imported the file hello.py
into our local environment - we therefore have access to all the functions defined in that file. Therefore, we can execute the following in our interactive interpreter:
>>> hello.do_stuff()
Hello!
Here, we reference the do_stuff()
function by writing hello.do_stuff()
, and the function do_stuff()
will execute just as if we had defined it within our session in the interactive interpreter.
The same behaviour holds for imports in Python files. If we write a file nice_day.py
(we assume here, that nice_day.py
is in the same directory as hello.py
), we could write it as follows:
# /usr/bin/env python3
import hello
def nice_day():
print("Have a nice day!")
return
if __name__ == "__main__":
hello.do_stuff()
nice_day()
Then, running this file as a script would yield:
$ ./nice_day.py
Hello!
Have a nice day!
As we have imported the functionality from hello.py
into the file nice_day.py
.
Additionally, since we only intend to use one function from hello.py
in our file nice_day.py
, we could have written,
from hello import do_stuff
After which we could have called the do_stuff()
function simply as do_stuff()
, rather than hello.do_stuff()
.
Python has a number of builtin modules which can be imported to provide enhanced functionality. As a brief example, we will provide an example of importing the math
module and accessing some of the functions it defines.
import math
math.gcd(45, 105) # => 15
math.cos(3.14) # => -0.9999987317275395
math.cos(math.pi) # => -1.0
Here, observe that we use the same import
statement as we used to import the hello.py
file. Notably, unlike with the hello.py
example, the directory in which we run this import statement is irrelevant - since the math
module is built in to Python, Python will handle the file path automatically to bring us the math
module when we import it.
With love, 🦄s, and 🐘s by the CS41 Staff