Python-Grundlagen

Python Grundlagen - Ein kurzes Tutorial

Python ist eine universelle, moderne high-level Programmiersprache, die
verschiedene Programmierparadigmen ermöglicht:

Allgemeine Charakteristik / Ziele

  • Ausdruckstark: Unterstützt das Schreiben von kurzem, klaren Programmcode.
  • Komplexe(re) Datenstrukturen möglich bzw. eingebaut, wie Listen oder Maps (dict).

Weitere (technische) Details

  • Dynamisch typisiert (dynamically typed)
  • Automatische Speicherverwaltung (automatic memory management)
  • in der Regel interpretiert, der Standard-Interpreter ist CPython (wird aber auch in Byte-Code kompiliert.)
  • es gibt aber weitere Implementierungen von Python.
  • Performanz-kritische (in Bezug auf die Laufzeit) Teile können in C/C++ entwickelt werden.
  • es gibt aber auch Compiler:
  • umfangreiche Bibliotheken für verschiedenste Anwendungsfälle
    • insbesondere auch im Bereich Data Science, Machine Learning, Artificial Intelligence weit verbreitet.

Python Style Guide

Sauberer Pythoncode folgt einem vorgegeben Stil, der in einem "Python Enhancement Proposals" pep beschrieben ist: https://www.python.org/dev/peps/pep-0008

Tipp: Verwenden Sie von Beginn an einen Type-Checker, um den von Ihnen entwickelten Code zu überprüfen, z.B. Style-Checker für jupyter notebooks.

In [2]:
%load_ext pycodestyle_magic
%pycodestyle_on

Variablen

Variablen sind Namen, die auf einen Wert, eine Funktion oder ein Objekt zeigen:

In [3]:
a_string = "hello, world"
an_integer = 12
a_float = 3.14
a_boolean = True

Beachte die dynamische Typisierung: Variablen haben explizite Typen. Diese werden bei der Zuweisung automatisch erkannt.

In [4]:
type(an_integer)
Out[4]:
int

Typumwandlungen (cast)

In [5]:
# Convert a string to an integer and vice-versa:
i = 12
s = str(i)
type(s)
Out[5]:
str

Print

Einfache Ausgabe via print

In [6]:
print("hello, world")
print(12)
print((5+3)/2)
hello, world
12
4.0
In [7]:
# This is a comment.

# To print several items,
# use commas (items will be separated by spaces):
print("abc", 12, a_float)

# A long statement may be split using backslash:
print('a long statement may be split using backslash',
      'this is still the same statement',
      'the end.')
abc 12 3.14
a long statement may be split using backslash this is still the same statement the end.

Zeichenketten (Strings)

Die Syntax erlaubt es drei verschiedene Möglichkeiten für Zeichenketten:

In [8]:
string_single_quotes = 'abc'
string_double_quotes = "abc"
string_triple_quotes = """this is
    a multiline
    string."""
In [9]:
# It's useful when we need to include quotes in a string:
string1 = 'hello "world"'
string2 = "don't"

# Otherwise we have to use backslashes:
string2 = 'don\'t'

# Other special characters: http://docs.python.org/ref/strings.html
In [10]:
# Strings are objects which support many operations:

strings = string1 + " : " + string2
print(strings)

# better: use join!
print(" ".join([string1, ':', string2]))
hello "world" : don't
hello "world" : don't

(Objekt-orientiert) Punkt-Notation erlaubt es auf Instanzen (hier von Zeichenketten) Methoden auszurufen:

In [11]:
strings_uppercase = strings.upper()
strings_uppercase
Out[11]:
'HELLO "WORLD" : DON\'T'

Weieter Zeichenketten-Methoden siehe z.B.: http://docs.python.org/lib/string-methods.html

Slicing

Extraktion von Substrings via Slicing:

In [12]:
beginning = strings[0:4]
beginning
Out[12]:
'hell'

String Formating Syntax

von https://realpython.com/python-f-strings/

In [13]:
name = "Klaus"
age = 74
In [14]:
# simple concatenate - note the explicit cast of the int
"Hello " + name + ". You are " + str(age) + "."
Out[14]:
'Hello Klaus. You are 74.'
In [15]:
# old school %-formating
"Hello, %s. You are %i." % (name, age)
Out[15]:
'Hello, Klaus. You are 74.'
In [16]:
# str.format()
"Hello, {}. You are {}.".format(name, age)
Out[16]:
'Hello, Klaus. You are 74.'
In [17]:
"Hello, {1}. You are {0}.".format(age, name)
Out[17]:
'Hello, Klaus. You are 74.'

