Python Basics for Computational Physical Modeling

Welcome to Python! This notebook will introduce you to some basic Python concepts that you’ll use in TME 310.

What will be covered

  1. Data Types - Kinds of information
  2. Variables - Where information is stored
  3. Numbers and Math - How calculations are done
  4. Strings - Text objects
  5. Lists - A built in way to organize data
  6. Conditionals - How we ask questions about data
  7. Loops - Doing repetitive things automatically

Data Types

There are different kinds of information we can store in Python - these are called types. Python has several built in data types. Today, we’ll cover some of the most commonly-used ones:

  • int (numeric integers)
  • float (numeric real numbers)
  • bool (true or false)
  • string (text)
  • list (collection of other objects)

int

An int is an integer value (e.g., 1 or -395). In Python, we can check the type of an object with the built-in type() function.

# An integer
type(1)
int
# Another integer
type(-395)
int

float

A float is another numeric data type in Python. Unlike an int, a float is a real number that can have several decimal places. For example:

# A floating point number
type(9.81)
float

Note that any number with a decimal place will be interpreted as a float by Python, even if there’s nothing after the decimal place:

# This is also a floating point number
type(1.)
float

bool

A bool (boolean) represents True or False values. These are useful for yes/no questions.

# In Python True and False are capitalized
type(True)
bool
type(False)
bool

str

A string (or str) represents text. Strings are surrounded by quotes. Either single or double quotes can be used to surround strings:

# A double-quoted string
type("hello")
str
# A single-quoted string
type('world')
str

It’s considered best practice to use double quotes for strings even though single quotes are accepted by Python. More importantly, whichever kind of quote starts the string has to be used to close it, or you’ll get a syntax error!

