Modularization#

Midjourney: Modular Blocks, ref. Piet Mondrian

Write programs that do one thing and do it well.

โ€” Doug McIlroy

Slides/PDF#

Split code into files, modules, and packages#

Large programs often contain dozens of classes with hundreds of functions and many thousands of lines of code. It quickly becomes difficult to keep an overview when all classes are defined in a single file. Especially when different programmers work at different places in the program, version conflicts can arise very quickly when people work on similar files.

To keep this organized and easy to navigate, code is split into multiple files with the extension .py. It is common to have one file

  • per class, when classes are defined

  • per topic, when helper functions are defined (e.g., mathematical functions, โ€ฆ)

  • per area of responsibility, when (for example) data loading is separated from its processing. This way you can later define other processing steps and reuse the loading.

If we save each class of the geometry elements from the Class Definition section of the last lecture, we would then have, for example, a project structure corresponding to:

Each file contains only the code of the class with the same name, even if that is only a few lines, as in the case of the classes Triangle, Tetragon, and Pentagon. Crucially, when a programmer searches for the code of a class, they should be able to see exactly in which file it can be found and not have to search far.

Larger projects are split into multiple modules by creating additional subdirectories. For example, we want to group all generic classes for points in the points directory and all geometric shapes in the shapes directory. This makes it easy to structure larger projects.

The sum of all modules then forms a package. In this case the geometry package, which we can reuse in different implementations.

main() - The entry point of a program#

When code is spread across multiple files, Python needs a hint about which code should be executed. For this, you define the special entry function main().

It exists in almost all programming languages and always designates the starting point of a program.

In Python, it either takes no arguments or receives them dynamically when it is invoked by the user or by another program (a.k.a. command-line arguments).

def main():
	print("This is the main function")

However, you want to avoid the function main() from being called when the Python file is, for example, imported as a library, where youโ€™re only interested in the functions. Therefore, at the end of a file that defines a main() function you use the following conditional.

if __name__ == "__main__":
	main()
This is the main function

She takes advantage of the fact that the value of the special variable __name__ in the main file is always '__main__', whereas in an imported file it indicates the name of the main file.

Importing Modules#

To avoid constantly loading unnecessary code, Python does not load this code automatically.

So, if we want to use the code in our files, modules, and packages, we must first tell Python to load it.

This importing is done with the import command.

A simple import is importing an entire package.

This is done by writing import followed by the package name.

import geometry.points.ImmutablePoint
import geometry.shapes.Line

def main():
	point_1 = geometry.points.ImmutablePoint.ImmutablePoint(x=54.083336, y=12.108811)
	point_2 = geometry.points.ImmutablePoint.ImmutablePoint(y=12.094167, x=54.075211)
	line_1 = geometry.shapes.Line.Line(start=point_1, end=point_2)
	print(f"Die Lรคnge der Linie zwischen Punkt 1 und 2 ist: {line_1.length()}")

if __name__ == "__main__":
	main()
Die Lรคnge der Linie zwischen Punkt 1 und 2 ist: 0.016747010509340444

One drawback of importing entire packages is that if we want to reference individual classes that reside in submodules, we must specify the fully qualified class name. For example, in the example above: geometry.points.ImmutablePoint.ImmutablePoint.

Therefore, one typically imports individual modules by specifying the path to a module, for example geometry.points.ImmutablePoint.ImmutablePoint. Here, you can also assign new names to the imported modules, such as point or line, in the example below.

import geometry.points.ImmutablePoint as point
import geometry.shapes.Line as line

def main():
	point_1 = point.ImmutablePoint(x=54.083336, y=12.108811)
	point_2 = point.ImmutablePoint(y=12.094167, x=54.075211)
	line_1 = line.Line(start=point_1, end=point_2)
	print(f"Die Lรคnge der Linie zwischen Punkt 1 und 2 ist: {line_1.length()}")

if __name__ == "__main__":
	main()
