Die Programmiersprache Python


Index

  1. Charakterisierung und Historie
  2. Einsatzgebiete
  3. Eigenschaften und Merkmale von Python
  4. Die wichtigsten Daten-/Objekt-Typen
  5. Variablen
  6. Eine Auswahl von Konzepten und Besonderheiten
  7. In medias res - praktischer Einstieg an Beispielen
  8. Namensräume und Sichtbarkeitsbereiche
  9. Funktionen
  10. Objekt-Orientierung
  11. Sammlung von Code-Beispielen
  12. Links und Literatur zu Python

1. Charakterisierung und Historie


2. Einsatzgebiete


3. Eigenschaften und Merkmale von Python


4. Die wichtigsten Daten-/Objekt-Typen


5. Variablen


6. Eine Auswahl von Konzepten und Besonderheiten


7. In medias res - praktischer Einstieg an Beispielen

Index:

  1. Hello World
  2. Programmierparadigmen
  3. Zugriff auf Argumenteliste und Umgebung
  4. Rechnen mit Python
  5. Strings
  6. Indexierung und Slicing bei Sequenzen
  7. Steuerfluss
  8. Reguläre Ausdrücke
  9. Ausnahme-Behandlung
  10. Tupel, Listen, Dictionaries, Mengen
  11. Funktionen
  12. Ein-/Ausgabe
  13. Ein-/Ausgabe 2 - Messwertprotokoll
  14. Start externer Kommandos
  15. Funktionale Programmierung
  16. OOP - Einstiegsbeispiel
  17. OOP - Klassen- und Instanzvariablen
  18. OOP - Polymorphie und Vererbung
  19. OOP - statische und Klassenmethoden
  20. OOP - Aufruf von Methoden der Basisklasse und MRO

1. Hello World

  #!/bin/env python3
  # -*- coding: utf8 -*-
  
  """
  Hello World: als Modul oder Programm nutzbar
  """
  
  # Definition der parameterlosen Funktion "hello"
  def hello():
      """Funktion hello(): Ausgabe des Strings 'hello world'"""
      print('hello world')
  
  if __name__ == '__main__':
      # die Funktion "hello" ausführen, da der vorliegende Code als Programm
      # gestartet und nicht als Modul importiert wurde
      hello()

Nutzung des Moduls helloworld.py:

  #!/bin/env python3
  # -*- coding: utf8 -*-
  
  import helloworld
  
  # die Funktion "hello" des Moduls "helloworld" aufrufen
  helloworld.hello()
  
  # den DOC-String von "hello" ausgeben
  print(helloworld.hello.__doc__)
  
  # Funktion help nutzen
  help(helloworld)

2. Programmierparadigmen

  #!/usr/bin/env python
  # -*- coding: utf8 -*-
  #
  # Demonstration von Programmier-Paradigmen
  
  import sys
  
  # Python 2 oder 3?
  python2 = sys.version_info[0] == 2
  
  if python2:
      # das range() von Python 3 heißt bei Python 2 xrange()
      range = xrange
  else:
      # ab Python 3 die Funktion filter() so redefinieren, dass sie immer eine
      # Liste statt eines Filter-Objekts liefert
      def filter(kriterium, sequenz):
          return list(__builtins__.filter(kriterium, sequenz))
  
  # ========================================
  # OOP - Objektorientierte Programmierung
  # ========================================
  
  print('OOP')
  
  # Filterkriterium: True bei positiven Werten, sonst False
  def positiv(x):
      return x > 0
  
  # Klasse für Sequenzen mit Filter-Funktion
  class filter_sequenz(object):
      def __init__(self, sequenz):
          self.sequenz = sequenz
  
      def filter(self, kriterium):
          # Nutzung der eingebauten Standard-Funktion filter();
          # Rekursion unterbleibt, da die Objektmethode filter()
          # mit self.filter() angesprochen werden muss
          return filter(kriterium, self.sequenz)
  
  # Objekt der Klasse filter_sequenz instanziieren
  fs = filter_sequenz(range(-5, 5))
  
  # für das Objekt die Methode filter aufrufen
  print(fs.filter(positiv))
  
  print('------------')
  
  # ========================================
  # imperative/prozedurale Programmierung
  # ========================================
  
  print('imperativ/prozedural')
  
  # imperativ programmierte Filterfunktion
  def p_filter(kriterium, sequenz):
      result = []
      for elem in sequenz:
          if kriterium(elem):
              result.append(elem)
      return result
  
  # Anwendung der Filterfunktion auf eine Liste
  print(p_filter(positiv, range(-5, 5)))
  
  print('------------')
  
  # ========================================
  # funktionale (deklarative) Programmierung
  # ========================================
  
  print('funktional')
  
  # Nutzung der eingebauten Funktion filter
  print(filter(positiv, range(-5, 5)))
  
  # dito, aber Nutzung einer anonymen Lambda-Funktion als Kriterium
  print(filter(lambda x: x > 0, range(-5, 5)))
  
  # List Comprehension: Listengenerierung durch funktionalen Ausdruck
  print([2 * x for x in range(-5, 5) if x > 0])
  
  print('------------')
  
  # bei großen Listen empfehlen sich Generatorausdrücke statt List Comprehensions;
  # Generator-Objekt über einen Generatorausdruck erzeugen:
  gen = (2 * x for x in range(-100, 100) if x > 0)
  print(gen)
  
  # durch Iteration über dem Generator die Elemente ausgeben, die kleiner 21 sind
  for x in gen:
      if x > 20:
          break
      print(x)
  
  print('------------')
  
  # den Generator kann man auch ohne Generatorausdruck erstellen:
  def mygen(von, bis):
      x = von if von > 1 else 1
      while x < bis:
          yield 2 * x
          x += 1
          print('back in generator; new x = %s' % x)
  
  # Nutzung des eigenen Generators
  for x in mygen(-100, 100):
      if x > 20:
          break
      print(x)
  
  print('------------')
  
  # ========================================
  # AOP - Aspektorientierte Programmierung
  # ========================================
  
  print('AOP')
  
  # ein Logger-Objekt als Wrapper zur Ergänzung des Aspekts (der
  # Querschnittsaufgabe) "Logging"
  class logger(object):
      def __init__(self, func):
          # Zähler für Anzahl der Funktionsaufrufe nullen
          self.calls = 0
          # die Original-Funktion sowie deren Namen und Doc-String merken
          self.func = func
          self.__name__ = func.__name__
          self.__doc__ = func.__doc__
  
      def __call__(self, *args):
          # Aufruf-Zähler inkrementieren
          self.calls += 1
          # Logging durchführen
          print('call %s to %s' % (self.calls, self.func.__name__))
          # Aufruf der Original-Funktion
          return self.func(*args)
  
  # die Funktion "myfunc" dekorieren und diese so mit dem Wrapper umgeben
  @logger
  def myfunc(*args):
      'das ist myfunc'
      print(args)
      return len(args)
  
  # zeigen, dass der Wrapper den Namen und den Doc-String von myfunc erhält
  print myfunc.__name__
  print myfunc.__doc__
  
  # indirekter Ruf der Funktion "myfunc" über den Logger als Wrapper; dessen
  # Funktion __call__ wird aufgerufen; sie fügt ihre Logging-Logik hinzu und ruft
  # dann die Original-Funktion "myfunc"
  print myfunc(1, 3, 7, 9)
  print myfunc(*(range(-5, 5)))
  print myfunc('x', 'y', 'z')
  print myfunc(True, False, None)

3. Zugriff auf Argumenteliste und Umgebung

  #!/usr/bin/env python
  # -*- coding: utf8 -*-
  
  """
  Ausgabe der Argumente-Liste und der Umgebung
  """
  
  import sys, os
  
  # sys.argv ist eine Liste
  print 'Argument-Liste: %s\n' % sys.argv
  
  # os.environ ist ein Dictionary
  # Keys  --> Namen der Umgebungsvariablen
  # Werte --> Inhalte der Umgebungsvariablen
  
  # max. Länge der Keys ermitteln; wir übergeben dazu einen durch einen
  # Generator-Ausdruck erzeugten Generator an die eingebaute Funktion max()
  max_len = max(len(x) for x in os.environ)
  
  # formatierte Ausgabe des Dictionarys
  print 'Umgebung:'              
  for key, value in os.environ.items():
    # bei Python 2 existiert für die Iteration noch os.environ.iteritems(); bei
    # Python 3 wird nur noch os.environ.items() unterstützt, das aber im Gegensatz
    # zu Python 2 keine Liste mehr liefert, sondern ein iterierbares View-Objekt
    print '  %-*s --> %s' % (max_len, key, value)

