19px
CCPP-I · Class 01

Python fundamentals

J/K or →/← jump between sections

Variables are names for values

Step through: a name appears when it is first assigned, and changes when assigned again.

Built-in names can be overwritten by accident

print = 1
print("123")
TypeError: 'int' object is not callable
del print   # back to the built-in
print("123")
123

Numbers: /, //, %

/ always gives a float. // is how many whole rows fit, % is what is left over.

Every value has a type

int

2, 13 // 2, a ** 2
whole numbers, exact

float

13 / 2, 0.1, 1e-3
decimals, not exact: 0.1 + 0.05 → 0.15000000000000002

bool · str

True, 5 > 4, 1 == 1.0
"text", 'H' == 'H'
comparisons give booleans

type(x) tells which one.

Strings

Text in quotes. Characters are numbered from 0 from the left, and from −1 from the right.

formula = 'C2H5OH'

Operators and methods

'Hello ' + 'World'
'Hello' + str(1), 'A' * 5
'H' in formula, formula.count('H'), formula.find('OH')
'Hello World' ('Hello1', 'AAAAA') (True, 2, 4)
'Hello' + 1
TypeError: can only concatenate str (not "int") to str

Text and numbers do not mix: convert first with str(1) or int('1').

split turns one string into a list of strings

line = 'H, C, N, O, Fe'
line.split(', ')
line.upper(), line.lower(), len(line)
('H, C, N, O, FE', 'h, c, n, o, fe', 14)

f-strings: values inside text

After the colon comes the format: alignment, width, decimals. Build one and watch the output cells.

n = 4
mass = 18.015
print(f'Water has {n} atoms and mass {mass:.1f}')
Water has 4 atoms and mass 18.0

Files

with opens the file and closes it for you. readlines() gives one string per line, newline included.

with open('test.txt', 'w') as f:
    f.write('123\n')
    f.write('456')
with open('test.txt') as f:
    lines = f.readlines()
lines
['123\n', '456']
test.txt1123⏎ 2456

'w' = write (creates or overwrites). No second argument = read.

with open('pyridine.xyz') as f:
    lines = f.readlines()
print(lines[0])
print(lines[2])
lines[2].split()
11 C -0.180226841 0.360945118 -1.120304970 ['C', '-0.180226841', '0.360945118', '-1.120304970']
pyridine.xyz011⏎ 1⏎ 2C -0.180226841 0.360945118 -1.120304970⏎ 3C -0.180226841 1.559292118 -0.407860970⏎ …

print adds its own newline, so a line that ends with \n prints with a blank line after it. split() without argument splits on any whitespace. Everything is still text: float('-0.18…') makes a number.

Lists and tuples

A list is an ordered, changeable collection. Click the statements in order.

elements = ['H', 'C', 'N', 'O', 'Fe']

Indexing and slicing work exactly as for strings

A list can mix types; a tuple is a list you cannot change

mixed = ['Hola', 123, True, 4.5]
for item in mixed:
    print(item, type(item))
Hola <class 'str'> 123 <class 'int'> True <class 'bool'> 4.5 <class 'float'>
t = ('H', 1, 1.008)
t[1] = 2
TypeError: 'tuple' object does not support item assignment

Round brackets, fixed content. Reading t[1] is fine.

Two names, one list

b = a does not copy. Both names point to the same object; changing it through one name is visible through the other.

for, range, enumerate

The loop variable takes each value of the collection in turn. The indented block runs once per value.

range makes the numbers to loop over

stop is never included. range(5) means range(0, 5, 1).

Index and value together

⏸

Exercises 1 and 2

1. Lists

With elements = ['H', 'C', 'N', 'O', 'Fe']:

  1. Print the first and the last element.
  2. Append 'S', remove 'N', print the length of the list.
  3. Print the list sorted alphabetically; the original list must stay unchanged.

2. Formatted table

Print the elements as a table with two right-aligned columns of width 5 and 8, using enumerate and f-strings:

Index  Element
    0        H
    1        C

Dictionaries: key → value

Look up by name instead of by position. Click the statements in order.

carbon = {'symbol': 'C', 'mass': 12.011, 'number': 6}

The dictionary used in the exercises and later today:

masses = {'H': 1.008, 'C': 12.011, 'N': 14.007, 'O': 15.999, 'Fe': 55.845}

Building a dictionary in a loop

⏸

Exercise 3: Dictionaries

Calculate the mass of water, ['H', 'H', 'O'], with a for loop and masses.

Conditions: if / elif / else

Conditions are tested top to bottom; the first true one wins, the rest are skipped.

Comparisons and and, or, not

== compares values, is compares identity

Empty things count as False

text = ''
if text:
    print('not empty')