Die Lรคnge der Linie zwischen Punkt 1 und 2 ist: 0.016747010509340444

Alternatively, parts of a module can also be imported using the wildcard * and the from statement. All elements are imported with the wildcard *. Specific elements, such as individual classes, can also be specified directly, as in the following example Line.

from geometry.points.ImmutablePoint import *
from geometry.shapes.Line import Line

def main():
	point_1 = ImmutablePoint(x=54.083336, y=12.108811)
	point_2 = ImmutablePoint(y=12.094167, x=54.075211)
	line_1 = Line(start=point_1, end=point_2)
	print(f"Die Lรคnge der Linie zwischen Punkt 1 und 2 ist: {line_1.length()}")

if __name__ == "__main__":
	main()
Die Lรคnge der Linie zwischen Punkt 1 und 2 ist: 0.016747010509340444

Import standard packages from Python#

Python includes many standard libraries for common tasks. For construction and environmental informatics, the following are the most useful:

packet

description

collections

More complex data types for counting, sorting

http

Functions of the HTTP protocol, such as web servers

json

Functions to store objects as text

logging

Functions to write logs

math

Mathematical functions

os

Functions to locate, load, and save files

pickle

Functions to store objects in binary form

pprint

print functions for pretty-printing objects

random

Functions for generating random numbers

re

Functions for regular expressions to search text

sys

Functions to obtain system information

time

Functions for time and date information

timeit

Functions to measure the performance of functions

traceback

Functions to display the stack trace

urllib

Functions to load and process URLs on the Internet

From the list, we have already become familiar with and used the libraries math, time, timeit, traceback, and logging. The other packages, however, offer additional useful functions.

For example, if we want to list all files in a directory, we use the os package.

import os

folder = "geometry/shapes/"
for count, filename in enumerate(os.listdir(folder)):
	if os.path.isfile(os.path.join(folder, filename)):
		path = os.path.join(folder, filename)
		print(path)
geometry/shapes/Tetragon.py
geometry/shapes/Pentagon.py
geometry/shapes/Line.py
geometry/shapes/Polygon.py
geometry/shapes/Triangle.py

Many websites offer application programming interfaces, so-called APIs, because these APIs are also used by their own websites to load data that is displayed on the page. The APIs usually use the JSON format to exchange data. This is a text-based file format that in Python closely resembles the dict data type, but it also supports all other primitive and composite data types in Python.

We would like, for example, to analyze the weather data of a weather station in Germany. We obtain these data from the German Weather Service. These data can also be downloaded from the API. For this, you need the ID (identification number) of a weather station, which can be found here. We take as an example a station in the Hansaviertel in Rostock with the ID 12495.

Then you can load the weather data with Python with the help of the urllib package from the API. The JSON format can be processed with the json package. To output this in a nicer way, we use the pprint function from the pprint package (pretty-print).

import urllib.request
import json
import pprint

