Python ist
eine universelle, moderne high-level Programmiersprache, die
verschiedene Programmierparadigmen ermöglicht:
dict
).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.
%load_ext pycodestyle_magic
%pycodestyle_on
Variablen sind Namen, die auf einen Wert, eine Funktion oder ein Objekt zeigen:
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.
type(an_integer)
int
# Convert a string to an integer and vice-versa:
i = 12
s = str(i)
type(s)
str
Einfache Ausgabe via print
print("hello, world")
print(12)
print((5+3)/2)
hello, world 12 4.0
# This is a comment.
# To print several items,
# use commas (items will be separated by spaces):
print("abc", 12, a_float)
# multiline is possible, if there are braces
print('multiline is possible,'
' if there are braces')
multiline = 'a long statement may be split using backslash',\
'this is still the same statement',\
'the end.'
print(multiline) # Note: multiline is a tuple of strings (see below)
abc 12 3.14 multiline is possible, if there are braces ('a long statement may be split using backslash', 'this is still the same statement', 'the end.')
Die Syntax erlaubt es drei verschiedene Möglichkeiten für Zeichenketten:
string_single_quotes = 'abc'
string_double_quotes = "abc"
string_triple_quotes = """this is
a multiline
string."""
# Note: a multiline string is a python here-document
# 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
# 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:
strings_uppercase = strings.upper()
strings_uppercase
'HELLO "WORLD" : DON\'T'
Weieter Zeichenketten-Methoden siehe z.B.: http://docs.python.org/lib/string-methods.html
Extraktion von Substrings via Slicing:
beginning = strings[0:4]
beginning
'hell'
name = "Klaus"
age = 74
# simple concatenate - note the explicit cast of the int
"Hello " + name + ". You are " + str(age) + "."
'Hello Klaus. You are 74.'
# old school %-formating
"Hello, %s. You are %i." % (name, age)
'Hello, Klaus. You are 74.'
# str.format()
"Hello, {}. You are {}.".format(name, age)
'Hello, Klaus. You are 74.'
"Hello, {1}. You are {0}.".format(age, name)
'Hello, Klaus. You are 74.'
Tipp: Verwenden Sie f-Strings:
# Note the f before the first "
f"Hello, {name}. You are {age}."
'Hello, Klaus. You are 74.'
# you can also use methods inside the f-String
f"{name.lower()} is funny."
'klaus is funny.'
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
Ersetze eine Substring in einem String:
print(a_string)
newstring = a_string.replace('abc', 'def')
print(newstring)
lol abc lol lol def lol
Strings sind Konstanten, die nicht geändert werden können. Alle String-Operationen erzeugen neue Strings.
Die Konvertierung von Strings (Zeichenketten) zu Bytes und umgekehrt hängt von der Kodierung (dem Encoding) zusammen. In Python 3 ist der Quellcode standardmäßig in UTF-8 kodiert. Eine Tabelle der Unicode-Codeposition und der korreponierenden numerischen UTF-8 Repräsentation finden Sie z.B. in https://www.utf8-zeichentabelle.de/unicode-utf8-table.pl?number=1024. Beachten Sie, dass ein Zeichen abhängig von Zeichen mit 1-4 Bytes in UTF( repräsentiert wird.
Beispiel: Der griechische Buchstabe τ
wird in UTF-8 mit zwei Bytes kodiert (numerische Repräsentation).
- binär: 11001111 10000100
- hexadezimal: 0xcf 0x84
als
b'\xcf\x84'
als zwei Hexadezimalzahlenτ
(Zeichenkette mit nur einem Zeichen)'τ'.encode('utf-8')
b'\xcf\x84'
#0xF0 0x9D 0x9C 0x8F
b'\xcf\x84'.decode('utf-8')
'τ'
Gewisse Zeichen (ein Byte-Zeichen?) werden im Bytestringdarstellung auch direkt als entsprechendes Zeichen dargestellt:
# "latin small letter o" ist 0x6f in UTF-8
# "6" is 0x36
b'\x6f\x36'
b'o6'
An folgendem Beispiel wird der Unterschied zwischen verschiedenen (Unicode)-Kodierungen klar. D.h. dieselbe Bytefolge kann abhängig von der Kodierung eine andere Stringfolge ergeben.
print(b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'.decode('utf-16'))
print(b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'.decode('utf-8'))
蓏콯캁澽苏 τoρνoς
Hinweis:
Eine Liste ist ein dynamisches Array von beliebigen Objekten/Typen:
Diese werden über eckige Klammern (square brackets) deklariert:
# declare a empty lists by
a_list = [] # or by
a_list = list()
type(a_list)
list
a_list.append(3) # append an element
a_list.append("bla")
a_list
[3, 'bla']
a_list = [1, 2, 3, 'abc', 'def']
# Lists may contain lists:
another_list = [a_list, 'abc', a_list, [1, 2, 3]]
# (in this case, "a_list" is only a pointer)
# 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
# It's easy to test if an item is in the list:
if 'abc' in a_list:
print('bingo!')
bingo!
# 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']
# 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']
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
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:
a_tuple = (1, 2, 3, 'abc', 'def')
a_tuple[3] # element indexing starts with 0
'abc'
# But parentheses are optional:
another_tuple = 1, 2, 3, 'abc', 'def'
another_tuple
(1, 2, 3, 'abc', 'def')
# 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)
tuple
Mehr zu Tupeln: http://docs.python.org/library/stdtypes.html#sequence-types-str-unicode-...
(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:
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']
29
# declare a dict by curly brackets
point = {'x': p[0], 'y': p[1], 'z': p[2]}
point['z'] = 55.
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
if 'x' not in point:
print('missing x')
for key in point:
print(key)
missing x y z
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.
a = 3
print(a)
if a == 3:
print('The value of a is:')
print('a=3')
3 The value of a is: a=3
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"
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¶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:
for a in range(4):
print(a)
0 1 2 3
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
Mehr zu Kontrollfluss: http://docs.python.org/tutorial/controlflow.html
Eine Funktion wird definiert mittels des def
Schlüsselworts:
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:
# type hints
def my_function(arg1: int, arg2: str, arg3: str = 'default value') -> None:
print('arg1 =', arg1)
print('arg2 =', arg2)
print('arg3 =', arg3)
# The 3rd arg may be omitted:
my_function('a string', 12)
arg1 = a string arg2 = 12 arg3 = default value
# 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
Funktionen sind Bürger erster Klasse (first-class citizen). Funktionen können in Variablen gespeichert werden und als Funktionsargumente übergeben werden.
def f(x):
return x ** 2
def operate(h, x): # higher-order function
print(h(x))
operate(f, 5)
25
Mittels des lambda
-Schlüsselworts können anonyme Funktionen erzeugt werden:
g = lambda x: x**3 # assign to the variable g the unnamed function
g(5)
# Note: this is not pep8 conform.
125
1:1: E731 do not assign a lambda expression, use a def
operate(lambda x: x**3, 5)
125
def make_incrementor(n):
return lambda x: x + n
f = make_incrementor(n=3)
f(2)
5
g = make_incrementor(7)
g(5)
12
print(make_incrementor(n=7)(x=8))
print(make_incrementor(7)(8))
15 15
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]
from functools import reduce
print(reduce(lambda x, y: x + y, foo))
print(sum(foo))
print(reduce(lambda x, y: x + y, map(lambda x: x * 2 + 10, foo)))
139 139 368
from operator import add
reduce(add, map(lambda x: x * 2 + 10, foo))
368
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
2
print("expr is of type: ",type(expr))
# split expr on the separator '+'
print(expr.split("+"))
expr.split("+")
# filter with "bool" as operator
print(list(filter(bool, expr.split("+") )))
print(list(map(bool, expr.split("+") )))
# all empty strings are False, not empty strings are True
# cast the string-list elements to int
# with "int" as operator
list(map(int, filter(bool, expr.split("+"))))
# add up all elemets with add-operator
print(reduce(add, map(int, filter(bool, expr.split("+") ))))
expr is of type: <class 'str'> ['28', '32', '', '', '32', '', '39'] ['28', '32', '32', '39'] [True, True, False, False, True, False, True] 131
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/
Ö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):
# 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
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')
import os
cwd = os.getcwd()
type(cwd)
str
os_chdir(path)
Anzeigen der Dateien für eine Verzeichnis: os.listdir(".")
(hier übergeordnetes Verzeichnis des Arbeitverzeichnis)
files = os.listdir("..")
type(files), type(files[0]) # python list of str
(list, str)
os.mkdir(name_of_dir)
z.B. HOME
Umgebungsvariable erhalten:
os.environ['HOME']
'/home/chris'
z.B: User-Id erhalten:
os.getuid()
1001
os.uname()
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')
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
Einfaches Ausführen eines Kommandos in einem Unterprozess subprocess.run(command)
import subprocess
proc = subprocess.run(["ls", "-l"],
universal_newlines=True,
stdout = subprocess.PIPE)
proc_out = proc.stdout.splitlines()
print(proc_out)
['total 184', '-rw-rw-r-- 1 chris chris 69094 Dez 16 16:09 extract_ard_politik_talkshows.ipynb', '-rw-rw-r-- 1 chris chris 0 Mai 6 2021 geckodriver.log', 'drwxr-xr-x 2 chris chris 4096 Mai 24 2020 intro_exercises', '-rw-r--r-- 1 chris chris 116 Mär 10 2021 literatur.txt', '-rwxrw-r-- 1 chris chris 3796 Dez 21 15:12 minesweeper.py', '-rwxrw-r-- 1 chris chris 3882 Dez 16 18:49 minesweeper.py~', '-rw-r--r-- 1 chris chris 659 Mai 7 2021 mymodule.py', 'drwxr-xr-x 3 chris chris 4096 Feb 23 2021 programs', 'drwxr-xr-x 2 chris chris 4096 Mai 7 2021 __pycache__', '-rw-r--r-- 1 chris chris 70790 Jan 4 14:08 Python-Grundlagen.ipynb', '-rw-rw-r-- 1 chris chris 2260 Mai 7 2021 Python-os-Modul.ipynb', '-rw-rw-r-- 1 chris chris 2173 Mai 7 2021 selenium.ipynb', '-rw-rw-r-- 1 chris chris 0 Nov 7 13:42 stderr.txt', '-rw-r--r-- 1 chris chris 201 Jan 31 2021 test.py', '-rw-r--r-- 1 chris chris 200 Jan 31 2021 test.py~']
Auch Pipes etc. möglich.
Mehr siehe https://docs.python.org/3/library/subprocess.html
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
Eine Klasse wird mit dem class
-Schlüsselwort definiert.
self
haben (Referenz auf die laufende Instanz der Klasse).Einige Methoden haben spezielle Bedeutung, z.B.:
__init__
: Konstruktor zum Ereugen einer Instanz.__str__
: Methode, die eine String-Repräsentation der Klasse beinhaltet.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))
# 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]
# 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:
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
# __add__ is the special methods for implementing the +-Operator
# we can dynamically extend the class Point by this method
Point.__add__ = point_add
print(p1 + p2)
Point at [1.250000, 2.500000]
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:
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
# 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
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!
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:
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
Leden Sie den Anfang von https://docs.python.org/3.9/tutorial/modules.html
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:
%%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
# We can import the module `mymodule` into our Python program using `import`:
import mymodule
# Use `help(module)` to get a summary of what the module provides:
help(mymodule)
mymodule.my_variable
'Hello World'
mymodule.my_function()
'Hello World'
my_class = mymodule.MyClass()
my_class.set_variable(10)
my_class.get_variable()
10
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?