else:
    print('empty')

Also false: 0, [], {}, None. Note '0' is a non-empty string.

⏸

Exercise 4: Conditions

Loop over masses and print each element as light (mass below 10), heavy (above 50) or medium:

H light
C medium
N medium
O medium
Fe heavy

while: repeat as long as a condition holds

The condition is re-checked before every pass. Something inside must move it towards False.

⏸

Exercise 5: Loops

Calculate the sum of all even numbers from 2 to n (included), first with a for loop over range, then with a while loop.

n = 10
# for loop ...
print(total)
# while loop ...
print(total)
30
30

Comprehensions: a loop that builds a list in one line

Hover a part to see where it goes.

for i in range(10):
    doubles.append(i * 2)
[i * 2 for i in range(10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
for x in range(1, 11):
    if x % 2 == 0:
        squares.append(x**2)
[x**2 for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

The same for dictionaries

name_lengths = {}
for element in elements:
    name_lengths[element] = len(element)
{element: len(element) for element in elements}
{'H': 1, 'N': 1, 'O': 1, 'Fe': 2, 'S': 1}
positions = {}
for i, element in enumerate(elements):
    positions[element] = i
{element: i for i, element in enumerate(elements)}
{'H': 0, 'N': 1, 'O': 2, 'Fe': 3, 'S': 4}
⏸

Exercise 6: Comprehensions

In one line each: the list of squares of the even numbers from 1 to 10, the list of symbols in masses heavier than carbon, and the dictionary of the heavy elements only (mass > 12) with a dictionary comprehension.

Reading an error message

Read the last line first: the kind of error and what went wrong. Then find the arrow: where.

1 / 0
-------------------------------------------- ZeroDivisionError Traceback (most recent call last) Cell In[1], line 1 ----> 1 1 / 0 ZeroDivisionError: division by zero
print(undefined_variable)
-------------------------------------------- NameError Traceback (most recent call last) Cell In[2], line 1 ----> 1 print(undefined_variable) NameError: name 'undefined_variable' is not defined

Usually a typo, or a cell that was not run yet.

Functions

A function gets its own box of variables while it runs. return hands a value back; print only shows it.

print is not return

Docstrings: a comment the function carries with it

def product(a, b):
    """Return the product of a and b."""
    return a * b

product?
Signature: product(a, b) Docstring: Return the product of a and b. Type: function

A string as the first line of the body. ? in Jupyter (or help(product) anywhere) shows it; this is what you see for every library function too.

A loop inside a function

Several values come back as a tuple

def get_even_numbers(n):
    numbers = list(range(0, n, 2))
    return len(numbers), numbers

count, values = get_even_numbers(10)
count, values
(5, [0, 2, 4, 6, 8])
count
5
values
[0, 2, 4, 6, 8]

Two names on the left, two values on the right: unpacking.

Scope: what lives where

Names created inside a function disappear when it returns. Constants go at the top, in CAPITALS.

energy_kcal
NameError: name 'energy_kcal' is not defined
def set_value():
    global value
    value = 5

set_value()
value
5

global writes into the outer box. Legal, rarely a good idea: return the value instead.

Default and keyword arguments

Positional arguments fill parameters left to right. Keywords go by name. Missing ones take their default.

*args collects extra positional, **kwargs extra keyword arguments

Recursion

A function may call itself. Each call gets its own box; they pile up, then unwind.

⏸

Exercise 7: Functions

1. Write molecular_mass(symbols) that returns the mass of a molecule given as a list of symbols, using masses.

print(molecular_mass(['H', 'H', 'O']))
print(molecular_mass(['C', 'O', 'O']))
18.015
44.009

2. Write describe(symbol, charge=0) that prints the symbol and its charge.

describe('O')
describe('Fe', charge=3)
O, charge 0
Fe, charge 3

Scripts and sys.argv

A .py file is run from the terminal. The words after python arrive as a list of strings. Edit the command line.

# argv.py
import sys

print(sys.argv)
$
sys.argv =

The homework template

# homework/hw1/count_atoms.py
import sys

if len(sys.argv) > 1:
    filename = sys.argv[1]
else:
    print("Filename not provided!")
    exit()

# filename is a string: open it, count the atoms

Everything in sys.argv is text. sys.argv[0] is the script itself.

⏸

Homework 1

by Slack, end of Tue 13 Oct

1. Fibonacci: the n-th term, with a loop and with recursion.

2. Read the book sections "Additional notes on I/O" and "Using libraries" (sys, os, argparse).

3. count_atoms.py: count the atoms of each element in an XYZ file.

$ python count_atoms.py heme.xyz
C  34
O  4
N  4
Fe 1
H  30