stationID='12495' # Rostock-Hansaviertel
with urllib.request.urlopen(f'https://dwd.api.proxy.bund.dev/v30/stationOverviewExtended?stationIds={stationID}') as f:
    data=f.read() # This returns binary data
    weather=json.loads(data) # We convert the binary data into a dict
    pprint.pprint(weather, indent=2, compact=True)
{ '12495': { 'days': [ { 'dayDate': '2025-08-17',
                         'icon': 2,
                         'icon1': None,
                         'icon2': None,
                         'moonPhase': 6,
                         'moonrise': 1755464484000,
                         'moonriseOnThisDay': 1755464484000,
                         'moonset': 1755439776000,
                         'moonsetOnThisDay': 1755439776000,
                         'precipitation': 0,
                         'stationId': None,
                         'sunrise': 1755400811000,
                         'sunriseOnThisDay': 1755400811000,
                         'sunset': 1755452877000,
                         'sunsetOnThisDay': 1755452877000,
                         'sunshine': 32767,
                         'temperatureMax': 218,
                         'temperatureMin': 139,
                         'windDirection': 3210,
                         'windGust': 370,
                         'windSpeed': 185},
                       { 'dayDate': '2025-08-18',
                         'icon': 2,
                         'icon1': None,
                         'icon2': None,
                         'moonPhase': 6,
                         'moonrise': 1755554333000,
                         'moonriseOnThisDay': 1755554333000,
                         'moonset': 1755530459000,
                         'moonsetOnThisDay': 1755530459000,
                         'precipitation': 0,
                         'stationId': None,
                         'sunrise': 1755487306000,
                         'sunriseOnThisDay': 1755487306000,
                         'sunset': 1755539156000,
                         'sunsetOnThisDay': 1755539156000,
                         'sunshine': 32767,
                         'temperatureMax': 204,
                         'temperatureMin': 117,
                         'windDirection': 3380,
                         'windGust': 315,
                         'windSpeed': 130},
                       { 'dayDate': '2025-08-19',
                         'icon': 2,
                         'icon1': None,
                         'icon2': None,
                         'moonPhase': 6,
                         'moonrise': 1755645158000,
                         'moonriseOnThisDay': 1755645158000,
                         'moonset': 1755620045000,
                         'moonsetOnThisDay': 1755620045000,
                         'precipitation': 0,
                         'stationId': None,
                         'sunrise': 1755573801000,
                         'sunriseOnThisDay': 1755573801000,
                         'sunset': 1755625435000,
                         'sunsetOnThisDay': 1755625435000,
                         'sunshine': 32767,
                         'temperatureMax': 229,
                         'temperatureMin': 119,
                         'windDirection': 3020,
                         'windGust': 204,
                         'windSpeed': 93},
                       { 'dayDate': '2025-08-20',
                         'icon': 2,
                         'icon1': None,
                         'icon2': None,
                         'moonPhase': 7,
                         'moonrise': 1755736539000,
                         'moonriseOnThisDay': None,
                         'moonset': 1755708637000,
                         'moonsetOnThisDay': 1755708637000,
                         'precipitation': 0,
                         'stationId': None,
                         'sunrise': 1755660295000,
                         'sunriseOnThisDay': 1755660295000,
                         'sunset': 1755711712000,
                         'sunsetOnThisDay': 1755711712000,
                         'sunshine': 32767,
                         'temperatureMax': 249,
                         'temperatureMin': 130,
                         'windDirection': 2660,
                         'windGust': 278,
                         'windSpeed': 111},
                       { 'dayDate': '2025-08-21',
                         'icon': 3,
                         'icon1': None,
                         'icon2': None,
                         'moonPhase': 7,
                         'moonrise': 1755736539000,
                         'moonriseOnThisDay': 1755736539000,
                         'moonset': 1755796531000,
                         'moonsetOnThisDay': 1755796531000,
                         'precipitation': 0,
                         'stationId': None,
                         'sunrise': 1755746790000,
                         'sunriseOnThisDay': 1755746790000,
                         'sunset': 1755797989000,
                         'sunsetOnThisDay': 1755797989000,
                         'sunshine': 32767,
                         'temperatureMax': 211,
                         'temperatureMin': 143,
                         'windDirection': 3150,
                         'windGust': 296,
                         'windSpeed': 93},
                       { 'dayDate': '2025-08-22',
                         'icon': 3,
                         'icon1': None,
                         'icon2': None,
                         'moonPhase': 7,
                         'moonrise': 1755827997000,
                         'moonriseOnThisDay': 1755827997000,
                         'moonset': 1755884001000,
                         'moonsetOnThisDay': 1755884001000,
                         'precipitation': 1,
                         'stationId': None,
                         'sunrise': 1755833285000,
                         'sunriseOnThisDay': 1755833285000,
                         'sunset': 1755884264000,
                         'sunsetOnThisDay': 1755884264000,
                         'sunshine': 32767,
                         'temperatureMax': 198,
                         'temperatureMin': 125,
                         'windDirection': 3260,
                         'windGust': 315,
                         'windSpeed': 130},
                       { 'dayDate': '2025-08-23',
                         'icon': 3,
                         'icon1': None,
                         'icon2': None,
                         'moonPhase': 0,
                         'moonrise': 1755919307000,
                         'moonriseOnThisDay': 1755919307000,
                         'moonset': 1755971217000,
                         'moonsetOnThisDay': 1755971217000,
                         'precipitation': 0,
                         'stationId': None,
                         'sunrise': 1755919779000,
                         'sunriseOnThisDay': 1755919779000,
                         'sunset': 1755970539000,
                         'sunsetOnThisDay': 1755970539000,
                         'sunshine': 32767,
                         'temperatureMax': 188,
                         'temperatureMin': 112,
                         'windDirection': 3030,
                         'windGust': 315,
                         'windSpeed': 148},
                       { 'dayDate': '2025-08-24',
                         'icon': 2,
                         'icon1': None,
                         'icon2': None,
                         'moonPhase': 0,
                         'moonrise': 1756010376000,
                         'moonriseOnThisDay': 1756010376000,
                         'moonset': 1756058291000,
                         'moonsetOnThisDay': 1756058291000,
                         'precipitation': 0,
                         'stationId': None,
                         'sunrise': 1756006274000,
                         'sunriseOnThisDay': 1756006274000,
                         'sunset': 1756056812000,
                         'sunsetOnThisDay': 1756056812000,
                         'sunshine': 32767,
                         'temperatureMax': 183,
                         'temperatureMin': 102,
                         'windDirection': 3010,
                         'windGust': 296,
                         'windSpeed': 111},
                       { 'dayDate': '2025-08-25',
                         'icon': 2,
                         'icon1': None,
                         'icon2': None,
                         'moonPhase': 0,
                         'moonrise': 1756101241000,
                         'moonriseOnThisDay': 1756101241000,
                         'moonset': 1756145313000,
                         'moonsetOnThisDay': 1756145313000,
                         'precipitation': 0,
                         'stationId': None,
                         'sunrise': 1756092768000,
                         'sunriseOnThisDay': 1756092768000,
                         'sunset': 1756143084000,
                         'sunsetOnThisDay': 1756143084000,
                         'sunshine': 32767,
                         'temperatureMax': 192,
                         'temperatureMin': 104,
                         'windDirection': 2810,
                         'windGust': 315,
                         'windSpeed': 111},
                       { 'dayDate': '2025-08-26',
                         'icon': 2,
                         'icon1': None,
                         'icon2': None,
                         'moonPhase': 0,
                         'moonrise': 1756191992000,
                         'moonriseOnThisDay': 1756191992000,
                         'moonset': 1756232335000,
                         'moonsetOnThisDay': 1756232335000,
                         'precipitation': 0,
                         'stationId': None,
                         'sunrise': 1756179263000,
                         'sunriseOnThisDay': 1756179263000,
                         'sunset': 1756229356000,
                         'sunsetOnThisDay': 1756229356000,
                         'sunshine': 32767,
                         'temperatureMax': 199,
                         'temperatureMin': 112,
                         'windDirection': 2690,
                         'windGust': 333,
                         'windSpeed': 130}],
             'forecast1': { 'cloudCoverTotal': [],
                            'dewPoint2m': [ 94, 89, 87, 85, 85, 86, 86, 87, 89,
                                            90, 90, 89, 88, 88, 88, 87, 88, 94,
                                            101, 105, 107, 105, 105, 102, 101,
                                            100, 100, 98, 99, 100, 102, 105,
                                            106, 106, 107, 106, 108, 107, 107,
                                            107, 108, 113, 119, 125, 124, 121,
                                            117, 114, 112, 111, 109, 107, 109,
                                            112, 116, 116, 118, 118, 121, 119],
                            'humidity': [ 475, 442, 434, 425, 428, 444, 475,
                                          535, 600, 644, 682, 709, 737, 766,
                                          792, 818, 824, 841, 820, 774, 731,
                                          672, 631, 588, 559, 535, 529, 509,
                                          509, 532, 574, 639, 712, 769, 816,
                                          837, 871, 894, 912, 924, 918, 906,
                                          855, 792, 694, 619, 560, 536, 510,
                                          491, 482, 461, 473, 503, 560, 622,
                                          706, 766, 823, 833],
                            'icon': [ 32767, 1, 32767, 32767, 32767, 32767,
                                      32767, 2, 32767, 32767, 32767, 32767,
                                      32767, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
                                      1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
                                      3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
                                      1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
                                      2, 2, 2, 2, 1, 1],
                            'icon1h': [ 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1,
                                        1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
                                        3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1,
                                        1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
                                        2, 2, 2, 2, 2, 2, 1, 1],
                            'isDay': [ False, False, False, False, False, False,
                                       False, True, False, False, False, False,
                                       False, True, True, True, True, True,
                                       True, True, True, False, False, False,
                                       False, False, False, False, False, False,
                                       True, True, True, True, True, True, True,
                                       True, True, True, True, True, True, True,
                                       True, False, False, False, False, False,
                                       False, False, False, False, True, True,
                                       True, True, True, True, True, True, True,
                                       True, True, True, True, True, True,
                                       False, False, False, False],
                            'precipitationProbablity': None,
                            'precipitationProbablityIndex': None,
                            'precipitationTotal': [ 32767, 32767, 32767, 32767,
                                                    32767, 32767, 32767, 32767,
                                                    32767, 32767, 32767, 32767,
                                                    32767, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0],
                            'start': 1755381600000,
                            'stationId': '12495',
                            'sunshine': [ 32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767],
                            'surfacePressure': [ 10137, 10133, 10132, 10131,
                                                 10131, 10132, 10136, 10142,
                                                 10147, 10152, 10155, 10159,
                                                 10161, 10162, 10165, 10167,
                                                 10169, 10174, 10176, 10178,
                                                 10181, 10181, 10182, 10183,
                                                 10183, 10183, 10182, 10182,
                                                 10182, 10182, 10183, 10186,
                                                 10187, 10189, 10192, 10192,
                                                 10192, 10191, 10190, 10187,
                                                 10186, 10185, 10184, 10182,
                                                 10180, 10178, 10175, 10174,
                                                 10167, 10161, 10156, 10149,
                                                 10146, 10142, 10138, 10139,
                                                 10137, 10136, 10133, 10132],
                            'temperature': [ 32767, 32767, 176, 32767, 32767,
                                             32767, 32767, 32767, 170, 32767,
                                             32767, 32767, 32767, 32767, 210,
                                             216, 217, 218, 217, 212, 201, 183,
                                             167, 157, 148, 141, 134, 128, 123,
                                             117, 117, 120, 131, 144, 155, 166,
                                             176, 184, 191, 197, 199, 203, 204,
                                             198, 188, 174, 158, 146, 138, 133,
                                             129, 124, 121, 119, 121, 128, 143,
                                             161, 181, 196, 208, 212, 218, 223,
                                             224, 229, 227, 220, 207, 190, 172,
                                             159, 151, 147, 142, 137, 134, 130,
                                             130, 139, 154, 174, 193, 210, 223,
                                             232, 238, 247, 248, 249, 242, 234,
                                             221, 204, 186, 176, 168, 162, 156,
                                             152, 147, 144, 143, 145, 150, 163,
                                             177, 186, 197, 205, 210, 211, 210,
                                             206, 200, 193, 185, 176, 172, 162,
                                             158, 153, 150, 143, 139, 136, 133,
                                             133, 140, 148, 157, 168, 178, 186,
                                             192, 196, 198, 198, 195, 189, 176,
                                             163, 151, 140, 131, 125, 121, 117,
                                             115, 112, 113, 118, 126, 138, 152,
                                             166, 175, 179, 182, 187, 188, 188,
                                             186, 181, 170, 151, 138, 128, 120,
                                             114, 111, 106, 105, 102, 102, 107,
                                             116, 131, 143, 157, 166, 173, 176,
                                             182, 183, 183, 182, 177, 168, 151,
                                             141, 129, 124, 116, 112, 108, 106,
                                             104, 105, 110, 117, 129, 144, 158,
                                             166, 176, 183, 187, 189, 192, 190,
                                             185, 174, 156, 150, 141, 134, 131,
                                             122, 118, 115, 113, 112, 117, 125,
                                             139, 154, 168, 178, 186, 193, 199,
                                             198, 196, 192, 192, 181, 164, 157,
                                             148, 142, 137],
                            'temperatureStd': [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                0, 0, 0, 9, 11, 12, 11, 11, 12,
                                                11, 9, 10, 12, 12, 12, 11, 12,
                                                11, 12, 14, 12, 9, 9, 9, 10, 11,
                                                12, 12, 12, 12, 12, 10, 12, 12,
                                                12, 12, 12, 12, 12, 11, 12, 13,
                                                14, 15, 14, 12, 10, 12, 12, 9,
                                                9, 9, 9, 9, 9, 10, 11, 12, 14,
                                                13, 14, 14, 14, 14, 14, 14, 14,
                                                14, 15, 12, 11, 12, 14, 14, 14,
                                                15, 16, 16, 16, 17, 17, 16, 15,
                                                14, 14, 15, 14, 14, 14, 12, 14,
                                                15, 15, 14, 14, 16, 18, 19, 21,
                                                22, 22, 21, 22, 22, 21, 20, 18,
                                                18, 16, 16, 18, 18, 19, 19, 18,
                                                18, 18, 16, 15, 17, 19, 20, 19,
                                                20, 21, 21, 20, 20, 19, 19, 18,
                                                17, 16, 15, 16, 16, 17, 16, 17,
                                                18, 18, 16, 15, 15, 15, 13, 13,
                                                14, 15, 15, 14, 16, 16, 17, 16,
                                                17, 17, 17, 18, 18, 18, 18, 18,
                                                18, 17, 16, 19, 19, 18, 19, 20,
                                                21, 20, 20, 22, 21, 21, 20, 20,
                                                20, 20, 20, 19, 20, 19, 19, 19,
                                                19, 19, 20, 19, 19, 20, 21, 22,
                                                23, 22, 23, 23, 23, 22, 21, 21,
                                                21, 20, 20, 20, 22, 22, 22, 22,
                                                20, 20, 19, 17, 18, 19, 21, 22,
                                                22, 22, 21, 21, 22, 22, 20, 20,
                                                20, 20, 20, 20],
                            'timeStep': 3600000,
                            'windDirection': None,
                            'windGust': None,
                            'windSpeed': None},
             'forecast2': { 'cloudCoverTotal': [],
                            'dewPoint2m': [ 117, 119, 125, 112, 108, 111, 119,
                                            117, 112, 114, 123, 119, 117, 122,
                                            122, 117, 113, 109, 108, 95, 82, 85,
                                            87, 85, 82, 87, 95, 87, 79, 80, 83,
                                            80, 79, 84, 91, 88, 77, 81, 88, 83,
                                            84, 88, 95, 87, 80, 82, 87, 88, 87,
                                            94, 107, 102, 101, 105, 106, 106],
                            'humidity': [ 895, 877, 648, 468, 414, 459, 650,
                                          747, 795, 817, 707, 578, 553, 635,
                                          724, 791, 843, 854, 726, 554, 471,
                                          509, 656, 766, 802, 813, 688, 549,
                                          491, 517, 694, 796, 839, 857, 709,
                                          574, 500, 534, 704, 802, 863, 863,
                                          724, 559, 492, 511, 660, 752, 829,
                                          858, 736, 581, 536, 571, 717, 816],
                            'icon': [ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
                                      3, 3, 3, 19, 3, 3, 3, 3, 2, 2, 2, 3, 3, 3,
                                      3, 3, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2,
                                      2, 2, 3, 3, 2, 2, 2, 2, 3, 2, 3, 2, 2,
                                      2],
                            'icon1h': [ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3,
                                        3, 3, 3, 3, 19, 3, 3, 3, 3, 2, 2, 2, 3,
                                        3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 2, 2,
                                        2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 3, 2,
                                        3, 2, 2, 2],
                            'isDay': [ False, False, True, True, True, True,
                                       True, False, False, False, True, True,
                                       True, True, True, False, False, False,
                                       True, True, True, True, True, False,
                                       False, False, True, True, True, True,
                                       True, False, False, False, True, True,
                                       True, True, True, False, False, False,
                                       True, True, True, True, True, False,
                                       False, False, True, True, True, True,
                                       False, False],
                            'precipitationProbablity': None,
                            'precipitationProbablityIndex': None,
                            'precipitationTotal': [ 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 1,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                    0, 0],
                            'start': 1755640800000,
                            'stationId': '12495',
                            'sunshine': [ 32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767, 32767, 32767, 32767, 32767,
                                          32767],
                            'surfacePressure': [ 10122, 10112, 10099, 10088,
                                                 10080, 10073, 10084, 10090,
                                                 10086, 10081, 10077, 10076,
                                                 10073, 10071, 10075, 10074,
                                                 10072, 10073, 10084, 10094,
                                                 10098, 10099, 10116, 10120,
                                                 10122, 10121, 10125, 10126,
                                                 10118, 10130, 10144, 10152,
                                                 10150, 10155, 10154, 10149,
                                                 10141, 10143, 10154, 10157,
                                                 10151, 10150, 10150, 10148,
                                                 10142, 10143, 10156, 10155,
                                                 10153, 10155, 10159, 10154,
                                                 10144, 10146, 10147, 10150],
                            'temperature': [],
                            'temperatureStd': [],
                            'timeStep': 10800000,
                            'windDirection': None,
                            'windGust': None,
                            'windSpeed': None},
             'forecastStart': None,
             'threeHourSummaries': None,
             'warnings': []}}