# This will cause an error!
type('mismatch")
  Cell In[9], line 2
    type('mismatch")
         ^
SyntaxError: unterminated string literal (detected at line 2)

list

A list is a collection of other objects. Lists are surrounded by square brackets [] with their elements separated by commas.

# A list of integers
type([1, 2, 3])
list

The elements of a list don’t need to be the same type.

# A list of mixed types
type([1, "two", 3.0, True])
list
# Even another list:
type([1, 2, ["I'm", "a", "list"], 4])
list

Variables

Variables are names that refer to objects. We create variables using the assignment operator =.

x = 5
x
5

Jupyter will automatically display the output of the last line in a cell if it is an expression or variable, and not assigned to anything. That’s why writing x on the last line in the cell above resulted in the value assigned to x being displayed.

But that automatic display only happens for the last line in a cell.

# No output here
y = 10
y
z = 37

In general, it’s better to explicitly tell Python what to display with the print() function, which does not need to be on any particular line.

# Printing the value of y that wasn't shown above
print(y)
10

Any data type can be assigned to a variable:

course = "TME 310A"
students = 25
max_grade = 4.0
fun = True

The value of one variable can be assigned to another, too:

a = 5
print(a)

# Assigning the value of a to b
b = a
print(b)
5
5
Caution

In the case above, a and b are both of type int. If the value of a is changed at some future point in the code, the value of b will be unaffected. But this is not the case for all data types, as we’ll see in a few sections.

Numbers and Math

Most arithmetic operations in Python act like you’d expect. For example:

# Addition
w = 20 + 34
print(w)
54
# Subtraction
x = 10 - 4
print(x)
6
# Multiplication
y = 3 * 7
print(y)
21
Caution

This is a good place to note one of the potential sources of error in Jupyter notebooks.

Now that we’ve just overwritten the variable y with a new value, go back to the Variables section and run the cell that contains print(y). Python will always interpret variables based on their values at the time of execution, which may not reflect the expected values based on a linear reading of the notebook.

Division in Python will often look how you’d expect, but there are some details worth examining. The basic division operator is /, which works on int and float data types, but always returns a float. This is because it converts anything it’s operating on into a float before performing division. So, even if it’s used between two integers and the result could be represented as an integer, the resulting data type will be float.

# Division
z = 15 / 3
print(z) # z is a float even though 15 can be divided by 3 evenly
5.0

To perform integer division in Python, use the // operator. This performs division and returns the result rounded down to the next smallest whole number.

# Returns an integer result (no rounding needed)
15 // 3
5
# This result gets rounded to the next smallest whole number
13 // 3
4

When you first learned to divide, you probably used “remainders” when numbers didn’t divide evenly. So the answer to 13 divided by 3 would have been 4 remainder 1. The integer division operator in Python ignores the remainder, but another useful operator provides it - the modulo operator: %.

# The remainder after integer division
13 % 3
1

The ** operator means “to the power of”. So 2 ** 3 means 2 to the power of 3.

2 ** 3
8

Strings

Although strings are text, not numbers, some of the operators discussed above can be used with strings. But, of course, they have different meanings when text is involved.

For example, “adding” two strings sticks the two pieces of text together:

"hello" + " world"
'hello world'

Multiplying a string creates multiple copies of the original text and sticks them all together:

" alright " * 3
' alright  alright  alright '

Other operators (like - or /) don’t have unique meaning with strings, so they aren’t allowed. Trying to use them will result in an error:

"hello" - " world"  # This will cause an error
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[28], line 1
----> 1 "hello" - " world"  # This will cause an error

TypeError: unsupported operand type(s) for -: 'str' and 'str'

The len() function returns the number of characters in a string:

len("This is a string. It's not super long, but it's long enough that I don't want to count the characters myself.")
109

One of the most useful features Python has for strings is the “F-string”. With F-strings, you can put variables inside your strings (inside curly braces {}) and get the variable value inserted into your string.

course = "TME 310A"
students = 25
max_grade = 4.0

print(f"There are {students} students registered for {course}. The highest grade you can get is {max_grade}")
There are 25 students registered for TME 310A. The highest grade you can get is 4.0

Strings are indexed, which means that each character in the string has a numbered position (called an index). You can access the \(n\)th character in a string putting the index you want in square brackets after the string:

my_string = "abstraction"
my_string[2]
's'

Depending on what languages you’ve programmed in before, you may have expected my_string[2] to return b since that’s the 2nd character in the string. But Python is a “zero-indexed” language, so counting always starts at zero. To get the first character in the string, we need to put 0 in square brackets:

my_string[0]
'a'

Trying to access an index past the end of the string will cause an error:

too_far = len(my_string)
my_string[too_far] # This will cause an error
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[33], line 2
      1 too_far = len(my_string)
----> 2 my_string[too_far] # This will cause an error

IndexError: string index out of range

Notice that the trying to access and index equal to the length of the string causes IndexError: string index out of range because the indexing starts at 0! That means that the last entry in the string will have the value len(my_string) - 1.

In fact, Python supports negative indexing without needing to find the length of the string. The last entry in the string has index -1; and the second to last entry has index -2, and so on.

my_string[-1] # This will give the last character of the string
'n'
my_string[-2] # This will give the second to last character of the string
'o'

Lists

We’ve already seen that lists are containers that hold objects. Now we’ll discuss how to interact with these containers.

Like strings, lists are indexed data types, so we can use the indexing syntax to access specific values in a list:

colors = ["red", "green", "blue"]

# Print the first item in the list:
print(colors[0])
red

One of the key differences between lists and strings is that lists are mutable, while strings are immutable. This means that you can change the value of an entry in a list, but you can’t with a string:

# Change the second color from "green" to "yellow":
colors[1] = "yellow"
print(colors)
['red', 'yellow', 'blue']
# Trying to change one of the characters in a string will cause an error:
my_string[0] = "A" # This will cause an error
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[38], line 2
      1 # Trying to change one of the characters in a string will cause an error:
----> 2 my_string[0] = "A" # This will cause an error

TypeError: 'str' object does not support item assignment

Copying A major implication of whether an object is mutable or not is how Python handles copies of the object. We saw previously that when we made a copy of an integer variable, changing the original variable did not change the copy. This kind of copying is called deep copying. It’s the default behavior for immutable objects in Python so the same applies to strings:

# Make a string and copy it
original_string = "TME 310A"
copied_string = original_string
print(original_string)
print(copied_string)
TME 310A
TME 310A
# Change the original string
original_string = "TME 310B"

# The copy is unchanged
print(original_string)
print(copied_string)
TME 310B
TME 310A

But since lists are mutable, when they’re copied, Python makes what’s called a shallow copy. A shallow copy points to the original rather than reproducing it. This means that a change to the original will immediately cause a change in the “copy”. And, strangely enough, a change in the copy will immediately change the original!

# Make a list and copy it
original_list = [ 1, 2, 3 ]
copied_list = original_list
print(original_list)
print(copied_list)
[1, 2, 3]
[1, 2, 3]
# Change the original list
original_list[0] = 99

# Both the original and the "copy" are changed!
print(original_list)
print(copied_list)
[99, 2, 3]
[99, 2, 3]
# Change the copy
copied_list[-1] = -5

# The original gets changed too!
print(original_list)
print(copied_list)
[99, 2, -5]
[99, 2, -5]

Conditionals

Conditionals let you make decisions in your code with if, elif, and else statements. Often these use boolean comparators which evaluate a statement and return whether it is true or false. Python’s boolean comparators are

Boolean comparator Meaning
> greater than
< less than
>= greater than or equal to
<= less than or equal to
!= not equal to
== equal to
# Create two variables and check whether one is greater than the other
a = 5
b = 10
a > b
False

Boolean comparators and conditional statements allow us to change the behavior of Python scripts depending on the values of certain variables. Conditional statements (like the if statement) have three parts: 1. The keyword 2. The test condition followed by a colon : 3. An indented codeblock that is executed if the test condition is True

For example:

temperature = 65                # a variable representing the temperature

if temperature > 70:            # The "if" keyword, the test condition, and a colon
    print("It's warm outside")  # An indented codeblock that runs if the test condition is True

The other conditional statements, else and elif (short for “else if”) use the same syntax as the if statement, but the must come after an if or elif statement. They can be used to execute different indented codeblocks under different circumstances.

For example:

temperature = 65  

if temperature > 70: 
    print("It's warm outside")
elif temperature < 50:
    print("It's cold outside")
else:
    print("It's a nice day")
It's a nice day

Loops

Loops let you repeat code. The for loop goes through each item in an iterable object, like a list:

fruits = ["apple", "banana", "orange"]

for fruit in fruits:
    print(fruit)
apple
banana
orange

The range() function creates an iterable sequence of numbers that can be used to control a for loop:

for i in range(5):
    print(i)
0
1
2
3
4

The while loop repeats as long as a condition is True.

count = 0

while count < 3:
    print(f"Count is {count}")
    count = count + 1
Count is 0
Count is 1
Count is 2

The example above is not a great use of a while loop because the same thing can be accomplished with a for loop and while loops have the potential to run forever (or until you manually force the computer to stop). That’s called an “infinite loop” and can be a pain if triggered accidentally in your code. In the code above, if you left out the list line in the loop, for example, the value of count would never increase and the conditional test (count < 3) would always be True and the loop would run forever.

It’s best to reserve while loops for situations where you’re uncertain how many iterations are needed.