Python#
Individual learning outcomes#
Use Python operators, data types (lists, dicts etc.), write functions into scripts which you can execute
YouTube Video
Note
This material is mostly adapted from the following resources:
Further excellent introductions to Python can be found at
Running Python#
There are three main ways to use Python.
By running a Python file, e.g.
python myscript.pyThrough an interactive console (Python interpreter or iPython shell)
In an interactive notebook (e.g. Jupyter,
.ipynbfiles)
In this course, we will mostly be interacting with Python via Jupyter notebooks. These are files that contain both code cells (which can be executed) and text cells (which can contain formatted text, images, links, etc.).
Note
If you have not yet set up Python on your computer, you can execute this tutorial in your browser via Google Colab. Download the .ipynb file using the download button on the top right corner and import it in Google Colab.
Basic Variables#
A variable is a name that refers to a value stored in the computer’s memory. In Python, you can create a variable by assigning a value to it using the = operator. For example:
a = 1 # assign integer 1 to variable a
b = "hello" # assign string "hello" to variable b
Anything that comes after the # symbol is a comment and is ignored by Python.
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()
Variables can be inspected by simply typing their name in a code cell and executing it, or by using the print() function:
a
1
print(b)
hello
All variables are objects. Every object has a type (class) that defines the possible values it can take and the operations that can be performed on it.
To find out what type your variables are, you can use the type() function:
type(a)
int
type(b)
str
We can check for the type of an object:
type(a) is int
True
type(a) is str
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_name.method_name and called via variable_name.method_name().
Note
You can use autocomplete by pressing <tab> to show you the methods available.
It is important to note that methods only do something when they are called (i.e. written with parentheses).
This calls the method:
b.capitalize()
'Hello'
This does not (it just returns the method itself):
b.capitalize
<function str.capitalize()>
String Operators#
Basic operations to modify strings help in manipulating text data, for example:
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 = "Kariba North Bank Power Plant"
capacity = 1730.74
technology = "Hydro"
message = f"On the Kariba, there is a {technology} power plant called {name}. It has a nominal capacity of {capacity:.2f} GWh."
message
'On the Kariba, there is a Hydro power plant called Kariba North Bank Power Plant. It has a nominal capacity of 1730.74 GWh.'
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
# exponentiation
2**4
16
# rounding
round(9 / 10)
# round(9 / 10, 1) # rounds to 1 decimal place
1
Comparison Operators#
We can compare objects using comparison operators, and we’ll get back a Boolean (i.e. True/False) result:
Operator |
Description |
|---|---|
|
is |
|
is |
|
is |
|
is |
|
is |
|
is |
|
is |
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 |
|---|---|
|
are |
|
is at least one of |
|
is |
# logic
(1 > 2) and (2 < 3)
False
(1 < 2) and (2 < 3)
True
(1 < 2) or (2 < 3)
True
(not 1 > 2) or (not 2 > 3)
True
Conditionals#
Conditionals are the first step toward more complex programming and offer an opportunity to get familiar with Python syntax. At their core, conditionals allow a program to make decisions about what code to execute depending on certain conditions.
x = 1000
if x > 0:
print("Positive Number")
elif x < 0:
print("Negative Number")
else:
print("Zero!")
Positive Number
Indentation#
In Python, indentation is mandatory and blocks of code are closed by the indentation level.
Best Practices#
Use Spaces, not Tabs: Spaces are the preferred method for indentation. While tabs can technically work, they may display differently depending on the editor, leading to visual inconsistencies
Never Mix Tabs and Spaces: In Python 3, mixing tabs and spaces in the same code block is disallowed and will result in a TabError.
Configure Your Editor: Most modern IDEs and editors (like PyCharm or Visual Studio Code) can be set to automatically insert 4 spaces when you press the Tab key.
Use Auto-Formatters: To maintain these standards without manual effort, you can use tools like Black or Ruff, which automatically format your code
if x > 0:
print("Positive Number")
if x >= 100:
print("Huge number!")
Positive Number
Huge number!
There is also a way to write if-else statements inline, i.e., in a single line, for simplicity.
words = ["the", "list", "of", "words"]
"long list" if len(words) > 10 else "short list"
'short list'
Loops#
Loops are another fundamental programming construct that allows 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.
count = 0
while count < 10:
count += 1
print(count)
10
for i in range(5):
print(i)
0
1
2
3
4
Note
In Python, we always count from 0!
We can also use a for loop to iterate over a sequence of values, for instance, the elements of a list.
for carrier in ["electricity", "hydrogen", "methane"]:
print(carrier, len(carrier))
electricity 11
hydrogen 8
methane 7
We can use enumerate() to get both the index and the value while iterating over a list.
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 square brackets mean? A list! Lists are one of the core Python data structures.
Lists#
l = ["hydrogen", "electricity", "methane"]
type(l)
list
Lists have a lot of useful methods for manipulating them, for instance, sorting them:
l.sort()
l
['electricity', 'hydrogen', 'methane']
There are many different ways to interact with lists. For instance:
Function |
Description |
|---|---|
|
Add an item to the end of the list. |
|
Extend the list by appending all the items in the given list. |
|
Insert an item at a given position. |
|
Remove the first item from the list whose value is x. |
|
Remove the item at the given position in the list, and return it. |
|
Return the index in the list of the first item whose value is x. |
|
Return the number of times x appears in the list. |
|
Sort the items of the list in place. |
|
Reverse the elements of the list in place. |
Here are some more examples of list methods.
Joining 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]
Accessing items from a list at specific positions:
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]
Checking if an item is in a list:
4 in z
True
Python is full of tricks for iterating and working with lists, for instance, list comprehensions:
squares = [n**2 for n in range(5)]
squares
[0, 1, 4, 9, 16]
Dictionaries#
This is another basic, but extremely useful data structure. It maps keys to values, like a real dictionary maps words to their definitions. There are two main ways to create a dictionary: using curly braces {} or the dict() function.
d = {
"name": "Kafue Gorge Power Plant",
"capacity": 47830.50,
"fuel": "Hydro",
}
e = dict(name="Kafue Gorge Power Plant", capacity=47830.50, fuel="Hydro")
e
{'name': 'Kafue Gorge Power Plant', 'capacity': 47830.5, 'fuel': 'Hydro'}
Values can be accessed via their keys. Square brackets are used in Python for accessing values in many different contexts.
d["capacity"]
47830.5
You can also test for the presence of items in a list:
"fuel" in d
True
Trying to access a key that does not exist will raise a KeyError. We can catch this error using a so-called try-except block. We try to access the key, and if it fails, we except the error and do something else instead.
try:
print(d["technology"])
except:
print("KeyError encountered.")
KeyError encountered.
As a way to avoid this, you can use the get() method of dictionaries, which allows you to provide a default value if the key does not exist:
d.get("technology", "OCGT")
'OCGT'
New entries can be added to a dictionary by assigning a value to a new key:
d["technology"] = "Stored Hydro"
d
{'name': 'Kafue Gorge Power Plant',
'capacity': 47830.5,
'fuel': 'Hydro',
'technology': 'Stored Hydro'}
Finally, you can also iterate over the keys and values of a dictionary using a for loop:
for k, v in d.items():
print(k, v)
name Kafue Gorge Power Plant
capacity 47830.5
fuel Hydro
technology Stored Hydro
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 they don’t have to.
A function without any arguments could look like this:
def say_hello():
"""Return the word hello."""
return "Hello"
We can call it like other functions of different data types using round brackets. This returns the string “Hello”:
say_hello()
'Hello'
Whatever is returned by a function can be assigned to a variable or used directly:
result = say_hello()
result
'Hello'
A function with arguments could be used to greet a specific person. This could look like this:
def say_hello_to(name, greeting):
"""Return a greeting to `name`"""
return greeting + " " + name
So, if we want to greet “Mr Mutamba”, we can call the function like this:
say_hello_to("Mr Mutamba", "Hi")
'Hi Mr Mutamba'
Functions can distinguish between different types of arguments, for instance, positional and keyword arguments. Positional arguments are assigned based on their order and compulsory, whereas keyword arguments are assigned based on their name (like a dictionary) and are optional (they can have default values). Here’s an example function that uses both types of arguments to greet a person in different languages:
def say_hello(name: str, bemba: bool = False):
"""Say hello in multiple languages."""
if bemba:
greeting = "Muli shani "
else:
greeting = "Hello "
return greeting + name
say_hello("Ba Mr Mutamba", bemba=True)
'Muli shani Ba Mr Mutamba'
Pure and 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.
The distinction is important because pure functions are more robust and predictable as they do not depend on previous / repeated code executions.
Below is an example for an impure function.
def remove_last_from_list(input_list):
input_list.pop()
names = ["Jethro", "Crywell", "Manyika"]
remove_last_from_list(names)
names
['Jethro', 'Crywell']
remove_last_from_list(names)
names
['Jethro']
We can do something similar with a pure function, which works with copies of the arguments instead of modifying them directly.
def remove_last_from_list_pure(input_list):
new_list = input_list.copy()
new_list.pop()
return new_list
names = ["Jethro", "Crywell", "Manyika"]
new_names = remove_last_from_list_pure(names)
names
['Jethro', 'Crywell', 'Manyika']
new_names
['Jethro', 'Crywell']
In general, pure functions are safer and more reliable, since they do not lead to unexpected value changes.
File Paths#
In your daily work you will work with file paths a lot. It is important to understand how to work with file locations for the purpose of reading or writing outputs of your work.
A file path tells Python where a file or folder is located, this is different depending on the operating system you are using. For example:
Linux / Mac “/home/mypc/data/report.csv”
Windows “C:/Users/mypc/Documents/report.csv”
Relative path “data/report.csv”
The best place to start in understanding file paths is to know where you currently are, that is your current working directory. This is because Python runs relative paths from the current working directory.
# Print current working directory
import os
current_dir = os.getcwd()
print("Current Working Directory:")
print(current_dir)
Current Working Directory:
/home/runner/work/pypsa-zambia-workshops/pypsa-zambia-workshops/pypsa-zambia-workshops
Absolute vs Relative Paths#
It is important to be able to differentiate the two;
Absolute path starts from the root of the computer
Relative path starts from the current working directory
# ABSOLUTE PATH
absolute_path = "/home/mypc/project/data/file.csv"
# RELATIVE PATH
relative_path = "data/file.csv"
print("Absolute Path:")
print(absolute_path)
print("\nRelative Path:")
print(relative_path)
Absolute Path:
/home/mypc/project/data/file.csv
Relative Path:
data/file.csv
Exercises#
Here are some exercises to practice and reinforce what you have learned in this notebook. You can find the solutions in the solutions section of the course website.
Task 1: What is 5 to the power of 5 minus 5?
Task 2: Create variables for the efficiency of 45% and the input_energy of 1000 MWh. Compute and print the output_energy using these variables.
Task 3: Split the following string into a list by splitting on the space character: “Python is very cool man!”
Task 4: Create a list with the names of every planet in the solar system (in order).
Have Python tell you the name of the third planet.
Have Python tell you the total number of planets.
Have Python tell you whether “Pluto” is in the list of planets.
Iterate through your planets and print the planet name only if it has an “s” at the end.
Task 5
Create a folder called ‘practice’
Inside it, create a file called ‘hello.txt’
Write your name into the file
Read the file and print the contents