Modularization#

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:
๐ geometry
๐ ImmutablePoint.py
๐ Point.py
๐ Line.py
๐ Pentagon.py
๐ Polygon.py
๐ Tetragon.py
๐ Triangle.py
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.
๐ geometry
๐ points
๐ ImmutablePoint.py
๐ Point.py
๐ shapes
๐ Line.py
๐ Pentagon.py
๐ Polygon.py
๐ Tetragon.py
๐ Triangle.py
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")
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.