Tipp: Verwenden Sie f-Strings:

In [18]:
# Note the f before the first "
f"Hello, {name}. You are {age}."
Out[18]:
'Hello, Klaus. You are 74.'
In [19]:
# you can also use methods inside the f-String
f"{name.lower()} is funny."
Out[19]:
'klaus is funny.'

Stripping

In [20]:
a_string = "  lol abc lol  "
print(a_string)
# Strip spaces at beginning and end of a string:
stripped = a_string.strip()
print(stripped)
  lol abc lol  
lol abc lol

Ersetzungen

Ersetze eine Substring in einem String:

In [21]:
print(a_string)
newstring = a_string.replace('abc', 'def')
print(newstring)
  lol abc lol  
  lol def lol  

Strings sind immutable

Strings sind Konstanten, die nicht geändert werden können. Alle String-Operationen erzeugen neue Strings.

Listen (lists)

Eine Liste ist ein dynamisches Array von beliebigen Objekten/Typen:

Diese werden über eckige Klammern (square brackets) deklariert:

In [22]:
# declare a empty lists by
a_list = []  # or by
a_list = list()
type(a_list)
Out[22]:
list
In [23]:
a_list.append(3)  # append an element
a_list.append("bla")
a_list
Out[23]:
[3, 'bla']
In [24]:
a_list = [1, 2, 3, 'abc', 'def']
In [25]:
# Lists may contain lists:

another_list = [a_list, 'abc', a_list, [1, 2, 3]]
# (in this case, "a_list" is only a pointer)
In [26]:
# Access a specific element by index (index starts at zero):

elem = another_list[1]
print(elem)
elem2 = another_list[3][1]
print(elem2)
abc
2
In [27]:
# It's easy to test if an item is in the list:

if 'abc' in a_list:
    print('bingo!')
bingo!
In [28]:
# Extracting a part of a list is called slicing:

list2 = a_list[2:4]  # Returns a list with items 2 and 3 (not 4)
print(list2)
[3, 'abc']
In [29]:
# Other list operations like appending:

print(a_list)
a_list.append('ghi')
print(a_list)
a_list.remove('abc')
print(a_list)
[1, 2, 3, 'abc', 'def']
[1, 2, 3, 'abc', 'def', 'ghi']
[1, 2, 3, 'def', 'ghi']

List Comprehension

In [31]:
vals = [1, 2, 3, 5, 7, 9, 10]

double_vals = [2 * v for v in vals]
print(double_vals)
[2, 4, 6, 10, 14, 18, 20]

Weitere Listen-Operationen siehe http://docs.python.org/lib/typesseq.html

Tuples

Ein Tuple ist ähnlich einer Liste, aber mit fester Länge (fixed-size) und unveränderlich (immutable). Die Elemente eines Tupels können nicht geändert oder gelöscht werden. Neue Elemente können nicht an- oder eingefügt werden

Die Elemente werden mittels Komma getrennt, wobei meist die Tuples mittels runden Klammern deklariert werden:

In [32]:
a_tuple = (1, 2, 3, 'abc', 'def')
a_tuple[3]  # element indexing starts with 0
Out[32]:
'abc'
In [33]:
# But parentheses are optional:

another_tuple = 1, 2, 3, 'abc', 'def'
another_tuple
Out[33]:
(1, 2, 3, 'abc', 'def')
In [34]:
# Tip: a tuple containing only one item must be declared using a comma,
#      else it is not considered as a tuple:

a_single_item_tuple = 'one value',

type(a_single_item_tuple)
Out[34]:
tuple

Dictionaries

(analog zu Maps in Java)

Dictionaries (dict) sind Schlüssel-Wert Abbildungen (Key-Value Maps), analog einer mathematischen Funktion.

Dictionaries können über geschweifte Klammern deklariert werden:

In [35]:
point = {}  # form an empty dictionary or by:
point = dict()

p = (1.2, -40.0, 29)
point['x'] = p[0]
point['y'] = p[1]
point['z'] = p[2]
point['z']
Out[35]:
29
In [36]:
# declare a dict by curly brackets
point = {'x': p[0], 'y': p[1], 'z': p[2]}