Installing and Importing External Packages#

Pythonโ€™s strength, however, lies in its enormous selection of available packages. For most purposes, there are Python packages available. One such directory is PyPI, which lists over 400,000 packages.

The installation of new packages for Python is easy. For this, you open a terminal (command line) and enter the command pip install <packetname>.

For example, we want to display the weather data we just loaded. To do this we use:

  • first the pandas package for creating a table from the weather data.

  • then we use the plotly package to draw a chart.

  • finally we create a web server with dash that will display the chart to us

We install all three with pip. We can do this in a single call. We use the --quiet switch to reduce the output.

# pip install pandas plotly dash  --quiet

Now we load both packages, with pandas typically assigned the alias pd and Plotly Express, which is easy to use, assigned the alias px.

import pandas as pd
import plotly.express as px
import dash

First, we convert the daily weather forecast days data from the weather station with the stationID into a table, since this can be processed by Plotly Express. Tables are called DataFrames in Pandas (in general, thatโ€™s what such tables are called in data science). So we create from the weather forecast a new object instance of type DataFrame via

df = pd.DataFrame(weather[stationID]['days'])
df
stationId dayDate temperatureMin temperatureMax precipitation windSpeed windGust windDirection sunshine sunrise ... moonrise moonset moonriseOnThisDay moonsetOnThisDay sunriseOnThisDay sunsetOnThisDay moonPhase icon icon1 icon2
0 None 2025-08-17 139 218 0 185 370 3210 32767 1755400811000 ... 1755464484000 1755439776000 1.755464e+12 1755439776000 1755400811000 1755452877000 6 2 None None
1 None 2025-08-18 117 204 0 130 315 3380 32767 1755487306000 ... 1755554333000 1755530459000 1.755554e+12 1755530459000 1755487306000 1755539156000 6 2 None None
2 None 2025-08-19 119 229 0 93 204 3020 32767 1755573801000 ... 1755645158000 1755620045000 1.755645e+12 1755620045000 1755573801000 1755625435000 6 2 None None
3 None 2025-08-20 130 249 0 111 278 2660 32767 1755660295000 ... 1755736539000 1755708637000 NaN 1755708637000 1755660295000 1755711712000 7 2 None None
4 None 2025-08-21 143 211 0 93 296 3150 32767 1755746790000 ... 1755736539000 1755796531000 1.755737e+12 1755796531000 1755746790000 1755797989000 7 3 None None
5 None 2025-08-22 125 198 1 130 315 3260 32767 1755833285000 ... 1755827997000 1755884001000 1.755828e+12 1755884001000 1755833285000 1755884264000 7 3 None None
6 None 2025-08-23 112 188 0 148 315 3030 32767 1755919779000 ... 1755919307000 1755971217000 1.755919e+12 1755971217000 1755919779000 1755970539000 0 3 None None
7 None 2025-08-24 102 183 0 111 296 3010 32767 1756006274000 ... 1756010376000 1756058291000 1.756010e+12 1756058291000 1756006274000 1756056812000 0 2 None None
8 None 2025-08-25 104 192 0 111 315 2810 32767 1756092768000 ... 1756101241000 1756145313000 1.756101e+12 1756145313000 1756092768000 1756143084000 0 2 None None
9 None 2025-08-26 112 199 0 130 333 2690 32767 1756179263000 ... 1756191992000 1756232335000 1.756192e+12 1756232335000 1756179263000 1756229356000 0 2 None None

