Introduction to Python#

Running Python#

There are three main ways to use Python.

  1. By running a Python file, e.g. python myscript.py

  2. Through an interactive console (Python interpreter or iPython shell)

  3. In an interactive notebook (e.g. Jupyter, .ipynb files)

In this course, we will mostly be interacting with Python via Jupyter notebooks.

Note

If you have not yet set up Python on your computer, you can execute this tutorial in your browser via Google Colab. Click on the rocket in the top right corner and launch “Colab”. If that does not work download the .ipynb file and import it in Google Colab

Basic Variables: Numbers and String#

# comments are anything that comes after the "#" symbol
a = 1  # assign integer 1 to variable a
b = "hello"  # assign string "hello" to variable b

The following identifiers are used as reserved words and should not be used as variable names:

False      class      finally    is         return
None       continue   for        lambda     try
True       def        from       nonlocal   while
and        del        global     not        with
as         elif       if         or         yield
assert     else       import     pass
break      except     in         raise

Additionally, the following built-in utility functions are always available:

abs() dict() help() min() setattr() all() dir() hex() next() slice() any()
divmod() id() object() sorted() ascii() enumerate() input() oct() staticmethod()
bin() eval() int() open() str() bool() exec() isinstance() ord() sum() bytearray()
filter() issubclass() pow() super() bytes() float() iter() print() tuple()
callable() format() len() property() type() chr() frozenset() list() range()
vars() classmethod() getattr() locals() repr() zip() compile() globals() map()
reversed() __import__() complex() hasattr() max() round() delattr() hash()
memoryview() set()
# how to we see our variables?
print(a)
print(b)
print(a, b)
1
hello
1 hello

All variables are objects. Every object has a type (class). To find out what type your variables are

print(type(a))
print(type(b))
<class 'int'>
<class 'str'>

As a shortcut, iPython notebooks will automatically print whatever is on the last line:

type(b)
str

We can check for the type of an object:

print(type(a) is int)
print(type(a) is str)
True
False

The NoneType is its own type in Python. It only has one possible value, None - it represents an object with no value.

n = None
print(n)
None
type(n)
NoneType

Objects can have attributes and methods, which can be accessed via variable.method

You can use autocomplete by pressing <tab> to show you the methods available.

# this returns the method itself
b.capitalize
<function str.capitalize()>
# this calls the method
b.capitalize()
'Hello'

String Operators#

Basic operations to modify strings.

s = "HOW ARE YOU TODAY?"
split = s.split(" ")
split
['HOW', 'ARE', 'YOU', 'TODAY?']
"-".join(split)
'HOW-ARE-YOU-TODAY?'

Python has ways of creating strings by filling in the blanks and formatting them nicely.

This is helpful for when you want to print statements that include variables or statements.

name = "Reuter West Power Plant"
capacity = 564.123456
technology = "CHP"
message = f"In Berlin, there is a {technology} power plant {name}. It has a nominal capacity of {capacity:.2f} MW."
message
'In Berlin, there is a CHP power plant Reuter West Power Plant. It has a nominal capacity of 564.12 MW.'

Math#

Basic arithmetic and boolean logic is part of the core Python library.

# addition / subtraction
1 + 1 - 5
-3
# multiplication
5 * 10
50
# division
1 / 2
0.5
# that was automatically converted to a float
type(1 / 2)
float
# exponentiation
2**4
16
# rounding
round(9 / 10)
1
# floor division
101 // 2
50
# modulo
101 % 2
1

Comparison Operators#

We can compare objects using comparison operators, and we’ll get back a Boolean (i.e. True/False) result:

Operator

Description

x == y

is x equal to y?

x != y

is x not equal to y?

x > y

is x greater than y?

x >= y

is x greater than or equal to y?

x < y

is x less than y?

x <= y

is x less than or equal to y?

x is y

is x the same object as y?

2 < 3
True
"energy" == "power"
False
2 != "2"
True
2 == 2.0
True

Boolean Operators#

We also have so-called “boolean operators” or “logical operators” which also evaluate to either True or False:

Operator

Description

x and y

are x and y both True?

x or y

is at least one of x and y True?

not x

is x False?

# logic
True and True
True
True and False
False
True or True
True
(not True) or (not False)
True

Conditionals#

Conditionals are the first step to programming and offer an opportunity to get familiar with Python syntax. At their core, conditionals allow a program to make decisions. They dictate the flow of execution based on whether certain conditions are met.

x = 100
if x > 0:
    print("Positive Number")
elif x < 0:
    print("Negative Number")
else:
    print("Zero!")
Positive Number

In Python, indentation is mandatory and blocks of code are closed by the indentation level.

if x > 0:
    print("Positive Number")
    if x >= 100:
        print("Huge number!")
Positive Number
Huge number!