point['z'] = 55.
In [37]:
point = {'x': p[0], 'y': p[1], 'z': p[2]}

del point['x']


# print(point['x']) # raises an error
print(point.get('x'))  # returns None
None
In [38]:
if 'x' not in point:
    print('missing x')

for key in point:
    print(key)
missing x
y
z

Blocks und Einrückungen (Kontrollfluss - control flow)

Code-Blöcke werden durch Einrückungen (indentation) abgegrenzt (und nicht durch Klammern { }). Einrückungen entstehen durch Tabs oder Leerzeichen.

Tipp: Niemals Tabs mit Leerzeichen mischen, da dann die Tiefe der Einrückung oft nicht visuell richtig angezeigt wird. Am besten einen Editor/IDE verwenden, die Tabs in Leerzeichen konvertiert.

if / elif / else - Abzweigungen

In [39]:
a = 3
print(a)
if a == 3:
    print('The value of a is:')
    print('a=3')
3
The value of a is:
a=3
In [40]:
if a == 'test':
    print('The value of a is:')
    print('a = "test"')
    test_mode = True
else:
    print('a != "test"')
    test_mode = False
    # do_something_else()
a != "test"
In [41]:
b = 2
if a == 1 or a == 2:
    pass  # do nothing
elif a == 3 and b > 1:
    pass
elif a == 3 and not b > 1:
    pass
else:
    pass

while Loops

In [42]:
a = 1
while a < 10:
    print(a)
    a += 1
1
2
3
4
5
6
7
8
9

for Loops

Ein Python for-Schleife ist eigentlich eine foreach-Schleife:

In [43]:
for a in range(4):
    print(a)
0
1
2
3
In [44]:
my_list = [2, 4, 8, 16, 32]
for a in my_list:  # my_list has to be an iterator
    print(a)
2
4
8
16
32

Funktionen

Eine Funktion wird definiert mittels des def Schlüsselworts:

In [45]:
def my_function(arg1, arg2, arg3='default value'):
    print('arg1 =', arg1)
    print('arg2 =', arg2)
    print('arg3 =', arg3)


# Call it (note that arguments are not strongly typed):
my_function(17, 'abc', "def")
arg1 = 17
arg2 = abc
arg3 = def

Es können (zur Dokumentation) Typ-Hinweise gegeben werden:

siehe https://docs.python.org/3/library/typing.html

In [46]:
# type hints
def my_function(arg1: int, arg2: str, arg3: str = 'default value') -> None:
    print('arg1 =', arg1)
    print('arg2 =', arg2)
    print('arg3 =', arg3)
In [47]:
# The 3rd arg may be omitted:
my_function('a string', 12)
arg1 = a string
arg2 = 12
arg3 = default value
In [48]:
# A function may return a value:
def fun2():
    print('fun2')
    return 'any return value'


# call it:
print('fun2() = %s' % fun2())
fun2
fun2() = any return value

Funktionale Aspekte von Python

Funktionen sind Brüger erster Klasse (first-class citizen). Funktionen können in Variablen gespeichert werden und als Funktionsargumente übergeben werden.

In [49]:
def f(x):
    return x ** 2


def operate(h, x):  # higher-order function
    print(h(x))


operate(f, 5)
25

Anonyme Funktionen (lambda functions)

Mittels des lambda-Schlüsselworts können anonyme Funktionen erzeugt werden:

In [50]:
g = lambda x: x**3  # assign to the variable g the unnamed function
g(5)

# Note: this is not pep8 conform.
Out[50]:
125
1:1: E731 do not assign a lambda expression, use a def
In [51]:
operate(lambda x: x**3, 5)
125
In [53]:
def make_incrementor(n):
    return lambda x: x + n


f = make_incrementor(3)
g = make_incrementor(7)
print(f(5), g(5))
print(make_incrementor(7)(8))
8 12
15
In [55]:
foo = [2, 18, 9, 22, 17, 24, 8, 12, 27]

# map and filter return iterators in python3
print(list(filter(lambda x: x % 3 == 0, foo)))

print(list(map(lambda x: x * 2 + 10, foo)))
[18, 9, 24, 12, 27]
[14, 46, 28, 54, 44, 58, 26, 34, 64]
In [57]:
from functools import reduce