10 rows ร— 21 columns

Now we plot the data df as a line chart using Plotly, with the date axis on the x-axis (dayDate) and the minimum temperature temperatureMin and the maximum temperature temperatureMax on the y-axis.

fig=px.line(df, x="dayDate", y=["temperatureMin", "temperatureMax"])
fig.show(renderer="svg")
../_images/6eacf3d01473f950ec1b8cd43eff6a2467e5e86b8e937965e087d413355100bc.svg

Finally, we want to display this diagram on a webpage on a web server. Here we use the package dash, which enables creating a webpage with Python commands and displaying interactive Plotly charts in it.

import dash

app = dash.Dash()

Then we generate a webpage with a heading (H1) that contains the plot as a Graph.

app.layout = dash.html.Div(children = [
    dash.html.H1(children='Wetter in Rostock'),
    dash.dcc.Graph(id="fare_vs_age", figure=fig)
])

And start the web server.

app.run_server()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[16], line 1
----> 1 app.run_server()

File ~/Documents/code/Lehre/ProgrammierUebung/.venv/lib/python3.12/site-packages/jupyter_dash/jupyter_app.py:176, in JupyterDash.run_server(self, mode, width, height, inline_exceptions, **kwargs)
    154 """
    155 Serve the app using flask in a background thread. You should not run this on a
    156 production server, use gunicorn/waitress instead.
   (...)    173     ``Dash.run_server`` method.
    174 """
    175 # Get superclass run_server method
--> 176 super_run_server = super(JupyterDash, self).run_server
    178 if not JupyterDash._in_ipython:
    179     # If not in IPython context, call run run_server synchronously
    180     super_run_server(**kwargs)

AttributeError: 'super' object has no attribute 'run_server'

This starts a web server that we can access in a browser at http://127.0.0.1:8083/. It shows a webpage with an interactive chart that displays the weather data.