There is also a way to write if statements “inline”, i.e., in a single line, for simplicity.

words = ["the", "list", "of", "words"]

x = "long list" if len(words) > 10 else "short list"
x
'short list'

Loops#

Loops tell a program to perform repetitive tasks. They govern the flow of execution by repeatedly processing a block of code, often until a certain condition is reached or for a predefined number of iterations.

There are two types of loops: the for loop, which iterates over a sequence of values, and the `while`` loop, which continues execution as long as a specified condition remains true.

# make a loop
count = 0
while count < 10:
    # bad way
    # count = count + 1
    # better way
    count += 1
print(count)
10
# use range
for i in range(5):
    print(i)
0
1
2
3
4

Note

In Python, we always count from 0!

# what is range?
?range
# iterate over a list we make up
for carrier in ["electricity", "hydrogen", "methane"]:
    print(carrier, len(carrier))
electricity 11
hydrogen 8
methane 7
# iterate over a list and count indices
for i, carrier in enumerate(["electricity", "hydrogen", "methane"]):
    print(i, carrier, len(carrier))
0 electricity 11
1 hydrogen 8
2 methane 7

What do the brackets mean? A list! Lists are one of the core Python data structures.

Lists#

l = ["electricity", "hydrogen", "methane"]
type(l)
list
# list have lots of methods
l.sort()
l
['electricity', 'hydrogen', 'methane']
# we can convert a range to a list
r = list(range(5))
r
[0, 1, 2, 3, 4]

There are many different ways to interact with lists. For instance:

Function

Description

list.append(x)

Add an item to the end of the list.

list.extend(L)

Extend the list by appending all the items in the given list.

list.insert(i, x)

Insert an item at a given position.

list.remove(x)

Remove the first item from the list whose value is x.

list.pop([i])

Remove the item at the given position in the list, and return it.

list.index(x)

Return the index in the list of the first item whose value is x.

list.count(x)

Return the number of times x appears in the list.

list.sort()

Sort the items of the list in place.

list.reverse()

Reverse the elements of the list in place.

# join two lists
x = list(range(5))
y = list(range(10, 15))
z = x + y
z
[0, 1, 2, 3, 4, 10, 11, 12, 13, 14]
# access items from a list
print("first", z[0])
print("last", z[-1])
print("first 3", z[:3])
print("last 3", z[-3:])
first 0
last 14
first 3 [0, 1, 2]
last 3 [12, 13, 14]
# this index notation also applies to strings
name = "Power Plant Reuter-West"
print(name[:5])
Power
# you can also test for the presence of items in a list
5 in z
False

Python is full of tricks for iterating and working with lists

# a cool Python trick: list comprehension
squares = [n**2 for n in range(5)]
squares
[0, 1, 4, 9, 16]
# iterate over two lists together uzing zip
for item1, item2 in zip(x, y):
    print("first:", item1, "second:", item2)
first: 0 second: 10
first: 1 second: 11
first: 2 second: 12
first: 3 second: 13
first: 4 second: 14

We are almost there. We have the building blocks we need to do basic programming. But Python has some additional useful data structures we need to learn about.

Dictionaries#

This is an extremely useful data structure. It maps keys to values.

Note

Dictionaries are unordered!

d = {
    "name": "Reuter West",
    "capacity": 564,
    "fuel": "hard coal",
}
e = dict(name="Reuter West", capacity=564, fuel="hard coal")
e
{'name': 'Reuter West', 'capacity': 564, 'fuel': 'hard coal'}
# access a value
d["capacity"]
564

Square brackets [...] are used in Python for accessing values in many different contexts.

# test for the presence of a key
print("fuel" in d)
True
# try to access a non-existant key
d["technology"]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[53], line 2
      1 # try to access a non-existant key
----> 2 d["technology"]

KeyError: 'technology'
# a way around missing keys -> defaults
d.get("technology", "OCGT")
# add a new key
d["technology"] = "CHP"
d
# iterate over keys
for k in d:
    print(k, d[k])
# better way
for k, v in d.items():
    print(k, v)

Functions#

For longer and more complex tasks, it is important to organize your code into reuseable elements.

Cutting and pasting the same or similar lines of code is tedious and opens you up to errors.

Best practice is to follow the DRY principle: “don’t repeat yourself”.

In Python, you can use functions for this purpose.

Functions are a central part of advanced Python programming.

Functions take some inputs (“arguments”) and do something in response.

Usually functions return something, but not always.

# define a function without arguments
def say_hello():
    """Return the word hello."""
    return "Hello"
# functions are also objects
type(say_hello)
# this does
say_hello()
# assign the result to something
res = say_hello()
res
# take some arguments
def say_hello_to(name):
    """Return a greeting to `name`"""
    return "Hello " + name
# intended usage
say_hello_to("World")
# take an optional keyword argument
def say_hello(name, german=False):
    """Say hello in multiple languages."""
    if german:
        greeting = "Guten Tag "
    else:
        greeting = "Hello "
    return greeting + name
print(say_hello("Mary"))
print(say_hello("Max", german=True))

Anonymous Functions#

Anonymous functions do not need a name, and are usually defined with a so-called lambda expression.

Anonymous functions can only take up one line of code, so they are not appropriate in most cases, but can be useful for smaller operations.

def square(n):
    return n**2
square(3)
square = lambda n: n**2
square(2)

Pure vs. Impure Functions#

Functions that do not modify their arguments or produce any other side-effects are called pure.

Functions that modify their arguments or cause other actions to occur are called impure.

Below is an example for an impure function.

def remove_last_from_list(input_list):
    input_list.pop()
names = ["Max", "Martha", "Marie"]
remove_last_from_list(names)
names
remove_last_from_list(names)
names

We can do something similar with a pure function.

In general, pure functions are safer and more reliable, since they do not lead to unexpected value changes.

def remove_last_from_list_pure(input_list):
    new_list = input_list.copy()
    new_list.pop()
    return new_list
names = ["Max", "Martha", "Marie"]
new_names = remove_last_from_list_pure(names)
names
new_names

Exercises#

Task 1: What is 5 to the power of 5?

Hide code cell content
5**5

Task 2: Split the following string into a list by splitting on the space character:

Hide code cell content
s = "Data Science for Energy System Modelling"
Hide code cell content
s.split(" ")

Task 3: Create a list with the names of every planet in the solar system (in order)

Hide code cell content
planets = [
    "Mercury",
    "Venus",
    "Earth",
    "Mars",
    "Jupyter",
    "Saturn",
    "Uranus",
    "Neptune",
]

Task 4: Have Python tell you how many planets there are by examining your list

Hide code cell content
len(planets)

Task 5: Use slicing to display the first four planets (the rocky planets)

Hide code cell content
planets[:4]

Task 6: Iterate through your planets and print the planet name only if it has an “s” at the end

Hide code cell content
for p in planets:
    if p.endswith("s"):
        print(p)

Task 7: Create a dictionary that contains the main facts about the Reuter West power plant.

Hide code cell content
rw = {
    "Country": "Germany",
    "Electricity Capacity": 564,
    "Heat Capacity": 878,
    "Technology": "Combined heat and power (CHP)",
    "Main Fuel": "Hard coal",
    "Vattenfall ownership share": "100%",
    "Status": "In Operation",
}

Task 8: Use this dictionary to access the main fuel type.

Hide code cell content
rw["Main Fuel"]

Task 9: Add the power plant’s approximate latitude and longitude to the dictionary.

Hide code cell content
rw["x"] = 13.24
rw["y"] = 52.53

Task 10: Write a function that converts units of energy from ‘ktoe’ to ‘GWh’

Hide code cell content
def ktoe_to_gwh(x):
    return 11.63 * x

Task 11: Write a more general unit conversion function that converts between all units of energy listed under the link below. The function should take arguments: for the original value, the original unit and the target unit. Implement the function in a way that the default target unit is “Wh”.

You can also just pick three units to convert between if you don’t feel like going through all combinations.

Hide code cell content
def to_joule(value, from_unit):
    if from_unit.endswith("cal"):
        return value / 0.2390
    elif from_unit.endswith("Btu"):
        return value / 0.0009478
    elif from_unit.endswith("Wh"):
        return value / 0.0002778
    elif from_unit.endswith("toe"):
        return value * 2.388e11
    elif from_unit.endswith("tce"):
        return value * 3.412e11
    else:
        raise NotImplementedError()
Hide code cell content
def convert_unit(value, from_unit, to_unit="Wh"):
    x = to_joule(value, from_unit)
    if to_unit.endswith("cal"):
        x *= 0.2390
    elif to_unit.endswith("Btu"):
        x *= 0.0009478
    elif to_unit.endswith("Wh"):
        x *= 0.0002778
    elif to_unit.endswith("toe"):
        x /= 2.388e11
    elif to_unit.endswith("tce"):
        x /= 3.412e11
    else:
        raise NotImplementedError()

    return x
Hide code cell content
convert_unit(200, "toe")

Task 12: Verify the function above by looping through all combinations of unit conversions and assert that applying the function back and forth results in the same value.

Hide code cell content
from itertools import product
Hide code cell content
units = ["cal", "Btu", "Wh", "toe", "tce"]
Hide code cell content
for i, j in product(units, units):
    x = convert_unit(convert_unit(100, i, j), j, i)
    print(x)

Task 13: Write code that uses a while loop to print odd numbers from 1 to 10.

Hide code cell content
number = 1
while number <= 10:
    if number % 2 != 0:  # Check if the number is odd
        print(number)
    number += 1