print(reduce(lambda x, y: x + y, foo))
print(reduce(lambda x, y: x + y, map(lambda x: x * 2 + 10, foo)))
139
368
In [58]:
from operator import add

reduce(add, map(lambda x: x * 2 + 10, foo))
Out[58]:
368
In [63]:
import operator

expr = "28+32+++32++39"
print(reduce(add, map(int, filter(bool, expr.split("+")))))

operator.mul(3, 10)
operator.pow(2, 3)
operator.itemgetter(1)([1, 2, 3])

# more see http://ua.pycon.org/static/talks/kachayev
131
Out[63]:
2

Skripts

Shebang:

  • !#/usr/bin/env python meist aber python2 Interpreter!
  • !#/usr/bin/env python3

Kommandozeilen-Argumente

Um Argumente auf der Kommandozeile einem Skript zu übergeben, ist es notwendig Module von der Standard Library zu importieren:

import sys

print(f"Name of the script      : {sys.argv[0]=}")
print(f"Arguments of the script : {sys.argv[1:]=}")

Tipp: Verwenden Sie lieber das argparse-Modul, um Kommandozeilenargumente zu behandeln. Arbeiten Sie hierzu das argparse-Tutorial durch.

mehr zu Kommandozeilenargumenten siehe https://realpython.com/python-command-line-arguments/

Dateiverarbeitung

Öffnen einer Datei zum Lesen:

f = open('my_file.txt')
# open() returns a file object with several methods.

# To read a specific number of characters:
s = f.read(10)

# To read the whole file in a string
s = f.read()

#To close an open file:
f.close()

To iterate over file lines:

f = open('my_file.txt')
for line in f:
    print (line)
f.close()

Besser mit with-Statement (Context Manager) - auch zur Fehlerbehandlung (siehe unten):

In [8]:
# open for read 'r'
with open('../bash/work/countries.txt', 'r') as f:
    for line in f:
        print(line, end='') #  there is already a newline at the end
France
Canada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zealand

Öffnen von Dateien zum Schreiben (write w) auch am besten mit with-Statement:

with open('out_file.txt', 'w') as fw:
  fw.write('a line of text\n') # note '\n' is necessary here
  fw.write(a_string)

Für weitere Datei-Operationen siehe z.B. http://docs.python.org/lib/bltin-file-objects.html

OS Modul

Um mit Dateien umzugehen, z.B. um eine Datei zu löschen werden wieder Module aus der Standard Bibliothek benötigt:

import os, os.path
if os.path.exists('my_file.txt'):
    os.remove('my_file.txt')
In [2]:
import os

Laufendes Arbeitsverzeichnis

In [11]:
cwd = os.getcwd()
type(cwd) 
Out[11]:
str

Wechseln des Arbeitsverzeichnis

os_chdir(path)

Verzeichnisinhalt auflisten

Anzeigen der Dateien für eine Verzeichnis: os.listdir(".") (hier übergeordnetes Verzeichnis des Arbeitverzeichnis)

In [13]:
files = os.listdir("..")
type(files), type(files[0]) # python list of str
Out[13]:
(list, str)

Erzeugen eines Verzeichnisses

os.mkdir(name_of_dir)

Umgebungsvariablen

z.B. HOME Umgebungsvariable erhalten:

In [20]:
os.environ['HOME']
Out[20]:
'/home/chris'

Unix/Linux User/Group Funktionalitäten

z.B: User-Id erhalten:

In [23]:
os.getuid()
Out[23]:
1001

Information zum Betriebssystem

In [25]:
os.uname()
Out[25]:
posix.uname_result(sysname='Linux', nodename='platon', release='5.4.0-72-generic', version='#80~18.04.1-Ubuntu SMP Mon Apr 12 23:26:25 UTC 2021', machine='x86_64')

Ausführen eines Programms in einer Subshell

os.system(some_command)

Dies ist nicht zu empfehlen. Verwenden Sie stattdessen das Modul subprocess (siehe unten).

für mehr zum OS-Modules siehe help(os) oder die Referenz https://docs.python.org/3.9/library/os.html

Module subprocess

Einfaches Ausführen eines Kommandos in einem Unterprozess subprocess.run(command)