4. Rechnen mit Python

  #!/bin/env python3
  
  # Rechnen mit Python
  
  # die Namen des aktuellen Sichtbarkeitsbereichs ausgeben
  print(dir(), end='\n\n')
  
  # die Module math, decimal und cmath für mathematische Funktionen, Dezimal- und
  # komplexe Zahlen importieren
  import math
  import decimal
  import cmath
  
  # aus dem Modul fractions importieren wir die Klasse Fraction
  from fractions import Fraction
  
  print(dir(), end='\n\n')
  
  # die Namen des Moduls math ausgeben
  print(dir(math), end='\n\n')
  
  print(math.__doc__, end='\n\n')
  # This module is always available.  It provides access to the
  # mathematical functions defined by the C standard.
  
  print(math.pi, math.e) # 3.141592653589793 2.718281828459045
  math.pi = 3.2 # circa :-)
  print(math.pi)
  
  print(math.log(math.e))
  
  # 2 Float-Zahlen aus Strings erstellen
  netto = float('17.2345')
  mwst = float('10')
  brutto = netto * (1 + mwst / 100)
  
  print('\nBrutto:', brutto)                                 # 18.957950000000004
  print('Brutto mit 2 Stellen: %.2f' % brutto)               # 18.96
  print('Brutto gerundet auf 3 Stellen: ', round(brutto, 3)) # 18.958
  print()
  
  # 0o11   --> Oktalzahl
  # 0x0a   --> Hexadezimalzahl (auch 0xa möglich); wahlweise Groß-/Kleinschreibung
  #            bei Präfix und Hexadezimalziffern
  # 1.8E5  --> float-Zahl in Exponentenschreibweise; e kann auch klein geschrieben werden
  print((1 + 0o11) * 0x0a, 1.35, 1.8E5, (3 + 1j) * 3, 3 ** 4)
  
  # Ganzzahlen vom Typ "int" unterstützen Langzahlarithmetik
  print(2 ** 1024)
  
  print(0b11, 0B11)  # Binärzahl 11 ==> 3
  print(0o72, 0O72)  # Oktalzahl 72 ==> 58
  print(bin(10))     # ==> '0b1010'
  
  print('\nkomplexe Zahlen')
  c = 3 + 4j # oder: 3 + 4J
  # kartesische Koordinaten (Real- und Imaginärteil)
  print(c.real, c.imag)
  
  # Polarkoordinaten
  print(cmath.polar(c))
  # (5.0, 0.9272952180016122)
  
  # alternative Bestimmung der Polarkoordinaten
  print(abs(c), cmath.phase(c))
  
  # Umwandlung der Polarform in die algebraische (kartesische) Form
  print(cmath.rect(*cmath.polar(c)))
  print(cmath.rect(cmath.polar(c)[0], cmath.polar(c)[1]))
  
  print('\nDivision')
  # // ==> ganzzahlige Division
  # %  ==> Rest der Division (Modulo-Operator)
  print(1/3, 1 // 3, 7 % 3)
  print()
  
  a = 12.1
  b = 2
  c = a / b
  print(c)       # 6.05
  print(str(c))  # 6.05
  print(repr(c)) # 6.0499999999999998 bis Python 2.6 und 6.05 ab Python 2.7
                 # repr() wird im interaktiven Modus benutzt
  
  print('\nDecimal')
  a = decimal.Decimal(str(a))
  b = decimal.Decimal(str(b))
  c = a / b
  print(repr(c))         # Decimal("6.05")
  print(c, end = '\n\n') # 6.05
  
  # Rundungsungenauigkeit bei float
  z = '0.33333333333333333333'
  print(decimal.Decimal(z) * 3) # Decimal('0.99999999999999999999')
  print(float(z) * 3)           # 1.0
  
  print(0.1 + 0.2 == 0.3) # False
  print(decimal.Decimal('0.1') + decimal.Decimal('0.2') == decimal.Decimal('0.3')) # True
  
  # die Operationen mit Decimals kann man ziemlich fein über den Context steuern;
  # hier ein Beispiel:
  
  # den Default-Kontext auslesen
  default_context = decimal.getcontext()
  # einen neuen Kontext einstellen, der eine abweichende Präzision aufweist
  decimal.setcontext(decimal.Context(prec = 5))
  
  # in einer Schleife jeweils ein Zahlenpaar addieren
  for z1, z2 in ('1.11111111', '2.22222222'), ('3.33333333', '4.44444444'):
      a = decimal.Decimal(z1)
      b = decimal.Decimal(z2)
      print(a)
      print(b)
      print(a + b)
      print('gerundet' if decimal.getcontext().flags[decimal.Rounded] else 'exakt')
      # den Default-Kontext wieder aktivieren
      decimal.setcontext(default_context)
  
  # Ausgabe:
  #   1.11111111
  #   2.22222222
  #   3.3333
  #   gerundet
  #   3.33333333
  #   4.44444444
  #   7.77777777
  #   exakt
  
  # mit with kann man die Präzision bequem auf einen Anweisungsblock begrenzen
  print('\nwith')
  with decimal.localcontext(decimal.Context(prec = 5)):
      print(decimal.Decimal('1') / decimal.Decimal('3')) # 0.33333
  
  print(decimal.Decimal('1') / decimal.Decimal('3'))   # 0.3333333333333333333333333333
  
  # rationale Zahlen (Brüche)
  print('\nFraction')
  f1 = Fraction(6, 8)                       # Fraction(3, 4)
  f2 = 2 * f1                               # Fraction(3, 2)
  print('%s + %s = %s' % (f1, f2, f1 + f2)) # Fraction(9, 4)
  # 3/4 + 3/2 = 9/4

5. Strings

  #!/usr/bin/env python
  # -*- coding: utf8 -*-
  
  """
  Python2-Strings
  
  Achtung: Strings bei Python 3 sind standardmäßig Unicode-Strings und keine
  Bytefolgen. Letztere muss man explizit kennzeichnen: b'Hallo'
  """
  
  import sys, codecs, os
  
  io_encoding = 'PYTHONIOENCODING' in os.environ
    # Wir vermerken, ob die Umgebungsvariable PYTHONIOENCODING gesetzt ist.
    #
    # Das Manual von Python 2.6 sagt zu PYTHONIOENCODING:
    #   Overrides the encoding used for stdin/stdout/stderr, in the syntax
    #   encodingname:errorhandler. The :errorhandler part is optional and has the
    #   same meaning as in str.encode().
    #
    #      New in version 2.6.
    #
    #   The encoding used for standard input, output, and standard error can be
    #   specified by setting the PYTHONIOENCODING environment variable before
    #   running the interpreter. The value should be a string in the form
    #   <encoding> or <encoding>:<errorhandler>. The encoding part specifies the
    #   encoding’s name, e.g. utf-8 or latin-1; the optional errorhandler part
    #   specifies what to do with characters that can’t be handled by the
    #   encoding, and should be one of “error”, “ignore”, or “replace”.
    #   (Contributed by Martin von Loewis.)
    #
    # Das Manual von Python 3 sagt zu PYTHONIOENCODING:
    #   If this is set before running the interpreter, it overrides the encoding
    #   used for stdin/stdout/stderr, in the syntax encodingname:errorhandler.
    #   The :errorhandler part is optional and has the same meaning as in
    #   str.encode().
    #
    #   For stderr, the :errorhandler part is ignored; the handler will always be
    #   'backslashreplace'.
    #
    #   Changed in version 3.4: The encodingname part is now optional.
    #
    # Das Setzen von PYTHONIOENCODING ist z.B. dann wichtig, wenn die Ausgabe
    # eines Python-Programms in eine Pipe oder eine Datei umgelenkt wird. Ggf.
    # kommt es dabei zu einem Fehler, der nicht auftritt, wenn die Ausgabe auf
    # ein Terminal erfolgt:
    #
    # * Fehlerfreie Ausgabe auf ein UTF8-fähiges Terminal:
    #
    #     python -c $'# -*- coding: utf8 -*-\nprint u"äöüß"'
    #
    # * Fehler bei Ausgabeumlenkung in eine Datei oder Pipe:
    #
    #     python -c $'# -*- coding: utf8 -*-\nprint u"äöüß"' > out
    #       Traceback (most recent call last):
    #         File "<string>", line 2, in <module>
    #       UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-7: ordinal not in range(128)
    #
    #     python -c $'# -*- coding: utf8 -*-\nprint u"äöüß"' | wc -l
    #       Traceback (most recent call last):
    #         File "<string>", line 2, in <module>
    #       UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-7: ordinal not in range(128)
    #       0
    #
    # * Fehlerfreie Ausgabeumlenkung in eine Datei oder Pipe:
    #
    #     PYTHONIOENCODING=utf8 python -c $'# -*- coding: utf8 -*-\nprint u"äöüß"' > out2
    #     PYTHONIOENCODING=utf8 python -c $'# -*- coding: utf8 -*-\nprint u"äöüß"' | wc -l
  
  # Apostroph und Doppel-Apostroph als gleichwertige String-Begrenzer
  
  # hintereinander notierte String-Literale werden zu einem String verkettet
  s1 = 'abc"\n' "xyz"   # entspricht: s = 'abc"\nxyz'
  s1 = 'abc"\n'\
  "xyz"                 # dito
  print(
        'aa'
        'bb'
       )                # entspricht: print 'aabb'
  
  s2 = "a'b"
  raw1 = r'\d+'   # entspricht: '\\d+'
  raw2 = r'\d+\\' # entspricht: '\\d+\\\\'
  
  # Achtung: einzelner Backslash darf bei Raw Strings nicht als letztes Zeichen
  # stehen:
  #   r'\a\' ist also unzulässig
  
  print s1, s2, raw1, raw2
  print ('Hallo' + ' Welt ') * 3
  
  # Strings können mehrzeilig sein
  print '''
    ich
    bin
    mehrzeilig
  '''
  
  print """
  ich
  auch
  """
  
  # Zeichenketten kann man zeichenweise durchlaufen (Sequenz)
  print
  for char in 'Python':
      print char
  
  # ord()    --> ASCII- bzw. Unicode-Code eines Zeichens bzw. Unicode-Zeichens
  #              ermitteln
  # chr()    --> Zeichen mit angegebenem ASCII-Code generieren
  # unichr() --> Unicode-Zeichen mit angegebenem Code generieren
  print
  p = 'Python' + '%c%c.%c' % (chr(32), chr(0x32), chr(ord('5') + 1))
  print p
  
  # weitere Beispiele zur String-Formatierung
  print 'Inhalt von Variable p = %(p)s' % vars()
  d = {
    'tuple'     : 'Tupel',
    'list'      : 'Liste',
    'dictionary': 'Wörterbuch',
    'set'       : 'Menge'
  }
  print 'deutsch: tuple --> %(tuple)s, list --> %(list)s, set --> %(set)s' % d
  
  print '%-10s%20s%5d%10.2f' % ('Hans', 'Moser', 70, 12314.21)
  
  # ab Python 2.6: neben % ist auch String-Methode format() nutzbar
  print '{0} --> {1}'.format('car', 'Auto', 'Moped')
  
  # ab Python 2.7: automatische Feldnummerierung, wenn alle Positionsargumente fehlen
  print '{} --> {}'.format('car', 'Auto', 'Moped')
  
  # eine Mischung beider Nummerierungsformen ist unzulässig
  try:
      print '{0} --> {}'.format('car', 'Auto', 'Moped')
  except Exception as e:
      print repr(e)
      # ==> ValueError('cannot switch from manual field specification to automatic field numbering',)
  
  print 'Ein {was} {tut} {wie}.'.format(was = 'Hawazuzi', tut = 'fährt', wie = 'langsam')
  print 'Ein {was} {tut} {wie}.'.format(was = 'Flugzeug', tut = 'fliegt', wie = 'schnell')
  
  print 'Tupel: {team1[2]},{team2[1]}'.format(team1 = [1, 2, 3], team2 = [4, 5, 6])
  print 'Dictionary: {{{team1[0]}:{team2[2]}}}'.format(team1 = [1, 2, 3], team2 = [4, 5, 6])
  
  for ueb, seite in ('Mathe', 1), ('Physik', 12), ('Chemie', 34):
      # .<40 ==> linksbündig in Länge 40 mit Füllzeichen .
      # >2d  ==> rechtsbündig in Länge 2 als ganze Dezimalzahl
      print '{ueb:.<40} Seite {seite:>2d}'.format(ueb = ueb, seite = seite)
  
  # Unicode-Zeichen ä; dessen Ausgabe auf ein UTF-8-Terminal ist kein Problem,
  # die Umlenkung der Ausgabe in eine Datei oder Pipe dagegen schon:
  #
  #   Traceback (most recent call last):
  #     File "strings.py", line 94, in <module>
  #       print unichr(228), ord(u'ä')
  #   UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 0: ordinal not in range(128)
  #
  # daher also try/except und explizite Kodierung als UTF-8 bei except
  print
  try:
      print unichr(228), ord(u'ä')
  except:
      print ('%s %s' % (unichr(228), ord(u'ä'))).encode('utf8')
      # wie oben beschrieben, kann man durch die Umgebungsvariable PYTHONIOENCODING
      # von außen die Standard-Kodierung einstellen, so dass sich auch
      # Nicht-ASCII-Zeichen ohne explizite Kodierung ausgeben lassen, z.B.
      #
      #   PYTHONIOENCODING=utf8 python -c $'# -*- coding: utf8 -*-\nprint u"äöüß"' | wc -l
  
  # statt try/except zu nutzen, können wir auch die Einstellung io_encoding
  # testen:
  if io_encoding:
      print unichr(228), ord(u'ä')
  else:
      print ('%s %s' % (unichr(228), ord(u'ä'))).encode('utf8')
  
  s = 'Guido van Rossum'.upper()
  print '%s, %s' % (s[-10:], s[:5])
  
  # Strings sind unveränderliche Objekte
  #
  # >>> a = 'aaa'
  # >>> a[1] = 'b'
  # Traceback (most recent call last):
  #   File "<stdin>", line 1, in ?
  # TypeError: object does not support item assignment
  
  # Anwendung von String-Funktionen (Methoden von String-Objekten)
  s = s[:5] + ' ' + s[-6:]
  print s.replace(' ROSSUM', '')
  print s
  
  print
  for g in 'Guido van Rossum', 'Guido Westerwelle', 'Heinz':
      if g.endswith('Rossum') and g.startswith('Guido'):
          print g, '--> BDFL'
      elif 'Guido' in g:
          print g, '--> any other Guido'
      else:
          print g, '--> ???'
  
  s = 'Guido van Rossum'
  print
  print '/'.join(s.lower().split())
  
  # Unicode-Strings
  unicode_str = u'äöü\N{Copyright Sign}'
  print 'latin1', unicode_str.encode('latin1')
  print >>sys.stderr, 'utf8', unicode_str.encode('utf8')
  
  # die Original-Standardausgabe sichern
  orig_stdout = sys.stdout
  
  # den StreamWriter des UTF8-Codecs auf stdout anwenden:
  #   >>> c = codecs.lookup('utf8')
  #   >>> c
  #   <codecs.CodecInfo object for encoding utf-8 at 0x98151ac>
  #   >>> list(c)
  #   [<built-in function utf_8_encode>, <function decode at 0xb7c22bec>, <class
  #   'encodings.utf_8.StreamReader'>, <class 'encodings.utf_8.StreamWriter'>]
  sys.stdout = codecs.lookup('utf8')[-1](orig_stdout)
  
  # den Unicode-String ausgeben (hier wegen des UTF8-Codecs auch ohne
  # PYTHONIOENCODING möglich)
  print 'utf8 via codecs', unicode_str
  
  # alternativ mit getwriter()
  # s.a. http://www.doughellmann.com/PyMOTW/codecs/#standard-input-and-output-streams
  sys.stdout = codecs.getwriter('UTF-8')(orig_stdout)
  print 'utf8 via codecs', unicode_str
  
  # die Original-Standardausgabe wiederherstellen
  sys.stdout = orig_stdout
  
  print
  
  # Einlesen und Auswerten einer Textdatei mit Zeilen der Art
  #
  # eins|zwei|drei|3|4
  # EINS|ZWEI|DREI|8|9
  
  # die Datei zeilenweise einlesen
  for line in open('string_input.txt'):
      # die Zeile strippen (Whitespace vorn und hinten abschneiden) und am Zeichen
      # '|' in Felder zerlegen
      fields = line.strip().split('|')
  
      # die Werte des vorletzten und letzten Feldes in Zahlen umwandeln, addieren
      # und die Summe in einen String konvertieren, damit das join() unten
      # funktioniert
      summe = str(int(fields[-2]) + int(fields[-1]))
  
      # alle Felder bis auf die letzten beiden sowie die errechnete Summe zu einer
      # Liste zusammenfügen und daraus einen String mit Slash als Feldtrenner
      # erzeugen
      print '/'.join(fields[:-2] + [summe])
  
  # die Datei einlesen und als Zeilenliste bereitstellen
  lines = open('string_input.txt').readlines()
  
  # die Zeilen inkl. Index durchlaufen und den Zeileninhalt in Kleinbuchstaben
  # konvertieren
  for idx, line in enumerate(lines):
      lines[idx] = line.lower()
  
  # die Liste in eine Datei schreiben
  open('string_out', 'w').write(''.join(lines))
  
  # Test, ob die Zeichenkette 'drei' in der Datei vorkommt
  print 'drei' in open('string_input.txt').read()
  
  # Textzeilen nach einer bestimmten Spalte lexikografisch und numerisch
  # sortieren; Spaltentrenner ist das Pipe-Zeichen |
  zeilen = '''BC1|222|ZB
  BC4|9|Magazin
  BC2|471|CBI
  BC3|66|CBII'''
  
  # den mehrzeiligen String in eine Zeilenliste zerlegen
  zeilen_liste = zeilen.splitlines()
  
  # lexikografisch sortierte Ausgabe; als Parameter key von sorted() nutzen wir
  # eine anonyme Lambda-Funktion, die eine Zeile in die Spalten zerlegt und den
  # Wert der 2. Spalte zurückliefert, so dass nach diesem sortiert wird
  print '\nlexikografisch nach Spalte 2 sortiert'
  for zeile in sorted(zeilen_liste, key = lambda z: z.split('|')[1]):
      print zeile
  
  # numerisch sortierte Ausgabe; analog oben, aber die Lambda-Funktion
  # konvertiert den Wert der 2. Spalte in eine ganze Zahl
  print '\nnumerisch nach Spalte 2 sortiert'
  for zeile in sorted(zeilen_liste, key = lambda z: int(z.split('|')[1])):
      print zeile

6. Indexierung und Slicing bei Sequenzen

  # wichtige Sequenzen: Strings und Tupel (beide unveränderlich) sowie Listen
  # (veränderlich)
  
  s = 'abcdefg'
  print s[2:5]                 # ==> 'cde'
  print s[0]                   # ==> 'a'
  print s[-1]                  # ==> 'g'
  print s[:]                   # ==> 'abcdefg'
                               #     allgemein erstellt "[:]" eine Kopie des Originals;
                               #     bei CPython-Strings werden aber keine Kopien generiert
  
  l = list(s)
  print l                      # ==> ['a', 'b', 'c', 'd', 'e', 'f', 'g']
  l[2:5] = 'x'                 # ==> Liste l wird manipuliert: ['a', 'b', 'x', 'f', 'g']
  print ''.join(l)             # ==> 'abxfg'
  l[:] = s[:2]                 # ==> Liste l wird manipuliert: ['a', 'b']
  print l                      # ==> ['a', 'b']
  
  # extended slices mit drittem Argument (step oder stride)
  print s[1:6:2]               # 'bdf'
  print s[::-1]                # 'gfedcba'
  
  print range(10)[::2]         # ==> [0, 2, 4, 6, 8]
  print range(10)[::-1]        # ==> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
  
  # Slicing hier konkret bei range() unnötig:
  print range(0,10,2)          # ==> [0, 2, 4, 6, 8]
  print range(9,-1,-1)         # ==> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

7. Steuerfluss

  #!/usr/bin/env python
  # -*- coding: utf8 -*-
  
  """
  Demo zu den Steuerfluss-Anweisungen von Python
  """
  
  import sys
  
  print 'Zeilen von stdin laut Länge klassifizieren'
  
  for line in sys.stdin:
    # \r und \n am String-Ende streichen
    line = line.rstrip('\r\n')
    # alle White-Spaces am Anfang und Ende des Strings streichen
    l = line.strip()
  
    if l == '' or l.startswith('#'):
      # Zeile ist leer oder Kommentar --> übergehen;
      # alternativer Test auf leeres l:
      #   if not l ...
      continue
  
    # Zeile nach der Länge klassifizieren
    l = len(line)
    if l < 10:
      print 'kurz', line
    elif l > 40:
      print 'lang', line
    else:
      print 'mittel', line
  
  print '---------------'
  
  print 'Ganzzahlen (Integers) eines Intervalls in zwei Integer-Faktoren zerlegen'
    
  # for mit else
  
  for n in range(2, 10):
    # Zahl n durchläuft das gewünschte Intervall
    for x in range(2, n):
      # x durchläuft alle möglichen ganzzahligen Faktoren
      if n % x == 0:
        # x teilt n ohne Rest und ist somit ein Faktor;
        # alternativer Test:
        #   if not n % x:
        print n, 'gleich', x, '*', n/x
        break
    else:
      # reguläres Schleifenende erreicht, also kein Abbruch durch break --> kein
      # Faktor ermittelt
      print n, 'ist eine Primzahl'
  
  # pass -- leere Anweisung
  if len(sys.argv) > 1:
    # muss noch implementiert werden
    pass
  
  # while-Schleifen mit und ohne else
  i = 10
  while i > 0:
    print i
    i -= 1
    if i == 5 and len(sys.argv) > 1:
      break
  else:
    # reguläres Schleifenende erreicht (kein Abbruch durch break)
    print 'Ende der Schleife', i
  
  print '==='
  
  # Endlosschleife
  i = 10
  while True:
    # typische Alternative:
    #   while 1: ...
    if i < 0:
      break
    print i
    i -= 1

8. Reguläre Ausdrücke

  #!/usr/bin/env python
  # -*- coding: utf8 -*-
  
  """
  Reguläre Ausdrücke - Modul re
  """
  
  import sys, re
  
  # (?i)    --> Flag i: ignore case
  # (?=egg) --> lookahead assertion; wird nur gesucht, nicht konsumiert
  muster = re.compile(r'(?i)spam\d+(?=egg)')
  
  s = 'x spaM1eGg spamlegg spam12xyc Spam88EGg spam23'
  print s
  
  # alle Vorkommen des Musters suchen
  print muster.findall(s)
  
  # erstes Vorkommen des Musters suchen
  res = muster.search(s)
  if res:
    print 'gefunden', s[res.start() : res.end()]
  print
  
  # match() sucht das Muster nur am String-Anfang (wie search() mit Anker ^)
  s = '# anton=a22 berta=b11 caesar dora='
  res = re.match(r'(\w+)=(\w+)', s)
  print 'match:', res
  
  # search() sucht das Muster irgendwo im String
  res = re.search(r'(\w+)=(\w+)', s)
  if res:
    print 'search:', res.groups()
  
  # Substitution
  print re.sub(r'(\w+)=(\w+)', r'\1:"\2"', s)
  
  # Substitution unter Nutzung einer eigenen Replace-Funktion, die für jedes
  # Match-Objekt gerufen wird; wir wandeln in Ausdrücken der Form "wort=wort" nur
  # das Wort links vom Gleicheitszeichen in Großbuchstaben um; group(0) und
  # group(1) sind hier identisch
  print re.sub(r'(\w+)(?==\w+)', lambda x : x.group(0).upper(), s)
  print re.sub(r'(\w+)(?==\w+)', lambda x : x.group(1).upper(), s)
  
  # man kann statt einer anonymen Lambda-Funktion auch eine "normale" benannte
  # Funktion nutzen
  def change_match(match_object):
    return match_object.group(0).upper()
  
  print re.sub(r'(\w+)(?==\w+)', change_match, s)
  
  # es ginge hier auch ohne Klammerung (ein Sub-Muster wird nicht benötigt)
  print re.sub(r'\w+(?==\w+)', lambda x : x.group(0).upper(), s)
  
  # man kann im Muster auch Rückbezüge nutzen
  t = 'Anton=Emil Berta=Berta Caesar=Lilly Dora=Dora Emil=Detektiv'
  print re.sub(r'\b(\w+)=(\1)\b', lambda x : x.group(1).upper() + '=' + x.group(2).lower(), t) 
  
  # Auswertung der kompletten OAI-Entladung von Qucosa im Format xMetaDissPlus
  
  # Muster zur Erkennung von URNs
  #
  # Präfixe:
  #   urn:nbn:de:swb:ch1-
  #   urn:nbn:de:bsz:ch1
  #   urn:nbn:de:bsz:ch1-qucosa-
  #
  # Für uns sind nur die neuen URNs von Qucosa interessant, also
  # urn:nbn:de:bsz:ch1-qucosa-.
  urn_patt = re.compile(r'urn:nbn:de:(swb|bsz):ch1-(qucosa-)?\d+')
  
  # Muster zur Erkennung von Qucosa-File-URLs
  #   http://www.qucosa.de/fileadmin/data/qucosa/documents/6015/data/Thesis_Steidle.pdf
  #   http://www.qucosa.de/fileadmin/data/qucosa/documents/4991/container/container.zip
  url_patt = re.compile(r'qucosa.de/fileadmin/data/qucosa/documents/(\d+)')
  
  # Muster zur Erkennung von ISSN und ISBN in Quellefeldern
  issn_patt = re.compile(r'(?i)ISSN:?\s*\d{4}-?\d{3}[\dX]|\<\d{4}-\d{3}[\dX]')
  isbn_patt = re.compile(r'(?i)ISBN:?\s*[\dX-]+|\<(\d+-){3}[\dX]+')
  # ein pragmatischer Versuch für DOIs
  doi_patt = re.compile(r'(?i)(DOI:?\s*|^|\s)(\d+\.\w+(/|%2f)[\w./<>:;()+-]+)')
  
  # komplette Datei als String einlesen
  try:
    xml_string = open('xMetaDissPlus.xml').read()
  
    # der Reihe nach die Muster absuchen und die Treffer ausgeben
    for text, pattern  in (
        ('urns', urn_patt),
        ('urls', url_patt),
        ('isbns', isbn_patt),
        ('issns', issn_patt),
        ('dois', doi_patt)):
      print
      print text
      for elem in pattern.finditer(xml_string):
        print elem.group(0)
  except Exception as e:
    print 'Problem bei xMetaDissPlus', e

9. Ausnahme-Behandlung

  # -*- coding: utf8 -*-
  """
  Ausnahmebehandlung (Exception Handling)
  """
  
  import sys
  
  def cat(filename, use_with = 1):
    """Ausgabe einer Datei analog Kommando cat"""
    try:
      if use_with:
        # am Ende des with-Blocks sorgt der Kontext-Manager generell dafür, dass
        # die Datei geschlossen wird
        with open(filename) as f:
          print f.read(),
      else:
        # die Datei wird geschlossen, wenn der Garbage Collector das Datei-Objekt
        # beseitigt
        print open(filename).read(),
    except:
      print 'Kann %s nicht einlesen' % filename
    else:
      print 'Alles OK'
          
  cat('/etc/hosts')
  cat('/etc/hosts', use_with = 0)
  cat('/etc/hosts1')
        
  print '------------------------'
  
  # Auswertung des Exception-Objekts, Behandlung von IOErrors
  for myfile in '/etc/shadow', '/etc/shadow2':
    try:
      open(myfile)
    except Exception, e:
      print 'Ausnahme: %s' % e
      print 'type', type(e)
      if isinstance(e, IOError):
        print 'IOError'
        print 'filename', e.filename
        print 'strerror', e.strerror
        print 'errno', e.errno
        print 'args', e.args
        if e.errno == 13 and e.filename == myfile:
          print '%s ==> permisson denied!!' % myfile
        print '======'
  
  print '------------------------'
  
  # try / except / else
  print 'try / except / else'
  print 'a < 0 ==> SystemExit (ignoriert)'
  print 'a > 7 ==> ValueError'
  try:
    a = int(raw_input('Nummer des auszugebenden Kommandozeilenarguments: '))
    if a < 0:
      exit(0)
    if a > 7:
      raise ValueError
    print sys.argv[a]
  except ValueError:
    print 'Ungueltiger Wert'
  except SystemExit:
    # diese Ausnahme ignorieren
    pass
  except:
    # sonstige Ausnahme erneut auswerfen
    raise
  else:
    # keine Ausnahme aufgetreten
    print 'Alles OK:', a
  
  print '------------------------'
  
  # try / finally
  print 'try / finally'
  for i in 1, 0:
    try:
      try:
        x = 1 / i
      finally:
        # finally wird immer ausgeführt und ist für Aufräumarbeiten gedacht;
        # eine aufgetretene Ausnahme wird nach finally erneut ausgeworfen
        print 'Division beendet'
      print x
    except:
      print 'Ausnahme %s, %s' % sys.exc_info()[:2]
  
  print '------------------------'
  
  # try / except / finally
  print 'try / except / finally'
  for i in 1, 0:
    try:
      x = 1 / i
      b # erzeugt einen NameError, da b nicht existiert
    except NameError, e:
      print 'Ausnahme %s, %s' % (type(e), e)
    finally:
      # finally wird immer ausgeführt und ist für Aufräumarbeiten gedacht;
      # eine nicht abgefangene Ausnahme wird nach finally erneut ausgeworfen
      print 'Division beendet'
    print x

10. Tupel, Listen, Dictionaries, Mengen

  #!/usr/bin/env python
  # -*- coding: utf8 -*-
  
  """
  Demo zu den Collection Types Tupel, Liste, Dictionary und Menge
  """
  
  import sys
  
  # Python-Version bestimmen
  python3 = sys.version_info[0] == 3
  python27 = sys.version_info[0] == 2 and sys.version_info[1] > 6
  
  # sofern es (bei Python 2) noch xrange gibt, nutzen wir dieses an Stelle von
  # range
  if 'xrange' in dir(__builtins__):
    range = xrange
  
  # auf print basierende portable Ausgabefunktion für Python 2 und 3
  def ausgabe(*args):
    # wir wandeln alle Positionsparameter über einen Generatorausdruck in
    # Strings um und verketten sie durch je ein Leerzeichen
    print(' '.join(str(x) for x in args))
  
  # Tupel
  ausgabe('Tupel')
  
  # leeres Tupel
  tupel0 = ()
  ausgabe(tupel0)
  
  # einelementiges Tupel mit Komma am Ende
  tupel1 = 1,      # Singleton
  ausgabe(tupel1)
  
  # mehrelementiges Tupel
  tupel = 1, 2, 3
  
  for i in 1, 2, 3: ausgabe(i)
  ausgabe()
  
  # explizite Tupel-Klammerung bei nicht leeren Tupeln generell möglich, aber bei
  # Tupel-Zuweisungen und Schleifen nicht nötig
  tupel1 = (1,)
  tupel = (1, 2, 3)
  for i in (1, 2, 3):
    ausgabe(i)
  ausgabe()
  
  ausgabe(tupel[0:2])
  a, b, c = tupel
  for i in tupel:
    ausgabe(i)
  ausgabe(tupel + tupel, tupel * 2)
  ausgabe(tupel + tuple('abcd') + tuple([1, 2, 3]))
  
  # hier ist eine explizite Klammerung nötig
  ausgabe('%d %d %d' % (1, 2, 3))
  
  # Tupel Packing und Sequence Unpacking
  a, b = 1, 2
  ausgabe('a,b=', a, b)
  # Austausch des Inhalts zweier Variablen (ohne Hilfsvariable)
  a, b = b, a
  ausgabe('a,b=', a, b)
  
  # Sequence Unpacking unter Nutzung eines durch einen Generatorausdruck
  # erzeugten Generators, der im Rahmen einer Iteration schrittweise die
  # gewünschten Werte liefert; konkret: Datums-String in seine Bestandteile
  # zerlegen, die resultierende Liste invertieren, deren Elemente durchlaufen, in
  # ganze Zahlen konvertieren und an 3 Variablen zuweisen
  t, m, j = (int(x) for x in reversed('2012-08-05'.split('-')))
  ausgabe(t, m, j)
  
  # Listen
  ausgabe('\nListen')
  
  # bei Python 2 kann man statt long(4) auch 4l oder 4L notieren, bei Python 3
  # ist das aber ein Syntaxfehler
  liste = [1, 'abc', 2.3, 4 if python3 else long(4), (1, 2), {1: 2}, [5, 6, 7]]
  ausgabe(liste)
  for elem in liste:
    ausgabe(type(elem), elem)
  
  ausgabe()
  # erstes Listenelement löschen
  del liste[0]
  # verbleibende Liste durchnummeriert (mit den Index-Werten) ausgeben
  for pos, elem in enumerate(liste):
    ausgabe(pos, elem)
  
  ausgabe()
  # die ersten 10 Zeilen von /etc/passwd in umgekehrter Reihenfolge durchlaufen
  for line in reversed(open('/etc/passwd').readlines()[:10]):
    # die Felder in umgekehrter Reihenfolge zusammensetzen und ausgeben
    ausgabe(':'.join(reversed(line.rstrip('\r\n').split(':'))))
  
  ausgabe()
  
  # List Comprehension
  
  # Liste der ungeraden Zahlen im Intervall [0, 19]
  liste = [x for x in range(20) if x % 2]
  
  # Liste der Tupel (x, y), bei denen y die ungeraden Zahlen im Intervall [0, 5]
  # und x die ganzzahlig durch 3 teilbaren Zahlen im Intervall [0, 9] durchläuft
  liste = [(x, y) for x in range(10) if not x % 3 for y in range(6) if y % 2]
  
  # statt des obigen Generatorausdrucks zum Aufspalten des Datums könnte man auch
  # eine List Comprehension nutzen, damit eine Liste aufbauen und beim Sequence
  # Unpacking verwenden
  t, m, j = [int(x) for x in reversed('2012-08-05'.split('-'))]
  
  # bei langen Listen ist das ggf. nachteilig; hier sind Generatoren
  # normalerweise effektiver, weil sie weniger Speicher benötigen
  ausgabe(sum(x * x for x in range(10000)))
  
  # eine analoge, selbst implementierte Generatorfunktion (mit yield statt
  # return)
  def num_gen(n):
    for x in range(n):
      yield x * x
  
  ausgabe(sum(num_gen(10000)))
  
  ausgabe('Listen als Stacks und Queues')
  ausgabe(liste)
  ausgabe('pop(3)')
  ausgabe(liste.pop(3))
  ausgabe(liste)
  ausgabe('append(25)')
  liste.append(25)
  ausgabe(liste, len(liste))
  
  stack = [1, 2, 3]
  # queue durch Slicing als Kopie von Stack anlegen
  queue = stack[:]
  
  # für push und pop gebundene Methoden der Listen-Klasse verwenden
  push = stack.append
  pop = stack.pop
  
  ausgabe('\nStack')
  push(4); ausgabe(stack)
  push(5); ausgabe(stack)
  while stack:
    el = pop()
    ausgabe(el, ',', stack)
  
  ausgabe('\nQueue')
  enqueue = queue.append
  # dequeue in 2 Versionen implementieren: als anonyme Lambda-Funktion oder als
  # benannte Funktion
  if 1:
    dequeue = lambda: queue.pop(0)
  else:
    def dequeue():
      return queue.pop(0)
  enqueue(4); ausgabe(queue)
  enqueue(5); ausgabe(queue)
  while queue:
    el = dequeue()
    ausgabe(el, ',', queue)
  
  # Dictionaries
  ausgabe('\nDictionaries')
  d = {'Anton': 34, 'Berta': 19, 'Cäsar': 45, 'Alfred': 88, 'Bianca': 11}
  
  ausgabe('\nsortierte Schlüssel')
  ausgabe(sorted(d.keys()))
  ausgabe(sorted(d))
  
  ausgabe('\nsortierte Werte')
  ausgabe(sorted(d.values()))
  
  ausgabe('\numgekehrt sortierte Werte')
  ausgabe(sorted(d.values(), reverse = True))
  ausgabe(list(reversed(sorted(d.values()))))
  
  # Iteration über dem Dictionary
  ausgabe('\nPaare')
  for key, value in d.items() if python3 else d.iteritems():
    ausgabe(key, value)
  ausgabe()
  
  ausgabe('Anton', d['Anton'])
  del d['Anton']
  d['Alfred'] += 10
  d.update(Marta = 23, Emma = 77)
  d = dict(d, Otto = 88, Adam = 43)
  d.update([('Jutta', 44), ('Jens', 17)])
  d.update((('Max', 99), ('Moritz', 100)))
  # noch mit Generatorausdruck
  d.update((x, x + 1) for x in range(8))
  
  ausgabe('\nAusgabe d:')
  ausgabe(d)
  for name in 'Anton', 'Emma', 'Jörg':
    ausgabe(name, name in d)
  
  # Mengen
  ausgabe('\nMengen')
  set1 = set('abcdax')  
  set2 = frozenset((1, 2, 3, 1, 'b'))  
  ausgabe('set1:', set1)
  ausgabe('set2:', set2)
  ausgabe('Vereinigung:', set1 | set2, set1.union(set2))
  ausgabe('Differenz:', set1 - set2, set1.difference(set2))
  ausgabe('Schnittmenge:', set1 & set2, set1.intersection(set2))
  # hier noch die symmetrische Differenz, also die Differenz aus
  # Vereinigungsmenge und Schnittmenge bilden
  ausgabe('Symmetrische Differenz:', set1 ^ set2, set1.symmetric_difference(set2))
  
  # Elemente hinzufügen und löschen
  set1.add(45)
  set1.remove('x')
  try:
    set1.remove('x')
  except:
    ausgabe('x nicht in der Menge!')
  set1.discard('x') # wenn x nicht in der Menge, dann passiert nichts
  set1.discard('a')
  ausgabe('set1:', set1)
  
  # Test auf Enthaltensein
  ausgabe('45 in set1:', 45 in set1) # True
  ausgabe('46 in set1:', 46 in set1) # False
  
  # ab Python 2.7 existieren u.a. noch:
  # - eine Dictionary und Set Comprehension sowie ein Set-Literal
  # - im Modul collections ein OrderedDict (das sich die Reihenfolge merkt, in
  #   der die Schlüssel eingefügt wurden) sowie ein defaultdict (das bei
  #   Zugriffen auf nicht vorhandene Schlüssel bestimmte Vorzugswerte liefert)
  if python27 or python3:
    import collections
  
    # der Code, der nur ab Python 2.7 kompilierbar ist, wird in Form eines
    # Strings angegeben und via exec() ausgeführt
    exec(r'ausgabe("\nDictionary Comprehension") ; s = {x: x*x for x in range(5)} ; ausgabe(s)')
    # ==> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
  
    exec(r'ausgabe("\nSet Comprehension") ; s = {x for x in range(20) if x % 3} ; ausgabe(s)')
    # Python 2.7 ==> set([1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19])
    # Python 3.4 ==> {1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19}
    exec(r'ausgabe("Set Comprehension 2") ; s = {(x, y) for x in range(2) for y in range(3)} ; ausgabe(s)')
    # Python 2.7 ==> set([(0, 1), (1, 2), (0, 0), (1, 1), (1, 0), (0, 2)])
    # Python 3.4 ==> {(0, 1), (1, 2), (0, 0), (1, 0), (0, 2), (1, 1)}
    exec(r'ausgabe("Set-Literal") ; s = {1, 3, 4} ; ausgabe(s)')
    # Python 2.7 ==> set([1, 3, 4])
    # Python 3.4 ==> {1, 3, 4}
  
    ausgabe('\nOrderedDict')
    o = collections.OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
    ausgabe(o)
    # ==> OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
    o['pear'] = 99
    ausgabe(['pear'])
    # ==> 99
    ausgabe(o)
    # ==> OrderedDict([('pear', 99), ('orange', 2), ('banana', 3), ('apple', 4)])
    del o['pear']
    o['pear'] = 99
    ausgabe(o)
    # ==> OrderedDict([('orange', 2), ('banana', 3), ('apple', 4), ('pear', 99)])
  
    # defaultdict
    ausgabe('\ndefaultdict')
    s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
    # als Vorzugswert wird eine leere Menge geliefert
    d = collections.defaultdict(set)
    for key, val in s:
      # beim Zugriff auf einen noch nicht existenten Key wird eine leere Menge
      # (set) geliefert
      d[key].add(val)
  
    ausgabe(d.items())
    # ==> [('blue', set([2, 4])), ('red', set([1])), ('yellow', set([1, 3]))]

11. Funktionen

  # -*- coding: utf8 -*-
  
  """
  Definition und Aufruf nutzerdefinierter Funktionen
  """
  
  from math import pi
  
  # globale Variable, die ein unveränderbares (immutable) Objekt referenziert
  g = 100
  
  # Addition von x zu g
  def add_global(x):
    # durch die Anweisung "global" beziehen sich Zuweisungen an g auf die globale
    # Variable g
    global g
    g += x      # Kurzform von g = g + x 
    # ohne return wird immer None als Funktionswert geliefert
  
  print 'g', g
  
  # den Funktionswert (hier None) fangen wir nicht auf --> Nutzung einer Funktion
  # als Prozedur
  add_global(10)
  print 'g nach add_global(10)', g
  print 'Wert von add_global(20)', add_global(20)
  print 'g', g
  
  def set_lokal(x):
    # ohne die Anweisung "global" erfolgt die Zuweisung an g immer lokal
    g = 2 * x
    print 'g in set_lokal', g
  
  set_lokal(20)
  print 'g nach set_lokal(20)', g
  
  def print_g():
    # das globale g ist auch ohne die Anweisung "global" lesbar
    print 'print_g', g
  
  print_g()
  
  # Funktion mit 2 Parametern; Parameter b bekommt einen Vorzugswert
  def produkt(a, b = 2):
    return a * b
  
  print '3*4=', produkt(3, 4)
  print '3*2=', produkt(3)
  
  # Nutzung von Schlüsselwort-Parametern statt Positionsparametern
  print 'AB*5=', produkt(b = 5, a = 'AB')
  
  # eine globale Variable, die auf ein veränderbares (mutable) Objekt verweist
  g_mutable = []
  
  # Funktion, die den Wert einer globalen Variablen verändert
  def add_to_list(x):
    # auch ohne die Anweisung "global" kann man das durch g_mutable referenzierte
    # Objekt modifizieren
    g_mutable.append(x)
  
  print 'g_mutable', g_mutable
  add_to_list(11)
  add_to_list('hallo')
  print 'g_mutable nach add_to_list', g_mutable
  
  # Rückgabe eines Tupels als Funktionswert; so kann eine Funktion n Werte
  # zurückliefern (auch Mengen, Dictionaries, Listen sind dafür nutzbar)
  def ret_n_werte(x):
    return x, x.upper(), x.lower(), x + '|' + x
  
  print
  res = ret_n_werte('Auto')
  print res
  
  # das Resultat-Tupel von hinten nach vorn ausgeben
  for r in res[::-1]: print r
  
  # dito, aber anders implementiert
  print
  for r in reversed(res): print r
  
  # eine als Parameter übergebene Filterfunktion auf alle Elemente einer Liste
  # anwenden (dafür gibt es bereits die Standardfunktion filter())
  def filtern(filt, liste):
    res = []
    for elem in liste:
      if filt(elem):
        res.append(elem)
    return res
  
  # Filterfunktion, die nur bei geraden Zahlen True liefert
  def gerade(x):
    return not x % 2
  
  # eine benannte Funktion wird als Filter übergeben
  print '\ngerade range(15)', filtern(gerade, range(15))
  
  # eine anonyme Lambda-Funktion als Filter
  print 'ungerade range(15)', filtern(lambda x: x % 2, range(15))
  
  # andere Implementierung von filtern(); das alte Funktionsobjekt wird dabei
  # eliminiert (vom Garbage Collector entsorgt), da es keinen Verweis mehr darauf
  # gibt (der Name "filtern" verweist nun auf das neue Funktionsobjekt)
  def filtern(filt, liste):
    # List Comprehension nutzen
    return [filt(x) for x in liste]
  
  liste = ['Abc', 'deF', 'GhI']
  print 'upper', filtern(str.upper, liste)
  # oder
  print 'upper 2', filtern(lambda x: x.upper(), liste)
  
  # Funktion mit beliebiger Anzahl von Positionsparametern
  def produkt_n(*faktoren):
    # testen, ob wir mindestens 1 Faktor haben
    assert faktoren, 'mindestens 1 Faktor erforderlich!'
    produkt = faktoren[0]
    for faktor in faktoren[1:]:
      produkt *= faktor
    return produkt
  
  print '\nprodukt_n()'
  try:
    produkt_n()
  except AssertionError as e:
    print e
  
  print 'produkt_n(1, 2, 3, 4, 5)=', produkt_n(1, 2, 3, 4, 5)
  print 'produkt_n(range(1, 6))=', produkt_n(*range(1, 6))
  
  # andere Implementierung
  def produkt_n(*faktoren):
    assert faktoren, 'mindestens 1 Faktor erforderlich!'
    return reduce(lambda x, y: x * y, faktoren)
  
  print 'mit reduce'
  try:
    print 'produkt_n(1, 2, 3, 4, 5)=', produkt_n(1, 2, 3, 4, 5)
    print 'produkt_n()=',
    print produkt_n()
  except AssertionError as e:
    print e
  
  # Funktion mit beliebiger Anzahl von Positions- und Schlüsselwort-Parametern
  def flexibel(*par, **sw_par):
    print 'Positionsparameter'
    for p in par:
      # da p auch ein Tupel sein kann, konvertieren wir p explizit in einen
      # String; andernfalls kann es zu einem Fehler kommen:
      #
      #   >>> p = 1,2,3
      #   >>> print '%s' % p
      #   Traceback (most recent call last):
      #     File "<stdin>", line 1, in <module>
      #   TypeError: not all arguments converted during string formatting
      print '  %s' % str(p)
  
    print 'Schlüsselwort-Parameter'
    # hier wissen wir, dass alle Schlüssel von sw_par Strings sind (andere Typen
    # sind unzulässig):
    #   TypeError: flexibel() keywords must be strings
    for p in sw_par:
      print '  %s:%s' % (p, sw_par[p])
  
  print '\nflexibel'
  flexibel(1, 'zwei', (1, 2), a = 23, b = [1, 2, 3], c = set([11, 22]), **{'d': 200, 'e': 400})
  
  # eine rekursive Funktion mit lokaler Funktion (nested functions)
  def maximum(*elems):
    # die innere Funktion ermittelt das Maximum von 2 Objekten; dafür nutzt man
    # normalerweise die Standardfunktion max(), die das Maximum einer Sequenz
    # ermittelt
    def max2(a, b):
      return a if a > b else b
  
    if elems:
      if len(elems) == 1:
        # bei einer einelementigen Sequenz ist das erste und einzige Element das
        # Maximum
        return elems[0]
  
      # impliziter else-Zweig: wir haben eine Sequenz mit mindestens 2 Elementen
      # vorliegen
      #
      # das Maximum aller Elemente entspricht dem Maximum des Tupels
      # (erstes Element, Maximum der restlichen Elemente); Termination der
      # Rekursion bei leerem "elems"
      return max2(elems[0], maximum(*elems[1:]))
  
  t = (1, 11, 111, 2, 22, 222, 999, 3, 33, 333)
  print '\nmax % s = %s' % (t, maximum(*t))
  t = 1,
  print 'max % s = %s' % (t, maximum(*t))
  t = ()
  print 'max % s = %s' % (t, maximum(*t))
  
  # eine äußere Funktion kann auch einen Verweis auf die innere Funktion
  # zurückliefern
  def make_adder(n = 5):
    def adder(x):
      # n ist der Parameter der äußeren Funktion
      return x + n
    # die äußere Funktion make_adder() liefert eine Closure zurück: eine Funktion
    # mit Bindung an einen Kontext für ihre freien Variablen
    return adder
  
  add_3 = make_adder(3)
  add_5 = make_adder()
  sub_1 = make_adder(-1)
  print '\nadd3(97)=', add_3(97)
  print 'add4(95)=', add_5(95)
  print 'sub_1(100)=', sub_1(100)
  
  from functools import wraps
    # s. https://docs.python.org/2/library/functools.html?functools.wraps#functools.wraps
  
  # Dekoratorfunktion; die Dekoration wird über eine Closure realisiert
  def add_log(f):
    # ohne den Dekorator @wraps() hätte app_elem.__name__ den Wert 'wrapper' und
    # der Doc-String app_elem.__doc__ ginge verloren
    @wraps(f)
    def wrapper(l, e):
      'der Wrapper, der um f gelegt wird'
      print 'füge %s an %s an' % (e, l)
      f(l, e)
    return wrapper
  
  # Nutzung des Dekorators;
  # @add_log entspricht app_elem = add_log(app_elem)
  @add_log
  def app_elem(liste, elem):
    'ein Element an eine Liste anhängen'
    liste.append(elem)
  
  print
  l = []
  app_elem(l, 55)
  app_elem(l, 66)
  print l
  
  print app_elem.__name__, '==>', app_elem.__doc__
  
  # eine generischere Implementierung der Dekoratorfunktion: mit beliebiger
  # Anzahl von Positions- und Schlüsselwort-Parametern; die Dekoration wird
  # wieder über eine Closure realisiert
  def add_log2(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
      'der wrapper in add_log2'
      # der Name der dekorierten Funktion steht in f.__name__
      print 'rufe %s: %s, %s' % (f.__name__, args, kwargs)
      resultat = f(*args, **kwargs)
      print 'Resultat von %s: %s' % (f.__name__, resultat)
      return resultat
    return wrapper
  
  # Nutzung des Dekorators
  @add_log2
  def summiere(wert1, wert2, wert3):
    'Summation'
    return sum((wert1, wert2, wert3))
  
  print
  print summiere(1, 2, 3)
  print summiere(wert3 = 100, wert2 = 200, wert1 = 100)
  
  print summiere.__name__, '==>', summiere.__doc__
  
  # Funktion, die ein veränderbares Parameter-Objekt modifiziert
  def to_upper(liste):
    """alle Strings einer Liste in Großbuchstaben umwandeln"""
    for i in range(len(liste)):
      try:
        liste[i] = liste[i].upper()
      except:
        # offenbar kein String; das ignorieren wir
        pass
  
  print
  l = ['anton', 1, 'berta']
  to_upper(l)
  print l
  
  # auch hier wird ein veränderbares Objekt modifiziert
  def make_unique(liste, keep = True):
    """alle mehrfach vorkommenden Listen-Elemente streichen"""
    if keep:
      # beim Streichen der Dubletten verändern wir die ursprüngliche Reihenfolge
      # der verbleibenden Elemente nicht
      result = []
      for elem in liste:
        if elem not in result:
          result.append(elem)
      # die alte durch die neue Liste komplett ersetzen
      liste[:] = result
    else:
      # Element-Reihenfolge spielt keine Rolle: Liste in eine Menge umwandeln,
      # dadurch die Dubletten eliminieren; anschließend die Menge wieder in eine
      # Liste konvertieren; durch den Slicing-Ausdruck ":" manipulieren wir die
      # Liste, die der Funktion als Argument übergeben wurde; sie wird komplett
      # durch die neue Liste ohne Dubletten ersetzt
      liste[:] = frozenset(liste)
      # oder etwas expliziter:
      #   liste[:] = list(frozenset(liste))
  
  print '\nmake_unique'
  l = [1, 8, 1, 2, 9, 2, 7, 2, 3, 7]
  print l
  m = l[:]
  make_unique(l)
  print l
  make_unique(m, False)
  print m
  
  print
  
  # Funktion zur Berechnung der Fibonacci-Zahlen mit Vorzugswert und Liste als
  # Ergebnis
  def fib(n = 5):
    """
    Folge der Fibonacci-Zahlen bis Zahl n ermitteln:
      a1 = 0
      a2 = 1
      an = an-2 + an-1 für n > 2
    """
    result = []
    # zwei Variablen, die die jeweils letzten beiden Elemente der Fibonacci-Folge
    # enthalten; die Initialbelegung mit 0, 1 sichert, dass wir die Sonderfälle
    # a1 und a2 ganz regulär mit behandeln können
    a, b = 0, 1
    # in einer Schleife n Fibonacci-Zahlen generieren
    for i in range(n):
      # aktuelles a_n an die Resultat-Liste anhängen
      result.append(a)
      # aus den aktuellen letzten beiden Elementen wird die nachfolgende Zahl der
      # Folge berechnet; die beiden Variablen werden durch zyklische Verschiebung
      # aktualisiert
      a, b = b, a + b
    return result
  
  print 'fib(10)', fib(10)
  print 'fib()', fib()
  print 'fib(0)', fib(0)
  print 'fib(1)', fib(1)
  print 'fib(2)', fib(2)
  
  # Funktion mit 2 Vorzugswerten, von denen aber wenigstens einer sinnvoll
  # überschrieben werden muss
  def kreis(radius = None, durchmesser = None):
    """
    Kreisumfang und -fläche berechnen
  
    Rückgabe: Tupel aus Umfang, Fläche, Fehlermeldung
    """
    eps = 1e-10
    if radius is durchmesser is None:
      return (None, None, 'Radius und Durchmesser nicht spezifiziert!')
    elif radius != None and durchmesser != None and abs(durchmesser - 2 * radius) > eps:
      return (None, None, 'Radius und Durchmesser passen nicht zusammen!')
    elif durchmesser is None:
      # Durchmesser aus Radius berechnen
      durchmesser = 2 * radius
    return (pi * durchmesser, pi / 4 * durchmesser ** 2, '')
  
  print '\nKreisberechnnung: Umfang, Fläche, ggf. Fehlermeldung'
  # wir durchlaufen ein Tupel von Ausdrücken, die die Funktion kreis() aufrufen,
  # und werten sie mit eval() aus
  for expr in ('kreis(radius = 3)',
               'kreis(3)',
               'kreis(durchmesser = 6)',
               'kreis(3, 6)',
               'kreis(3, 6.001)',
               'kreis()'):
    print expr, '-->', eval(expr)
  
  # Funktion mit Positionsparametern mit und ohne Vorzugswert sowie 2 Argumenten,
  # die die überzähligen Positions- und Schlüsselwort-Parameter auffangen
  def myfunc(arg1, arg2, arg3 = '', arg4 = 55, *pos_args, **key_args):
    print arg1
    print arg2
    print arg3
    print arg4
    print pos_args
    print key_args
    print
  
  # Positions- und Schlüsselwort-Parameter lassen sich beim Funktionsaufruf durch
  # Tupel, Listen und Dictionaries ausdrücken:
  
  tupel = 1, 2
  liste = [4, 5]
  dictionary = {'max' : 88, 'moritz' : 99}
  myfunc(1, arg4 = 66, *liste, **dictionary)
  
  # Resultat:
  #  1
  #  4
  #  5
  #  66
  #  ()
  #  {'max': 88, 'moritz': 99}
  
  myfunc(3, arg4 = 68, *tupel, **dictionary)
  
  # Resultat
  #  3
  #  1
  #  2
  #  68
  #  ()
  #  {'max': 88, 'moritz': 99}
  
  liste = [4, 5, 6]
  dictionary = {'arg4' : 111, 'max' : 88, 'moritz' : 99}
  myfunc(*liste, **dictionary)
  
  # Resultat:
  #  4
  #  5
  #  6
  #  111
  #  ()
  #  {'max': 88, 'moritz': 99}
  
  liste = [4, 5, 6, 7, 8, 9]
  dictionary = {'max' : 88, 'moritz' : 99}
  myfunc(*liste, **dictionary)
  
  # Resultat:
  #  4
  #  5
  #  6
  #  7
  #  (8, 9)
  #  {'max': 88, 'moritz': 99}
  
  # ein Tupel als Parameter ist ab Python 3 unzulässig!!
  def tupel_print(a, (b, c), d):
    print a
    print b
    print c
    print d
  
  tupel_print(1, ('a', 2), 1.34)
  
  # Resultat:
  #  1
  #  a
  #  2
  #  1.34

12. Ein-/Ausgabe

  #!/usr/bin/env python3
  # -*- coding: utf8 -*-
  
  """
  Demo der Ein-/Ausgabe mit und ohne Formatierung
  """
  
  import sys
  
  # Ausgabe nach stdout
  print(1, 2, 'hallo')
  
  # ohne Zeilenschaltung am Ende
  print('Hallo', end=' ')
  # mit Zeilenschaltung am Ende
  print('Leute')
  
  # Ausgabe nach stderr
  print('Fehler', file=sys.stderr)
  
  # zeilenweise von stdin lesen
  print('zeilenweise von stdin lesen')
  for line in sys.stdin:
      print(line, end=' ')
  
  # ältere Variante:
  print('nochmal zeilenweise von stdin lesen')
  while 1:
      line = sys.stdin.readline()
      if line == '':
          # alternativer Test:
          #   if not line:
          break
      print(line, end=' ')
  
  print('===')
  
  # File zum Lesen öffnen
  f = open('/etc/hosts')
  
  # File geschlossen einlesen
  
  # als String
  inhalt = f.read()
  
  # danach den Lesezeiger wieder an den Anfang setzen
  f.seek(0)
  
  # als Zeilenliste
  zeilen_liste = f.readlines()
  print(inhalt)
  print(zeilen_liste)
  print()
  
  # File kopieren
  out = open('/tmp/hosts_dos', 'w')
  for line in open('/etc/hosts'):
      # im DOS-Format speichern
      print(line.rstrip('\n') + '\r', file=out)
  # File schließen
  out.close()
  
  # noch eine Kopie im Unix-Format anlegen
  out = open('/tmp/hosts_unix', 'w')
  for line in open('/tmp/hosts_dos'):
      # durch den standardmäßig aktiven Universal Newline Mode stellt Python die
      # gelesenen Zeilen automatisch im UNIX-Format bereit (mit \n am Ende)
      out.write(line)
  out.close()
  
  # mit with
  with open('/etc/hosts') as in_file:
      with open('/tmp/hosts_dos', 'w') as out_file:
          for line in in_file:
              out_file.write(line.strip('\n') + '\r\n')
  
  # ab Python 2.7 geht es auch so:
  # with open('/etc/hosts', 'U') as in_file, open('/tmp/hosts_dos', 'w') as out_file:
  #     for line in in_file:
  #         out_file.write(line.strip('\n') + '\r\n')
  
  # bei Python 3 ist 'U' deprecated
  with open('/etc/hosts') as in_file, open('/tmp/hosts_dos', 'w') as out_file:
       for line in in_file:
           out_file.write(line.strip('\n') + '\r\n')
  
  # Formatierung mit %
  for i in range(10):
      print('%10d, %10d' % (i, 2 * i))
  print()
  
  # die Werte aus einem Dictionary nehmen
  d = {'Anton': 10, 'Berta' : 20, 'Cäsar' : 30}
  print('%(Anton)d, %(Berta)d, %(Cäsar)d' % d)
  print()
  
  # die Funktionen zfill() und rjust() nutzen
  for i in range(10):
      print(str(i).zfill(10))
  print()
  
  for i in range(10):
      print(str(i).rjust(20))

13. Ein-/Ausgabe 2 - Messwertprotokoll

  #!/usr/bin/env python
  # -*- coding: utf8 -*-
  #
  # Messwerte lesen und zusammen mit einem Zeitstempel in ein Messwertprotokoll
  # (Textdatei) ausgeben
  #
  # 24.6.2016
  
  import datetime, time, random, struct
  
  # den Zufallszahlengenerator initialisieren
  random.seed()
  
  # die Ausgabedatei mit dem Messwertprotokoll zum Schreiben öffnen
  out = open('messwert_protokoll.txt', 'w')
  
  # die Eingabedatei mit den Messwerten zum Lesen öffnen; wir simulieren die
  # Datenquelle durch die Linux-Zufallsdatenquelle /dev/urandom 
  mw_quelle = open('/dev/urandom')
  
  # Endlos-Einleseschleife (Abbruch durch Ctrl-C)
  while True:
    # den Messwert (4 Zufallsbytes) lesen
    mw = mw_quelle.read(4)
  
    # den aktuellen Zeitstempel bilden
    tstamp = datetime.datetime.today()
    
    # Kontrollausgabe des Messwerts
    print 'MW: %s' % repr(mw)
  
    # den Messwert in seine Hex-Darstellung konvertieren (byteweise, big-endian
    # byte order)
    #
    # hex() generiert keine führende Null: hex(5) ==> 0x5 und nicht 0x05
    # wir ergänzen die führende Null mit zfill()
    if 1:
      # Variante 1 mit Generator
      mw_hex = '0x' + ''.join(hex(ord(byte))[2:].zfill(2) for byte in mw)
    else:
      # Variante 2 mit struct.unpack:
      #
      #   > symbolisiert big-endian
      #   I interpretiert den 4-Byte-String als unsigned int der Länge 4 Byte
      #   unpack liefert ein Tupel mit dem unsigned int als 1. Element
      mw_hex = '0x' + hex(struct.unpack('>I', mw)[0])[2:].zfill(8)
  
    # Ausgabe von Zeitstempel und Hex-Messwert
    print >>out, '%s: %s' % (tstamp, mw_hex)
  
    # etwas schlafen, um auf den folgenden Messwert zu warten
    sleep_time = random.randint(0, 3)
    print 'sleep %s' % sleep_time
    time.sleep(sleep_time)

14. Start externer Kommandos

  #!/usr/bin/env python
  # -*- coding: utf8 -*-
  
  """
  Aufruf externer Programme aus Python-Programmen
  """
  
  import sys, os, tempfile, subprocess
  
  class MyError(Exception):
      """eigene Exception-Klasse"""
      def __init__(self, value = ''):
          Exception.__init__(self, value)
  
  def make_temp_file():
      """sichere Erzeugung eines temporären Files"""
      if 'mkstemp' in dir(tempfile):
          # mkstemp() steht zur Verfügung (ab Python 2.3); dann nutzen wir es;
          # es liefert ein Tupel aus File Descriptor und File-Name; wir benötigen den
          # Namen; den Descriptor schließen wir
          fd, name = tempfile.mkstemp('', 'PYTMP.', '/tmp')
          os.close(fd)
          return name
      else:
          # mkstemp() steht nicht zur Verfügung; dann nutzen wir das
          # Unix-Standard-Tool mktemp über eine Pipe
          data = os.popen('mktemp /tmp/PYTMP.XXXXXXXX 2>/dev/null').readlines()
          if not data:
              # leerer Datei-Name --> Fehler
              raise MyError, 'Fehler beim Anlegen einer temporären Datei'
          # den Namen der temporären Datei zurückgeben
          return data[0].strip()
  
  def quote_shell_args(s):
      """
      einen String so quotieren, dass man ihn literal als Wort an ein
      Bourne-Shell-Skript übergeben kann
      """
      return "'" + s.replace("'", r"'\''") + "'"
  
  def run_external_command(*cmd_words):
      """externes Kommando über eine Bourne-Shell ausführen"""
      try:
          # Aufruf des Shell-Kommandos vorbereiten;
          # zuerst einen temporären Dateinamen für die Kommunikation mit dem
          # Shell-Skript generieren
          tfile = make_temp_file()
  
          # den Kommando-String generieren
          cmd = ' '.join([quote_shell_args(str(word)) for word in cmd_words] + ['>> %s 2>&1' % tfile])
  
          # Skript ausführen
          if os.system(cmd):
              # system() hat einen Fehler gemeldet; wir melden ihn über die Ausnahme
              # MyError
              raise MyError, 'Fehler beim Ausführen des externen Skripts ' + cmd
  
          # Ausgabe des Skripts zurücklesen und zurückgeben; das temporäre File wird
          # hier gleich mit gelöscht
          result = open(tfile).readlines()
          os.unlink(tfile)
          return result
      except MyError:
          # eine MyError-Ausnahme werfen wir unverändert wieder aus
          raise
      except:
          # alle anderen Ausnahmen werfen wir als MyError-Ausnahme aus
          raise MyError, 'Ausnahme: %s/%s' % (sys.exc_info()[0], sys.exc_info()[1])
  
  # Start eines externen Kommandos via Shell
  os.system('date')
  print
  
  # externes Shell-Kommando starten und das Ergebnis über eine Pipe zurücklesen
  for line in os.popen('cal 6 2009 2>&1'):
      print line,
  
  # Implementierung mit dem Modul subprocess statt os.system (ab Python 2.4):
  for cmd in ('cal', '7', '2006'), ('rm', '/'), 'date1':
      try:
          print 'calling %s' % repr(cmd)
          status = subprocess.call(cmd)
          if status:
              print 'Fehler; Exit-Code:', status
      except:
          print 'Ausnahme: %s/%s' % (sys.exc_info()[0], sys.exc_info()[1])
      print
  
  # Nutzung der eigenen Funktion run_external_command
  for cmd in ('cal', 7, 2006), ('cal1', 7, 2006):
      try:
          for line in run_external_command(*cmd):
              print line,
          print
      except MyError, obj:
          print obj.args[0], '\n'
  
  # bidirektionale Kommunikation mit einem externen Kommando
  subp = subprocess.Popen('touch /tmp/aaa ; rm -v /tmp/aaa / ; tr a-z A-Z',
                          # line buffered
                          bufsize = 1,
                          # das Kommando an die Shell zur Ausführung übergeben
                          shell = True,
                          # über eine Pipe kommunizieren
                          stdin = subprocess.PIPE,
                          stdout = subprocess.PIPE,
                          stderr = subprocess.PIPE
                         )
  s = 'aaa\nbbb\nccc'
  stdout, stderr = subp.communicate(s)
  print 'gesendet:', s
  print 'stdout:', stdout
  print 'stderr:', stderr

15. Funktionale Programmierung

  #!/bin/env python3
  
  """
  Demo zu filter(), map(), reduce(), lambda
  List Comprehensions
  Generator-Ausdrücke
  """
  
  import operator
  from math import pi, sin
  from functools import reduce
  
  # filter(), map(), reduce()
  
  print('Liste der nicht durch 2 und 3 teilbaren ganzen Zahlen zwischen 2 und 24')
  def f(x): return x % 2 and x % 3
  print(list(filter(f, range(2, 25))))
  print()
  
  print('Liste der dritten Potenzen der ganzen Zahlen von 1 bis 10')
  def cube(x): return x ** 3
  print(list(map(cube, range(1, 11))))
  print()
  
  print('Liste der Produkte der korrespondierenden Elemente der Intervalle [1, 7] und [7, 1]')
  seq = range(1, 8)
  def mul(x, y): return x * y
  print(list(map(mul, seq, reversed(seq))))
  print()
  
  print('Summe der ganzen Zahlen von 1 bis 10')
  def add(x, y): return x + y
  print(reduce(add, range(1, 11)))
  print()
  
  print('Summe einer Zahlenfolge')
  def mysum(seq):
      def add(x, y): return x + y
      # hier mit Start-Wert 0, so dass wir auch leere Sequenzen verarbeiten können
      return reduce(add, seq, 0)
  print(mysum(range(1, 11))), mysum([])
  print()
  
  print('bessere Implementierung durch Nutzung der eingebauten Funktion sum()')
  print(sum(range(1, 11)), sum([]))
  print()
  
  # Currying (partial function application): bestimmte Argumente an eine Funktion
  # binden
  
  # mit Closure: innere Funktion, die auf lokale Namen einer äußeren Funktion
  # Bezug nimmt
  print('add3 mit Closure')
  def make_adder(n):
      def adder(x):
          #return operator.add(x, n)
          return x + n
      return adder
  
  add3 = make_adder(3)
  print(add3(1))
  
  # mit lambda: anonyme Funktion
  print('add3 mit lambda')
  #add3 = lambda x: operator.add(x, 3)
  add3 = lambda x: x + 3
  print(add3(1))
  
  # mit rufbarer Klassen-Instanz
  print('add3 mit rufbarer Klassen-Instanz')
  class make_adder_class(object):
      def __init__(self, n):
          self.n = n
      def __call__(self, x):
          return x + self.n
  
  add3 = make_adder_class(3)
  print(add3(1))
  
  # List Comprehensions
  
  print('List Comprehensions')
  freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
  print([fruit.strip() for fruit in freshfruit])
  print()
  
  vec = [2, 4, 6]
  print([3 * x for x in vec])
  print([3 * x for x in vec if x > 3])
  print([3 * x for x in vec if x < 2])
  print([(x, x ** 2) for x in vec])
  print()
  
  vec1 = [2, 4, 6]
  vec2 = [4, 3, -9]
  print([x * y for x in vec1 for y in vec2])
  print([x + y for x in vec1 for y in vec2])
  print([vec1[i] * vec2[i] for i in range(len(vec1))])
  print()
  
  # Pi auf eine steigende Zahl von Stellen runden
  print([str(round(pi, i)) for i in range(1, 10)])
  print()
  
  # Generator-Ausdrücke
  
  print('Generator-Ausdrücke')
  print()
  
  print('Summe der Quadratzahlen')
  print(sum(i ** 2 for i in range(10)))
  print()
  
  print('Skalarprodukt zweier Vektoren')
  xvec = [10, 20, 30]
  yvec = [7, 5, 3]
  print(sum(x * y for x, y in zip(xvec, yvec)))
  print()
  
  print('Tabelle der Sinus-Funktion')
  print(dict((x, sin(x * pi / 180)) for x in range(0, 31)))
  print()
  
  print('Menge der Wörter eines Textes')
  print(set(word for line in open('/etc/hosts') for word in line.split()))
  print()
  
  print('maximales Element einer Personen-Liste bestimmen')
  class Person(object):
      def __init__(self, alter, groesse):
          self.alter = alter
          self.groesse = groesse
  
      def __str__(self):
          return repr((self.alter, self.groesse))
  
  personen = [Person(39, 172), Person(88, 165), Person(15, 181), Person(88, 175)]
  print(max((person.alter, person.groesse) for person in personen))
  # alternativ nutzbar wären
  print(max([(elem.alter, elem.groesse) for elem in personen]))
  print(max(personen, key = lambda elem: (elem.alter, elem.groesse)))
  print()
  
  print('Zeichenfolge eines Strings umkehren und als Liste liefern')
  data = 'golf'
  print(list(data[i] for i in range(len(data)-1, -1, -1)))
  
  # einfachere Implementierung ohne Generator-Ausdruck
  print(list(reversed(data)))
  
  # oder mit Slicing
  print(list(data[::-1]))

16. OOP - Einstiegsbeispiel

  # Klasse zur Verwaltung von Personen
  class Person():
      # Konstruktor/Initialisierer
      def __init__(self, alter, groesse, name = None):
          self.alter = alter
          self.groesse = groesse
          self.name = name
  
      # String-Repräsentation einer Person erstellen
      def __repr__(self):
          return repr((self.alter, self.groesse, self.name))
  
      # einfache String-Repräsentation einer Person erstellen
      def __str__(self):
          return '%s/%s/%s' % (self.alter, self.groesse, self.name)
  
      # Person altern lassen, also Alter um n Jahre erhöhen
      def altern(self, n = 1):
          self.alter += n
  
      # mittels eines Dekorators eine Property mytuple erzeugen;
      # Properties implementieren das Deskriptor-Protokoll
      @property
      def mytuple(self):
          # das ist der Getter; den Namen lassen wir hier im Rückgabewert aus
          return self.alter, self.groesse
      # alternativ:
      #   def mytuple(self): return self.alter, self.groesse
      #   mytuple = property(mytuple)
  
      # einen Setter für die Property definieren
      @mytuple.setter
      def mytuple(self, tup):
          if tup[0] > 10 and tup[1] > 150:
              self.alter, self.groesse = tup[:2]
              if len(tup) > 2:
                  # wenn t einen Namen enthält, dann diesen auch setzen
                  self.name = tup[2]
  
      # einen Deleter für die Property definieren
      @mytuple.deleter
      def mytuple(self):
          self.alter = self.groesse = 0
          self.name = ''
  
  # Personenliste erstellen
  personen = [Person(39, 172, 'ABC'), Person(88, 165), Person(15, 181), Person(88, 175)]
  
  # Ausgabe der Personenliste
  print(personen)
  print()
  
  # Attribut mytuple der letzten Person der Liste löschen
  del personen[-1].mytuple
  
  # Iteration über der Personenliste und Ausgabe der einzelnen Personen
  for pers in personen:
      print(pers, '==>', repr(pers))
  print()
  
  # alle Personen altern lassen
  for pers in personen:
      pers.altern(3)
  
  # nochmal ausgeben
  print('nach dem Altern')
  print(personen)
  print()
  
  # nochmal altern lassen, diesmal funktional
  list(map(lambda x: x.altern(3), personen))
  print('nach dem 2. Altern')
  print(personen)
  print()
  
  # nochmal funktional altern lassen, diesmal mit Vorzugswert n
  list(map(Person.altern, personen))
  print('nach dem 3. Altern')
  print(personen)
  print()
  
  # Ausgabe der sortierten Personenliste
  print(sorted(personen, key = lambda pers: (pers.alter, pers.groesse)))
  print()
  
  # dito mit benannter Funktion statt einer anonymen lambda-Funktion
  def pers_key(pers):
      return pers.alter, pers.groesse
  
  print(sorted(personen, key = pers_key))
  
  # Attribute sind public, man kann von außen zugreifen
  print(personen[0].alter)
  print(personen[0].groesse)
  print(personen[0].name)
  
  p = personen[0]
  print(p.alter + p.groesse)
  
  # Nutzung der Property mit Getter
  print(p.mytuple)
  p.alter += 100
  print(p.mytuple)
  
  # Nutzung des Setters der Property
  p.mytuple = 1, 2 # wird vom Setter stillschweigend ignoriert
  print(repr(p.mytuple))
  
  p.mytuple = 11, 155
  print(repr(p.mytuple))
  
  # nochmal, aber mit Name
  p.mytuple = 11, 155, 'Pumuckl'
  print(repr(p.mytuple))
  
  for p in personen:
      print(p)
  
  print('maximales Element einer Personen-Liste bestimmen')
  print(max((person.alter, person.groesse) for person in personen))
  print()
  
  # Größe von außen ändern
  personen[1].groesse += 5
  for p in personen:
      print(p)
  print()
  
  # neues Attribut setzen
  personen[1].name2 = 'XYZ'
  for p in personen:
      print(p) # __str__() wird für die String-Darstellung gerufen
  print()
  
  # hier sieht man das neue Attribut
  for p in personen:
      print(vars(p))
  print()
  
  print('Maximum der Property mytuple')
  print(max(person.mytuple for person in personen))
  # oder:
  print(max(personen, key = lambda p: (p.alter, p.groesse)).mytuple)
  
  # das Tupel der letzten Person der Liste ändern
  personen[-1].mytuple = 110, 190
  
  # Maximum erneut ausgeben
  print('Maximum der Property mytuple nach Zuweisung')
  print(max(person.mytuple for person in personen))
  print()
  
  # nochmal alle Attribute mit vars()
  for p in personen:
      print(vars(p))

17. OOP - Klassen- und Instanzvariablen

  #!/bin/env python3
  
  # Klasse mit Klassen- und Instanzvariablen sowie Name Mangling für __name
  class mytest(object):
      # 4 Klassenvariablen erzeugen
      a = 5
      b = a + 5
      __c = 9 * a
      __d__= __c + a
      # im Klassenkörper, aber außerhalb der Funktionen ist der Klassenname
      # unbekannt; die Anweisung
      #   b = mytest.a + 5
      # funktioniert daher nicht (NameError: name 'mytest' is not defined)
  
      # Konstruktor / Initialisierer
      def __init__(self):
          # Instanzvariable x generieren
          self.x = 99
  
      # summiere die Attribute a (Klassenvariable) und x (Instanzvariable)
      def mysum(self):
          return self.a + self.x
  
      def myprint(self):
          # Zugriff auf die globalen Variablen a, b, _mytest__c, __d__
          print('globale Variablen in myprint', a, b, __c, __d__)
  
          # Bei der Implementierung einer Klasse (d.h. im Klassenkörper) erfolgt der
          # Zugriff auf deren Klassen- und Instanzvariablen sowie Methoden immer
          # qualifiziert, also durch Angabe der Instanz-Referenz (self) oder des
          # Klassennamens.
  
          # Zugriff auf die Klassenvariablen über die Instanz (self); sofern die
          # Klassenvariablen durch gleichnamige, von außen zugewiesene
          # Instanz-Variablen verdeckt werden, erfolgt der Zugriff auf die
          # Instanz-Variablen
          print('Klassen-Variablen via self in myprint', self.a, self.b, self.__c, self.__d__)
  
          # Zugriff auf die Klassenvariablen über den Klassennamen, der innerhalb
          # einer Funktion bekannt ist; gleichnamige Instanz-Variablen werden hier
          # ignoriert
          print('Klassenvariablen in myprint', mytest.a, mytest.b, mytest.__c, mytest.__d__)
  
          # Zugriff auf die Instanzvariablen x und y
          print('Instanzvariablen x, y in myprint', self.x, self.y)
  
          # Zugriff auf die Methode mysum()
          print('sum =', self.mysum())
          # diese Methode ist auch über die Klasse rufbar
          print('sum2 =', mytest.mysum(self))
  
  # 4 globale Variablen mit denselben Namen wie die Klassenvariablen anlegen
  a = 1
  b = 2
  _mytest__c = 3
  __d__ = 4
  
  # Instanz von mytest anlegen
  m = mytest()
  
  # Instanzvariable y anlegen
  m.y = 100
  
  # Instanzvariable __z anlegen
  m.__z = 200
  
  # die Instanz analysieren
  print('Instanzanalyse')
  print('m.__dict__', m.__dict__)
  print('vars(m)', vars(m))
  print('dir(m)', dir(m))
  print()
  
  # Zugriff auf die Variablen; Python kennt nur öffentliche Attribute und daher
  # keine Sprachmittel zur Steuerung der Sichtbarkeit (z.B. public, private,
  # protected, ...)
  print('m.a', m.a)
  print('m.b', m.b)
  print('m._mytest__c', m._mytest__c)
  print('m.__d__', m.__d__)
  print('m.x', m.x)
  print('m.y', m.y)
  print('m.__z', m.__z)
  m.myprint()
  
  # die Klassenvariable a mit einem neuen Wert belegen; das geht nur über die
  # Klasse und nicht über die Instanz, also nicht über m.a
  mytest.a = 1000
  print('mytest.a', mytest.a)
  
  # 2. Instanz erzeugen
  m2 = mytest()
  # darüber Zugriff auf die Klassenvariable a
  print('m2.a', m2.a)

18. OOP - Polymorphie und Vererbung

Modul BankKonto:

  #!/bin/env python3
  
  """
  OOP-Demo am Beispiel eines gebührenfreien und gebührenpflichtigen Bankkontos
  
  Idee: s. Tutorial "Programmieren lernen" von Alan Gauld
  """
  
  from decimal import Decimal
  
  class SaldoError(Exception):
      """spezielle Ausnahme für Bankkonten-Operationen"""
  
      def __init__(self, saldo):
          Exception.__init__(self, 'Entschuldigung, Sie haben nur %6.2f EUR auf Ihrem Konto' % saldo)
  
  class BankKonto(object):
      """Realisierung eines gebührenfreien Bankkontos"""
  
      def __init__(self, initialBetrag = 0):
          """Konstruktor"""
          # Saldo wird im Attribut "__saldo" gespeichert; wegen der 2 Unterstriche
          # erfolgt ein "name mangling", also eine automatische Umbenennung zu
          # "_BankKonto__saldo"
          self.__saldo = Decimal(str(initialBetrag))
          # anz_buchungen unterliegt keinem "name mangling"
          self.anz_buchungen = 0
          print('Konto erzeugt mit Saldo von %6.2f' % self.__saldo)
  
      def buchung(self, betrag):
          """eine Buchung (Ein-/Auszahlung) vornehmen"""
          betrag = Decimal(str(betrag))
          self.anz_buchungen += 1
          if betrag >= 0:
              # nichtnegativer Betrag --> Einzahlung
              self.__saldo += betrag
          else:
              # negativer Betrag --> Auszahlung
              if self.__saldo >= abs(betrag):
                  self.__saldo += betrag
              else:
                  raise SaldoError(self.__saldo)
  
      def __iadd__(self, betrag):
          """Überladung des Operators +="""
          self.buchung(betrag)
          return self
  
      def __isub__(self, betrag):
          """Überladung des Operators -="""
          return self.__iadd__(-betrag)
  
      def __str__(self):
          """Überladung von str()"""
          return '%6.2f' % self.__saldo
  
      def __repr__(self):
          """Überladung von repr()"""
          return self.__str__()
  
      def kontostand(self):
          """Abfrage Kontostand"""
          return self.__str__()
  
      def transfer(self, betrag, konto):
          """Transfer auf ein anderes Konto"""
          try:
              # Betrag vom Saldo des aktuellen Kontos subtrahieren
              self -= betrag
              # und dann zum Saldo des anderen Kontos addieren
              konto += betrag
          except SaldoError as obj:
              # wenn ein SaldoError ausgeworfen wurde, dann schlug die Subtraktion
              # fehl; sie wurde daher zusammen mit der nachfolgenden Addition nicht
              # ausgeführt
              print(obj.args[0])
  
  class KostenKonto(BankKonto):
      """von BankKonto abgeleitete Klasse für ein gebührenpflichtiges Konto"""
  
      def __init__(self, initialBetrag = 0, gebuehr = 0.05):
          """Konstruktor; überschreibt die entsprechende Methode der Basisklasse"""
          # Konstruktor der Superklasse rufen
          super(KostenKonto, self).__init__(initialBetrag)
          # die Gebühr in der Property "gebuehr" unter Verwendung der entsprechenden
          # Set-Methode speichern
          self.gebuehr = Decimal(str(gebuehr))
  
      def buchung(self, betrag):
          """erweiterte Buchungs-Methode; überschreibt die entsprechende Methode der Basisklasse"""
          print('buchung in KostenKonto')
          # Direktzugriff auf die Gebühr unter Umgehung der Get-Methode der Property
          super(KostenKonto, self).buchung(betrag - self.__gebuehr)
  
      # die Gebühr mittels geeigneter Dekoratoren (decorators) als Property definieren;
      # Zugriffe der Art obj.gebuehr nutzen dann die Property-Methoden, Zugriffe
      # der Art self.__gebuehr und obj._KostenKonto__gebuehr dagegen nicht
      #
      # Hinweis: In älterem Code wurden Properties über die eingebaute Funktion
      # property() definiert, z.B.
      #  gebuehr = property(get_geb, set_geb, del_geb, 'property gebuehr')
      #
      @property
      def gebuehr(self):
          """Implementierung der Property-Methode __get__(): Ermittlung der eingestellten Gebühr"""
          print('hole Gebühr')
          return self.__gebuehr
  
      @gebuehr.setter
      def gebuehr(self, geb):
          """Implementierung der Property-Methode __set__(): Setzen einer neuen Gebühr"""
          self.__gebuehr = Decimal(str(geb))
          print('neue Gebühr gesetzt:', geb)
  
      @gebuehr.deleter
      def gebuehr(self):
          """Implementierung der Property-Methode __delete__(): Löschen der Gebühr"""
          print('Gebühr kann nicht gelöscht werden')

Nutzung des Moduls BankKonto:

  #!/bin/env python3
  
  """
  Demo zur Nutzung der Klassen des Moduls BankKonto
  """
  
  from BankKonto import *
  
  print('erzeuge b mit Initialbetrag 500 und k mit Initialbetrag 0 und Gebühr 0.03')
  b = BankKonto(500)
  k = KostenKonto(gebuehr = 0.03)
  
  # die Kontenobjekte mit dir() und vars() analysieren
  print()
  print('dir(b)')
  print(dir(b))
  print()
  print('vars(b)')
  print(vars(b))
  print('vars(b) is b.__dict__', vars(b) is b.__dict__)
  print()
  print('dir(k)')
  print(dir(k))
  print()
  print('vars(k)')
  print(vars(k))
  print('vars(k) is k.__dict__', vars(k) is k.__dict__)
  print()
  
  print('Kontostände')
  print('b', b, b.kontostand())
  print('k', k, k.kontostand())
  print()
  
  for betrag in 100, 600:
      print('Transfer %6.2f von b nach k' % betrag)
      b.transfer(betrag, k)
      print('b, k', b, k)
      print()
  
  for i in range(2):
      print('50 abheben von b und k')
      for varname in 'b', 'k':
          try:
              var = vars()[varname]
              var -= 50
              print(varname, var)
          except SaldoError as obj:
              print('Fehlschlag:', obj.args[0])
  print()
  
  print('300 einzahlen auf k')
  k += 300
  print(k)
  print()
  
  print('b._BankKonto__saldo', b._BankKonto__saldo)
  print('k._KostenKonto__gebuehr', k._KostenKonto__gebuehr)
  print('b.anz_buchungen', b.anz_buchungen)
  print('k.gebuehr', k.gebuehr)
  k.gebuehr = 0.04
  print('k.gebuehr', k.gebuehr)
  
  del k.gebuehr

19. OOP - statische und Klassenmethoden

  #!/bin/env python3
  #
  # statische und Klassenmethoden
  
  # eine simple Zählerklasse
  class Counter(object):
      # die Schrittweite in einer Klassenvariablen ablegen
      step = 1
  
      # Klassenmethode mit Dekorator @classmethod definieren
      @classmethod
      def count(cls, x):
          # Argument 1 ist ein Klassen- und kein Instanzobjekt
          return cls.step + x
  
  class DoubleCounter(Counter):
      # gegenüber der Basisklasse Counter wird nur die Schrittweite verdoppelt
      step = 2
  
  x = DoubleCounter.count(10) # ruft Counter.count(DoubleCounter, 10) ==> 12
  print(x)
  
  # eine Klasse zur Verwaltung von Adressen
  class Address(object):
      def __init__(self, zip_code, city, street, number):
          self.zip_code = zip_code
          self.city = city
          self.street = street
          self.number = number
  
      # die String-Repräsentation der Adresse
      def __str__(self):
          return '%s %s\n%s %s' % (self.street, self.number, self.zip_code, self.city)
  
      # statische Methode (normale Funktion ohne implizites 1. Argument), die die
      # Heimatadresse zurückgibt
      @staticmethod
      def home():
          return Address(12345, 'my city', 'my street', 1)
  
      # Klassenmethode, die im Gegensatz zur statischen Methode bei einem Aufruf
      # über eine Subklasse ein Objekt der Subklasse und keines der Basisklasse
      # liefert
      @classmethod
      def home2(cls):
          return cls(12345, 'my city', 'my street', 1)
  
  # eine abgeleitete Adress-Klasse, die in __str__ die Adresse anders formatiert
  class Address2(Address):
      def __str__(self):
          return '%s %s\n%s %s' % (self.zip_code, self.city, self.street, self.number)
  
  # Aufruf der statischen und Klassenmethoden über die Klassen
  a = Address.home()
  print(type(a))
  print(a)
  print()
  
  b = Address.home2()
  print(type(b))
  print(b)
  print()
  
  c = Address2.home() # liefert Address statt Address2
  print(type(c))
  print(c)
  print()
  
  d = Address2.home2() # liefert Address2
  print(type(d))
  print(d)
  print()
  
  # Aufruf der statischen und Klassenmethoden über die Instanzen
  x = Address(45678, 'your city', 'your street', 100)
  y = Address2(45678, 'your city', 'your street', 100)
  
  print(x.home(), '\n')
  print(x.home2(), '\n')
  print(y.home(), '\n')
  print(y.home2())

20. OOP - Aufruf von Methoden der Basisklasse und MRO

  #!/bin/env python3
  #
  # Aufruf von Methoden der Basisklasse über super() und MRO
  
  # Definition einer Basisklasse
  class Base(object):
      def __init__(self):
          print('Base.__init__')
  
  # A ist eine abgeleitete Klasse
  class A(Base):
      def __init__(self):
          print('start A.__init__')
          # die Funktion __init__() der Basisklasse wird nicht automatisch
          # gerufen; der Aufruf von Methoden der Basisklasse sollte stets über
          # super() erfolgen;
          # bei Python 3 kann super() parameterlos gerufen werden, bei Python
          # benötigte super() 2 Argumente, z.B.:
          #   super(A, self).__init__()
          super().__init__()
          print('end A.__init__')
  
  # B ist eine weitere abgeleitete Klasse
  class B(Base):
      def __init__(self):
          print('start B.__init__')
          super().__init__()
          print('end B.__init__')
  
  # Mehrfachvererbung: C erbt von A und B und über beide von Base
  class C(A, B):
      def __init__(self):
          print('start C.__init__')
          # super() ist unabhängig von der Zahl der Basisklassen nur einmal zu rufen;
          # es vermeidet den Mehrfachaufruf von Basisklassen-Methoden, der bei
          # Konstrukten der Art
          #   A.__init__(self)
          #   B.__init__(self)
          # erfolgen kann, und sorgt dafür, dass die Methoden aller Basisklassen
          # genau einmal gerufen werden
          super().__init__()
          print('end C.__init__')
  
  c = C()
  # start C.__init__
  # start A.__init__
  # start B.__init__
  # Base.__init__
  # end B.__init__
  # end A.__init__
  # end C.__init__
  
  # super() stützt sich auf die MRO (method resolution order)
  print(Base.__mro__)
  # (<class '__main__.Base'>, <class 'object'>)
  
  print(A.__mro__)
  # (<class '__main__.A'>, <class '__main__.Base'>, <class 'object'>)
  
  print(B.__mro__)
  # (<class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
  
  print(C.__mro__)
  # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
  
  # A und B haben nachfolgend keine gemeinsame Basisklasse außer object
  class A(object):
      def hello(self):
          print('A.hello')
          super().hello()
  
  class B(object):
      def hello(self):
          print('B.hello')
  
  class C(A, B):
      pass
  
  # auch dann stützt sich super() auf die MRO
  c = C()
  c.hello()
  # A.hello
  # B.hello
  
  print(A.__mro__)
  # <class '__main__.A'>, <class 'object'>)
  
  print(B.__mro__)
  # (<class '__main__.B'>, <class 'object'>)
  
  print(C.__mro__)
  # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

8. Namensräume und Sichtbarkeitsbereiche

Praktisches Beispiel vor der Theorie:

  # -*- coding: utf8 -*-
  """
  Nested Scopes
  
  Ausgabe:
    inner():
    x in inner
    global x in module
    y in outer
    global set by inner()
    __main__
    -------------------------
    outer():
    x in outer
    global x in module
    y in outer
    global set by inner()
    __main__
    -------------------------
    main():
    global x in module
    global x in module
    y
    global set by inner()
    __main__
  """
  
  x = 'global x in module'
  y = 'y'
  z = 'z'
    
  def outer():
    """äußere Funktion"""
    x = 'x in outer'
    y = 'y in outer'
  
    def inner():
      """innere Funktion"""
      global z
      x = 'x in inner'
      z = 'global set by inner()'
      print 'inner():'
      print x
      print globals()['x']
      print y
      print z
      print __name__
      print '-------------------------'
  
    inner()
    print 'outer():'
    print x
    print globals()['x']
    print y
    print z
    print __name__
    print '-------------------------'
  
  outer()
  print 'main():'
  print x
  print globals()['x']
  print y
  print z
  print __name__

Namensraum (namespace)

Sichtbarkeitsbereich (scope)


9. Funktionen


10. Objekt-Orientierung


11. Sammlung von Code-Beispielen


12. Links und Literatur zu Python

Einstiegs-Punkte:

Python-Implementierungen:

Sammlungen von Python-Software:

freie Editoren, Shells und integrierte Entwicklungsumgebungen (IDEs)

Python-Bücher im Bestand der UB Chemnitz:

Links zu Kursen, Open Books, Tutorials, FAQs:

Web-Seiten zu einigen Python-Büchern:


Holger Trapp

letzte Modifikation: 29.03.2023