NB_02_String_und_Datenstrukturen_Werkzeuge

(c) 2019/2020 Hochschule Augsburg - Fakultät für Informatik - Prof.Dr.Nik Klever

Zeichenketten-Muster Übereinstimmung (String Pattern Matching)

Reguläre Ausdrücke (Regular Expressions)

Das Modul re bietet ein Werkzeug um reguläre Ausdrücke auf Zeichenketten anzuwenden um damit eine erweiterte Verarbeitung zu erreichen. Für das komplexe Matching (die Übereinstimmung mit Suchund die Manipulation von Zeichenketten bieten reguläre Ausdrücke prägnante und optimierte Lösungen:

In [2]:
import re
re.findall(r'\bT[a-z]*', 'Donald, Tick, Trick und Track')
Out[2]:
['Tick', 'Trick', 'Track']
In [3]:
re.sub(r'(\b[a-z]+) \1', r'\1', 'Katze im im Sack')
Out[3]:
'Katze im Sack'

Wenn nur einfache Funktionen benötigt werden, werden in der Regel String-Methoden bevorzugt, weil sie einfacher zu lesen und zu debuggen sind:

In [4]:
'tea for too'.replace('too', 'two')
'tea for two'
Out[4]:
'tea for two'
In [5]:
code = """def listfkt(*arg):
    return arg
def dictfkt(**arg):
    return arg
"""
print(repr(code))
leerzeichen = r" "*2
re.sub(r'^(.*)$', leerzeichen+r'\1', code, 0, re.MULTILINE)
'def listfkt(*arg):\n    return arg\ndef dictfkt(**arg):\n    return arg\n'
Out[5]:
'  def listfkt(*arg):\n      return arg\n  def dictfkt(**arg):\n      return arg\n  '
In [6]:
%%Mooc MoocStringAssessment
Out[6]:

Einrücken von Python-Code

Es ist folgender Code gegeben:

1 code = """def listfkt(*arg):
2     return arg
3 def dictfkt(**arg):
4     return arg
5 """
6 print(repr(code))
7 leerzeichen = r" "*2
8 re.sub(r'^(.*)$', leerzeichen+r'', code, 0, re.MULTILINE)

Verändern sie die Zeile 7 so, dass der Code in der Variablen code um 4 Leerzeichen eingerückt wird



Mathematik

math

Das Modul math bietet Zugriff auf die zugrunde liegenden C-Bibliotheksfunktionen für Gleitkomma-Arithmetik:

In [7]:
import math
math.cos(math.pi / 4)
Out[7]:
0.7071067811865476
In [8]:
math.log(1024, 2)
Out[8]:
10.0

random

Das Modul random bietet Werkzeuge für die Erstellung von zufälligen Auswahl-Listen:

In [9]:
import random
random.choice(['Apfel', 'Pfirsich', 'Banane'])
Out[9]:
'Banane'
In [10]:
random.sample(range(100), 10)   # Auswahl ohne Ersetzung
Out[10]:
[29, 3, 74, 6, 47, 59, 57, 46, 91, 62]
In [11]:
random.random()    # zufälliger Float-Wert
Out[11]:
0.44384155987412977
In [12]:
random.randrange(6)    # zufälliger Integer-Wert aus dem Wertebereich von range(6)
Out[12]:
1

statistics

Das Statistikmodul berechnet grundlegende statistische Eigenschaften (Mittelwert, Median, Varianz usw.) von numerischen Daten:

In [13]:
import statistics
data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
statistics.mean(data)
Out[13]:
1.6071428571428572
In [14]:
statistics.median(data)
Out[14]:
1.25
In [15]:
statistics.variance(data)
Out[15]:
1.3720238095238095

Das SciPy-Projekt enthält viele andere Module für numerische Berechnungen.

In [16]:
%%Mooc Video
Out[16]:

Internet Zugang

Es gibt eine Reihe von Modulen für den Zugriff auf das Internet und die Verarbeitung von Internet-Protokollen. Zwei der einfachsten sind urllib.request für das Abrufen von Daten von URLs sowie da Modul smtplib für das Senden von E-Mails (Man beachte, dass das zweite Beispiel den Zugriff auf einen entsprechenden Mailserver benötigt)

urllib

In [17]:
from urllib.request import urlopen
with urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl') as response:
    for line in response:
        line = line.decode('utf-8')         # Decoding the binary data to text.
        if 'EST' in line or 'EDT' in line:  # look for Eastern Time
            print(line)
<BR>May. 20, 07:58:48 AM EDT		Eastern Time

In [18]:
from urllib.request import urlopen
with urlopen('http://www.ptb.de/cms/de/ptb/fachabteilungen/abt4/fb-44/ag-441/darstellung-der-gesetzlichen-zeit/zeitzonen.html') as response:
    for line in response:
        line = line.decode('utf-8')         # Decoding the binary data to text.
        if 'Land' in line and 'Lokalzeit' in line and 'Sommerzeit' in line:  # Überschrift suchen
            for cell in line.split('<ul>'):
                if 'Deutschland' in cell:
                    print(cell)
<li>Deutschland </li></ul></td><td bgcolor="#ffffcc" valign="top" width="133">UTC + 1 Std.</td><td align="center" bgcolor="#ffffcc" valign="top" width="182">EU</td></tr><tr bgcolor="#ffff99"><td bgcolor="#cccccc" valign="top" width="247">

smtplib

In [19]:
import smtplib
server = smtplib.SMTP('smtp.hs-augsburg.de')
server.sendmail('nik.klever@hs-augsburg.de', 'nik@klever.name',
"""To: nik.klever@hs-augsburg.de
From: doris.rieder@hs-augsburg.de

Bitte noch die Pruefungstermine bekanntgeben.
""")
server.quit()
Out[19]:
(221, b'2.0.0 Bye')
In [20]:
%%Mooc MoocMultipleChoiceAssessment
Out[20]:

Herausfiltern von Text aus einer Webseite

Es ist folgender Code gegeben:

from urllib.request import urlopen
from IPython.display import HTML
ul = "<ul>"
with urlopen('http://python.hs-augsburg.de:8888/notebooks/DuR/SE2016FS02/Skript/chapter.ipynb') as response:
    for line in response:
        line = line.decode('utf-8') # Decoding the binary data to text.
        if 'Abgabetermin' in line:  # Abgabetermin suchen
            ul += re.sub(r'^<li>(<a href="chapter/(.*)/Aufgabe.ipynb">Übungsaufgabe</a>) Abgabetermin="(.*)"</li>$', 
                         r'<li>\1</>',
                         line)
ul += "</ul>
HTML(ul)

Verändern sie den Code nun so, dass die folgende Ausgabe als Ergebnis ausgegeben wird:


  • Wiederholung Informatik 1 - 2017-04-05
  • Module - 2017-04-19
  • Fehler und Ausnahmen - 2017-05-03
  • Iteratoren und Generatoren - 2017-05-17
  • Standard Bibliothek 1 - 2017-06-01

Hinweis: Geben sie erstmal den obigen Code in einer Notebook Zelle ein und schauen sie sich auch mal die Variable ul jeweils genauer an

Verändern sie das Argument repl (das 2. Argument) in der Funktion sub des regulären Ausdrucks so, dass die obige Ausgabe erhalten wird

r'<li>\1</li>'
r'<li>\2</li>'
r'<li>\2 - \3</li>'
r'<li>\1  - </li>'

Datum und Zeit

Das Modul datetime enthält Klassen zur Manipulation von Datum und Uhrzeit sowohl auf einfache als auch komplexe Weise. Es wird sowohl die Datums- und Uhrzeit-Arithmetik unterstützt als auch eine effiziente Extraktion aller Datums- und Zeitelement für die Ausgabeformatierung und -manipulation. Das Modul unterstützt auch Zeitzonen.

Datum- und Zeitangaben sind leicht erstellt und formatiert

In [21]:
from datetime import date
now = date.today()
now
Out[21]:
datetime.date(2017, 5, 20)
In [22]:
now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.")
Out[22]:
'05-20-17. 20 May 2017 is a Saturday on the 20 day of May.'

Datum- und Zeitangaben unterstützen die Kalender-Arithmetik

In [23]:
birthday = date(1964, 7, 31)
age = now - birthday
age.days
Out[23]:
19286

Datenkompression

Gebräuchliche Datenarchivierung und Komprimierungsformate werden direkt von einigen Modulen unterstützt: zlib, gzip, bz2, lzma, zipfile und tarfile.

zlib

In [24]:
import zlib
s = b"Fischer's Fritze fischt frische Fische, Frische Fische fischt Fischer's Fritze"
len(s)
Out[24]:
78
In [25]:
t = zlib.compress(s)
len(t)
Out[25]:
47
In [26]:
zlib.decompress(t)
Out[26]:
b"Fischer's Fritze fischt frische Fische, Frische Fische fischt Fischer's Fritze"
In [27]:
zlib.crc32(s)
Out[27]:
488700465
In [28]:
%%Mooc MoocCheckboxesAssessment
Out[28]:

tarfile

Mit dem folgenden Code wird ein tar-geziptes Archiv des aktuellen Ordners erstellt:

import tarfile
import glob
with tarfile.open("beispiel.tgz", "w:gz") as tar:
    for name in glob.glob("*"):
        tar.add(name)

Mit dem folgenden Code wird das obige Archiv ausgepackt und mittels des Generator filetypes werden nur die Dateien mit der filetype-Endung (.py) herausgefiltert:


def filetypes(members):
    for tarinfo in members:
        if tarinfo.name.endswith(".py"):
            yield tarinfo

with tarfile.open("beispiel.tgz", "r:gz") as tar:
    for f in filetypes(tar):
        print(f.name)

Statt der for-Schleife könnten mit dem Aufruf tar.extractall(members=filestype(tar)) alle entsprechenden Dateien aus dem Archiv ausgepackt werden.

Welche Antworten sind notwendig, damit alle möglichen Filetypen aus dem Archiv herausgefiltert werden können ?
Geben sie die beste Antwort an, da einige Antworten zwar möglich aber teilweise unnötig sind

def filetypes(members):
def filetypes(filetype,members):
def filetypes(members,filetype='.py'):
  for tarinfo in members:
    if tarinfo.name.endswith('.py'):
    if tarinfo.name.endswith(['.py','.ipynb']):
    if tarinfo.name.endswith(filetype):
      yield tarinfo
      yield tarinfo, ['.py','.ipynb']
      yield tarinfo, filetype
with tarfile.open('beispiel.tgz', 'r:gz') as tar:
  for f in filetypes(tar):
  for f in filetypes('.ipynb',tar):
  for f in filetypes(tar,'.ipynb'):
    print(f.name)
In [29]:
%%Mooc Video
Out[29]:

Leistungsmessung

Einige Python-Anwender haben ein spezielles Interesse daran, die relative Leistung verschiedener Lösungsansätze für das gleiche Problem zu kennen. Python stellt hierfür ein Messinstrument zur Verfügung, das diese Fragen sofort beantworten kann.

timeit

Zum Beispiel ist es sinnvoll, den Ansatz Tupel zu verwenden und anschliessend wieder auszupacken mit dem traditionellen Ansatz zum Austauschen von Argumenten zu vergleichen. Das Modul timeit zeigt schnell einen geringen Leistungsvorteil des Tupelansatzes:

In [30]:
from timeit import Timer
Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
Out[30]:
0.04813456899137236
In [31]:
Timer('a,b = b,a', 'a=1; b=2').timeit()
Out[31]:
0.03537327898084186

profile und pstats

Im Gegensatz zu der genauen Granularität von timeit bieten die Module profile und pstats Werkzeuge zur Ermittlung zeitkritischer Abschnitte in größeren Codeblöcken.

Qualitätskontrolle

Ein Ansatz für die Entwicklung qualitativ hochwertiger Software ist es, Tests für jede Funktion zu schreiben, sobald sie entwickelt worden sind, und diese Tests immer wieder während des gesamten Entwicklungsprozesses durchzuführen.

doctest

Das Modul doctest bietet ein Tool zum Scannen eines Moduls und zur Validierung von Tests, die in den docstrings eines Programm-Codes eingebettet sind. Der Testaufbau ist so einfach wie Cut-and-Paste, also das Ausschneiden eines typischen Aufrufs und anschliessend Wiedereinfügen einschliesslich dessen Ergebnisse in den docstring. Dies verbessert die Dokumentation insgesamt, indem sie dem Benutzer sowohl ein Beispiel aufzeigt als auch dem Modul doctest ermöglicht, den Code zu überprüfen und damit sicherzustellen, dass der Code der Dokumentation treu bleibt:

In [32]:
def durchschnitt(values):
    """Berechnet das arithmetische Mittel einer Liste von Zahlen.
    
    >>> durchschnitt([20, 30, 70])
    40.0
    """
    return sum(values) / len(values)

import doctest
doctest.testmod()   # hiermit werden automatisch alle eingebetteten Tests validiert
Out[32]:
TestResults(failed=0, attempted=1)
In [33]:
%%Mooc MoocStringAssessment
Out[33]:

doctest

Es ist folgender Code gegeben:

def sumgerade(values):
    """Berechnet die Summe aller geraden Zahlen in einer Liste von Zahlen.
    >>> sumgerade([1, 3, 7, 9, 10, 15, 30, 50])
    """
    return sum(x for x in values if x%2 == 0)

import doctest
doctest.testmod(verbose=True)   # hiermit werden automatisch alle eingebetteten Tests validiert

Verändern sie den obigen Code so, dass der doctest-Aufruf korrekt abläuft und keine Fehlermeldung mehr kommt.
Geben sie daher als Antwort das korrekte Ergebnis ein, welches vor der Zeile 3 stehen muss



unittest

Das Modul unittest ist nicht so einfach wie das Modul doctest, aber es erlaubt den Einsatz und die Pflege einer umfangreicheren Menge von Tests in einer separaten Datei:

In [34]:
%%writefile test_statistik.py
import unittest

def durchschnitt(values):
    """Berechnet das arithmetische Mittel einer Liste von Zahlen.
    
    >>> print(durchschnitt([20, 30, 70]))
    40.0
    """
    return sum(values) / len(values)

class TestStatistischeFunktionen(unittest.TestCase):

    def test_durchschnitt(self):
        self.assertEqual(durchschnitt([20, 30, 70]), 40.0)

    def test_durchschnitt_gerundet(self):
        self.assertEqual(round(durchschnitt([1, 5, 7]), 1), 4.3)
        
    def test_durchschnitt_nulldivision(self):
        with self.assertRaises(ZeroDivisionError):
            durchschnitt([])
            
    def test_durchschnitt_typeerror(self):
        with self.assertRaises(TypeError):
            durchschnitt(20, 30, 70)

if __name__ == '__main__':
    unittest.main()  # Das Aufrufen von der Befehlszeile ruft alle Tests auf
Overwriting test_statistik.py
In [35]:
!python test_statistik.py -v
test_durchschnitt (__main__.TestStatistischeFunktionen) ... ok
test_durchschnitt_gerundet (__main__.TestStatistischeFunktionen) ... ok
test_durchschnitt_nulldivision (__main__.TestStatistischeFunktionen) ... ok
test_durchschnitt_typeerror (__main__.TestStatistischeFunktionen) ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK

Batterien enthalten

Python hat die Philosophie, dass die Batterien - wie bei technischen Spielzeugen - im Gesamtpaket enthalten sind. Dies ist am besten erkennbar durch den äusserst anspruchsvollen Umfang und die Robustheit seiner größeren Pakete. Beispielsweise:

  • Die Module xmlrpc.client und xmlrpc.server machen die Implementierung von Remote-Procedure-Aufrufen zu einer fast trivialen Aufgabe. Trotz der einschlägigen Modulnamen ist kein direktes Wissen von XML erforderlich.
  • Das Modul email ist eine Bibliothek zum Verwalten von E-Mail-Nachrichten, einschließlich MIME und anderen RFC 2822-basierten Nachrichtendokumenten. Im Gegensatz zu den Modulen smtplib und poplib, die tatsächlich Nachrichten senden und empfangen, verfügt das Modul email über ein komplettes Toolset zum Erstellen und Decodieren komplexer Nachrichtenstrukturen (einschließlich der Anhänge) und zur Implementierung der Text-De- und En-Codierung sowie Headerprotokollen.
  • Das Modul json bietet eine robuste Unterstützung für das Analysieren dieses populären Datenaustauschformats.
  • Das Modul csv unterstützt das direkte Lesen und Schreiben von Dateien im Comma-Separated Value-Format, die häufig von Datenbanken und Tabellenkalkulationen unterstützt werden.
  • Die Verarbeitung von XML wird von den Paketen xml.etree.ElementTree, xml.dom und xml.sax unterstützt. Gemeinsam vereinfachen diese Module und Pakete den Datenaustausch zwischen Python-Anwendungen und anderen Tools erheblich.
  • Das Modul sqlite3 ist ein Wrapper für die SQLite-Datenbankbibliothek und stellt eine persistente Datenbank bereit, die aktualisiert und mit einer einfachen, jedoch nicht standardmäßigen SQL-Syntax aufgerufen werden kann.
  • Die Internationalisierung wird durch eine Reihe von Modulen wie gettext, locale und codecs unterstützt.
In [36]:
%%Mooc Video
Out[36]:

Weitere Literatur

In [37]:
%%Mooc WebReference

Brief Tour of the Standard Library

https://docs.python.org/3/tutorial/stdlib.html

Hinweis: Kurze Auflistung einige Module aus der Standardbibliothek

In [38]:
%%Mooc WebReference

re — Regular expression operations

https://docs.python.org/3/library/re.html

Hinweis: re — Methoden und Operationen für Reguläre Ausdrücke

In [39]:
%%Mooc WebReference

urllib.request — Extensible library for opening URLs

https://docs.python.org/3/library/urllib.request.html

Hinweis: urllib.request — Erweiterte Bibliothek zum Öffnen und Herunterladen von URLs (Webseiten)

In [40]:
%%Mooc WebReference

doctest — Test interactive Python examples

https://docs.python.org/3/library/doctest.html

Hinweis: doctest - Test interaktiver Python Beispiele