In [71]:
import subprocess
proc = subprocess.run(["ls", "-l"], 
                      universal_newlines=True, 
                      stdout = subprocess.PIPE)
In [72]:
proc_out = proc.stdout.splitlines()
print(proc_out)
['total 108', '-rw-rw-r-- 1 chris chris     0 Mai  6 13:09 geckodriver.log', 'drwxr-xr-x 2 chris chris  4096 Mai 24  2020 intro_exercises', '-rw-r--r-- 1 chris chris   116 Mär 10 16:12 literatur.txt', '-rw-r--r-- 1 chris chris   659 Mai  7 11:08 mymodule.py', 'drwxr-xr-x 3 chris chris  4096 Feb 23 17:21 programs', 'drwxr-xr-x 2 chris chris  4096 Mai  7 11:08 __pycache__', '-rw-r--r-- 1 chris chris 71261 Mai  7 13:41 Python-Grundlagen.ipynb', '-rw-rw-r-- 1 chris chris  2260 Mai  7 12:49 Python-os-Modul.ipynb', '-rw-rw-r-- 1 chris chris  2173 Mai  7 09:39 selenium.ipynb', '-rw-r--r-- 1 chris chris   201 Jan 31 16:21 test.py', '-rw-r--r-- 1 chris chris   200 Jan 31 13:03 test.py~']

Auch Pipes etc. möglich.

Mehr siehe https://docs.python.org/3/library/subprocess.html

Klassen

Python erlaubt Klassen-basierte objektoriente Programmierung.

Eine Klasse gibt die Struktur für Instanzen der Klassen (Objekte) vor. Operationen auf den Instanzen können mittels Methoden ("Funktionen" für Klassen) definiert werden.

In Python enthält eine Klasse also

  • Attribute (attributes), d.h. Variablen und
  • Methoden (methods).

Eine Klasse wird mit dem class-Schlüsselwort definiert.

  • Jeder Methode der Klasse sollte als erstes Argument self haben (Referenz auf die laufende Instanz der Klasse).
  • Einige Methoden haben spezielle Bedeutung, z.B.:

In [66]:
class Point:
    """
    Simple class for representing a point in a Cartesian coordinate system.
    """

    def __init__(self, x, y):
        """
        Create a new Point at x, y.
        """
        self.x = x  # instance variable self.x
        self.y = y

    def translate(self, dx, dy):
        """
        Translate the point by dx and dy in the x and y direction.
        """
        self.x += dx
        self.y += dy

    def __str__(self):
        return("Point at [%f, %f]" % (self.x, self.y))
In [67]:
# To create a new instance of a class:

p1 = Point(0, 0)  # this will invoke the __init__ method in the Point class

print(p1)  # this will invoke the __str__ method
Point at [0.000000, 0.000000]
In [68]:
# To invoke a class method in the class instance `p`:

p2 = Point(1, 1)

# this modifies the internal store coordinates
p1.translate(0.25, 1.5)

print(p1)
print(p2)
Point at [0.250000, 1.500000]
Point at [1.000000, 1.000000]

Methoden können dynamisch zu Klassen hinzugefügt werden:

In [71]:
def point_add(self, other):
    """
    Defines the +-Operator
    """
    assert isinstance(other, Point)  # check the type
    newPoint = Point(self.x, self.y)
    newPoint.x += other.x
    newPoint.y += other.y
    return newPoint
In [72]:
# __add__ is the special methods for implementing the +-Operator
# we can dynamically extend the class Point by this method
Point.__add__ = point_add
In [73]:
print(p1 + p2)
Point at [1.250000, 2.500000]

Exceptions

Wenn zur Laufzeit Bedingungen, die erfüllt sein müssten, nicht gegeben sind, können Exceptions (Ausnahmen) geworfen werden. Wenn beispielsweise die mathematische Operation Teile $a$ durch $b$ ausgeführt werden soll, macht dies nur Sinn mit der Bedingung, dass $b\neq 0$ ist. Hier kann in der Implementierung der Teil-Opration dies überprüft werden und falls dies nicht der Fall ist eine Exception geworfen werden.

In Python werden Exceptions mittels raise geworfen:

In [74]:
raise Exception("description of the error")
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-74-c32f93e4dfa0> in <module>
----> 1 raise Exception("description of the error")

Exception: description of the error
In [77]:
# A typical use of exceptions is to abort functions
# when some error condition occurs, for example:
def point_add2(self, other):
    """
    Defines the +-Operator
    """
    if not isinstance(other, Point):  # check the type
        raise TypeError("Second argument of +-Operator is not a Point!")
    newPoint = Point(self.x, self.y)
    newPoint.x += other.x
    newPoint.y += other.y
    return newPoint


Point.__add__ = point_add2
In [78]:
p3 = Point(2., 3.)
print(p3 + 4.) # this causes an exception
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-78-658d1a1e3e7b> in <module>
      1 p3 = Point(2., 3.)
----> 2 print(p3 + 4.)

<ipython-input-77-da6623114068> in point_add2(self, other)
      6     """
      7     if not isinstance(other, Point):  # check the type
----> 8         raise TypeError("Second argument of +-Operator is not a Point!")
      9     newPoint = Point(self.x, self.y)
     10     newPoint.x += other.x

TypeError: Second argument of +-Operator is not a Point!

Fangen von Exceptions

Programmcode sollte mit Exceptions (Ausnahmen) umgehen können. Dazu sollte in der Beschreibung der Schnittstellen zu Modulen (siehe unten) angegeben sein, welche Ausnahmen auftreten können. Das Behandeln von Ausnahmen wird als Exception Handling bezeichnet.

Dabei werden in Python mittels try und except die Ausnahmen/Fehler "gefangen", um damit programmatisch umzugehen:

In [83]:
import sys
try:
    print("test if we can add an int:")
    p4 = p3 + 4
except Exception as e:
    print("Caught an exception: ", str(e),  file=sys.stderr)  # print to stderr
# 
test if we can add an int:
Caught an exception:  Second argument of +-Operator is not a Point!

Beachte: Einfaches Ausgeben der Exception-Ursache und Weiterausführen des Codes (wie im obigen Beispiel) ist ein Antipattern. Dies kann zu schwer zu findenden Bugs führen, da in der Regel, die Exception nicht ohne Grund geworfen wurde. Richtiges Exception-Handling ist bei der Softwareentwicklung sehr wichtig.

Mehr zum Thema Fehler und Exceptions, siehe z.B. https://docs.python.org/3/tutorial/errors.html

Modules

TODO deutsch

One of the most important concepts in good programming is to reuse code and avoid repetitions.

The idea is to write functions and classes with a well-defined purpose and scope, and reuse these instead of repeating similar code in different part of a program (modular programming). The result is usually that readability and maintainability of a program is greatly improved. What this means in practice is that our programs have fewer bugs, are easier to extend and debug/troubleshoot.

Python supports modular programming at different levels. Functions and classes are examples of tools for low-level modular programming. Python modules are a higher-level modular programming construct, where we can collect related variables, functions and classes in a module. A python module is defined in a python file (with file-ending .py), and it can be made accessible to other Python modules and programs using the import statement.

Consider the following example: the file mymodule.py contains simple example implementations of a variable, function and a class:

In [85]:
%%file mymodule.py
# ipython magic function writes the content of the cell to a text file "mymodule.py"

"""
Example of a python module. Contains a variable called my_variable,
a function called my_function, and a class called MyClass.
"""

my_variable = "Hello World"

def my_function():
    """
    Example function
    """
    return my_variable
    
class MyClass:
    """
    Example class.
    """

    def __init__(self):
        self.variable = my_variable
        
    def set_variable(self, new_value):
        """
        Set self.variable to a new value
        """
        self.variable = new_value
        
    def get_variable(self):
        return self.variable
Overwriting mymodule.py
In [87]:
# We can import the module `mymodule` into our Python program using `import`:
import mymodule
In [88]:
# Use `help(module)` to get a summary of what the module provides:
help(mymodule)
In [89]:
mymodule.my_variable
Out[89]:
'Hello World'
In [91]:
mymodule.my_function()
Out[91]:
'Hello World'
In [93]:
my_class = mymodule.MyClass()
my_class.set_variable(10)
my_class.get_variable()
Out[93]:
10

Übung Recherche

Recherchieren Sie weshalb Python-Dateien oft die Konstruktion if __name_ == "__main__" enthalten. Wie funktioniert die Konstruktion? Was ist der Sinn der Konstruktion, bzw. wann wird diese eingesetzt?

Futher readings