print 10 + 20 + 30 + \ 40
print [1, 2, 3, 4]
print 1; a = 99; print a; a += 7; print a
for i in range(4): print i; print i+10 print for i in range(4): print i print i + 10 # falsch: # a = 5; for i in range(3): print i
and or notstatt
&& || !
if ausdruck: anweisungen elif ausdruck: anweisungen elif ausdruck: anweisungen ... else: anweisungen
ausdruck1 if bedingung else ausdruck2
ausdruck1 and ausdruck2 ausdruck1 or ausdruck2
while ausdruck: anweisungen else: anweisungen for element in sequenz: anweisungen else: anweisungen
Schleifensteuerung mit break und continue
try ... except ... else try ... finally raise pass
ab Python 2.5 zusätzlich:
try ... except ... finally
neue Anweisung ab Python 2.5:
with ausdruck [as varname]: anweisungen
Der Code-Block wird hier unter Steuerung eines Kontext-Managers ausgeführt, der sichert, dass am Anfang und/oder Ende des Blocks bestimmte Aktionen immer ausgeführt werden. So werden z.B. Files auch nach dem Auftreten einer Ausnahme generell geschlossen.
Anwendungsbeispiel:
# in Python 2.5 ist folgender Import nötig, weil "with" erst nach Python 2.5 zum # Standard-Sprachumfang gehört from __future__ import with_statement with open('/etc/hosts') as f: for line in f: print line,
Detaillierte Erläuterung: CODE/with.py
import sys from math import pi, sinh from math import pi as PI from socket import *
def myfunc():
print 'myfunc'
if __name__ == '__main__':
# wurde als Programm gerufen
myfunc()
# -*- coding: utf8 -*- # Mail-Versand mit dem Standard-Modul smtplib # Module smtplib und sys importieren import smtplib, sys # MIMEText aus dem Modul text des Sub-Pakets email.mime des Pakets email # importieren; # im Dateisystem z.B. unter /usr/lib64/python2.7/email/mime/text.py from email.mime.text import MIMEText # unser ASCII-Mailtext mail_text = ''' Hello friends, this is a simple ASCII mail. ''' # eine MIMEText-Nachricht erstellen msg = MIMEText(mail_text) # Header setzen msg['Subject'] = 'test mail' me = msg['From'] = 'otto@hrz.tu-chemnitz.de' you = msg['To'] = 'hot@hrz.tu-chemnitz.de' # Mail senden s = smtplib.SMTP() if len(sys.argv) > 1 and sys.argv[1] == 'd': # Kommandozeilenargument 1 lautet "d", daher Debug einschalten s.set_debuglevel(1) #s.connect(host = 'mailbox.hrz.tu-chemnitz.de') s.connect() s.sendmail(me, [you], msg.as_string()) s.close()
erfolgreich importierte Module können (in begrenztem Umfang) erneut geladen werden: Python 2 bietet dafür die eingebaute Funktion reload() und Python 3 die Funktion imp.reload(); David Beazley warnt in seinem Buch "Python Essential Reference", dass diese Funktion nie wirklich sicher funktionierte und rät daher zum Neustart des Python-Interpreters
Modul-Suchpfad:
>>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] >>> dir(__builtins__) ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', ... 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
#!/bin/env python3 class eins(object): a = 99 class zwei(eins): b = 100 class drei(zwei): pass e = eins() z = zwei() d = drei() # dir() ermittelt auch die Attribute der Basisklassen print(dir(e)) # ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', # '__getattribute__', '__hash__', '__init__', '__module__', '__new__', # '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', # '__str__', '__subclasshook__', '__weakref__', 'a'] print(dir(z)) # ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', # '__getattribute__', '__hash__', '__init__', '__module__', '__new__', # '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', # '__str__', '__subclasshook__', '__weakref__', 'a', 'b'] print(dir(d)) # ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', # '__getattribute__', '__hash__', '__init__', '__module__', '__new__', # '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', # '__str__', '__subclasshook__', '__weakref__', 'a', 'b'] d.c = 200 print(dir(d)) # ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', # '__getattribute__', '__hash__', '__init__', '__module__', '__new__', # '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', # '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'c'] # im Vergleich dazu vars(); hier wird nur das Instanz-Dictionary betrachtet print(vars(d)) # {'c': 200} print(vars(z)) # {} print(vars(e)) # {}
Anmerkung:
Funktionen sind (wie alle Python-Objekte) first-class objects und können somit als Argumente und Rückkehrwerte von Funktionen auftreten.
Details unterscheiden sich bei Python 2 und 3: CODE/python_classes.py
a = True b = 1 # Operatoren zum # Test auf wertmäßige Gleichheit: ==, != # Test auf Identität : is, is not print a == b # True print a != b # False print a is b # False print a is not b # True
def outer(): def inner(): print(a) a = 9 inner() # "del" ist hier ein Syntaxfehler, da die Variable a in der inneren Funktion # referenziert wird # # SyntaxError: can not delete variable 'a' referenced in nested scope del aDiese Einschränkung wurde allerdings mit Python 3.2 aufgehoben, so dass dort die del-Anweisung zulässig ist.
# -*- coding: utf8 -*- def outer(): def inner(): def innermost(): nonlocal a, b print(a) # Ausgabe: 30 print(b) # Ausgabe: 20 a = 40 # ändert 'a' von inner() b = 50 # ändert 'b' von outer() a = 30 innermost() print(a) # Ausgabe: 40 print(b) # Ausgabe: 50 a = 10 b = 20 inner() print(a) # Ausgabe: 10 print(b) # Ausgabe: 50 outer()
x, y = 1, 2 x, y = y, x print x, ygenutztes allgemeines Konzept: tuple packing und sequence unpacking bzw. iterable unpacking
items = range(5) a, *rest = items # a = 0, rest = [1, 2, 3, 4] a, *rest, b = items # a = 0, rest = [1, 2, 3], b = 4 *rest, b = items # rest = [0, 1, 2, 3], b = 4
a, b, c = 1, 5, 3 print a < b > c x = a == b == c print x a = b = c = 0 print a == b == c
Ausdruck | Wert |
---|---|
x or y | wenn x falsch, dann y, sonst x |
x and y | wenn x falsch, dann x, sonst y |
not x | wenn x falsch, dann True, sonst False |
# -*- coding: utf8 -*- import pprint # einfache List Comprehension: funktionaler Ausdruck in eckigen Klammern gerade_zahlen = [z for z in range(20) if not z % 2] print gerade_zahlen # verschachtelte List Comprehension: # - line iteriert über den Zeilen der Datei /etc/hosts (durchläuft diese # schrittweise) # - word iteriert jeweils über den Wörtern der aktuellen Zeile # - die innere List Comprehension erzeugt die Liste der in Großschreibung # konvertierten Wörter einer Zeile # - die äußere List Comprehension invertiert diese Wort-Liste und fügt die # Wörter der invertierten Liste wieder zu einem String zusammen lines = [' '.join(reversed([word.upper() for word in line.split()])) for line in open('/etc/hosts')] pprint.pprint(lines) # Generatorausdruck: funktionaler Ausdruck in runden Klammern; es werden # dieselben funktionalen Ausdrücke wie bei der List Comprehension unterstützt # Sequence Unpacking der durch eine Generatorfunktion erzeugten Teile eines # Strings; die Anzahl der generierten Elemente der Sequenz muss bei Python 2 # mit der Anzahl der Variablen auf der linken Seite der Zuweisung # übereinstimmen; bei Python 3 könnte man auch eine Wildcard-Variable nutzen datum = '5.8.2012' tag, monat, jahr = (int(part) for part in datum.split('.')) print tag, monat, jahr, tag + monat + jahr print type(tag) == type(monat) == type(jahr) == int
Beispiele:
# -*- coding: utf8 -*- # Menge der ganzen Zahlen von 0 bis 9 s = set(range(10)) # Ausgabe aller Elemente der Menge mittels for for elem in s: print elem # nur das erste Element ausgeben for elem in s: print elem break # erzeuge explizit einen Iterator für die Menge und hole das erste # Element print next(iter(s)) # alternativ: Umwandlung der Menge in eine Liste und Entnahme des ersten # Elements durch Slicing (ist bei langen Sequenzen weniger effizient, da über # die komplette Sequenz iteriert und die gesamte Liste aufgebaut werden muss) print list(s)[0]
Umwandlung DOS- in Unix-Text-File:
python3 -c "for line in open('/etc/hosts'): print(line.rstrip('\r\n'))"
oder
python3 -c "for line in open('/etc/hosts'): print(line, end = '')"
Beispiel:
# -*- coding: utf8 -*- def my_generator(n = 5): """generiert die Folge der ersten n natürlichen Zahlen""" assert n > 0, 'n muss größer 0 sein' i = 1 while i <= n: # yield statt return yield i i += 1 for j in my_generator(8): print(j) print('') for j in my_generator(): print(j) print('') try: for j in my_generator(0): print(j) except AssertionError as e: print('Ausnahme: %s/%s' % (type(e), e)) # ohne for, explizit mit next() und Abfangen der StopIteration generator = my_generator(5) while True: try: print(next(generator)) except StopIteration: break print('Generator erschöpft') # man kann die StopIteration vermeiden, indem man bei next() ein # Default-Argument angibt generator = my_generator(3) while True: item = next(generator, None) if item is None: break print(item)
Beispiel:
# -*- coding: utf8 -*- import sys def coroutine(): """Couroutine, die via yield Werte liefert und übernimmt""" print('bereit') value = 5 while True: # yield liefert hier einen Wert und liest einen zurück, der per send() # oder next() übergeben wird, wobei next() immer None übergibt; # bei der Verwendung des Rückgabewertes von yield muss die # yield-Anweisung geklammert werden, es sei denn, sie ist der einzige # Operand auf der rechten Seite einer Zuweisung; nachfolgend könnten die # Klammern daher entfallen value = (yield value if value > 20 else value + 1) # Generator/Couroutine erzeugen c = coroutine() # Generator/Coroutine starten, also bis zum ersten yield ausführen if len(sys.argv) == 1: # Variante 1 mit next(): # next() existiert bei Python 2 und 3 gleichermaßen und ruft die # Iterator-Methode c.next() bei Python 2 und c.__next__() bei Python 3 print('next empfing %s' % next(c)) else: # Variante 2 mit send(None) statt next() print('send(None) empfing %s' % c.send(None)) # nun mit send() Werte senden und yield-Resultate zurücklesen for i in 3, 17, 4, 81: print('send(%s) empfing %s' % (i, c.send(i)))
# -*- coding: utf8 -*- # Liste der Zahlen 0 bis 9 for i in range(10): print i, print # Liste der Zahlen von 1 bis 14 mit Abstand 3: 1, 4, 7, 10, 13 for i in range(1, 15, 3): print i, print # mit xrange() statt range() for i in xrange(10000): if i > 10: # auf Grund des zeitigen Schleifenabbruchs ist xrange() effektiver, weil es # im Gegensatz zu range() nur die wirklich benötigten Zahlen der Folge und # keine Liste mit 10000 Elementen generiert break print i, print # xrange() akzeptiert dieselben Argumente wie range() for i in xrange(1, 15, 3): print i, print
ternärer Operator existiert vor Python 2.5 nicht
Operator-Syntax:
Alternativ-Notation, die auch vor Python 2.5 nutzbar ist:
# statt # a ? b : c # bei Python auch vor Version 2.5 nutzbare Alternative: # (a and [b] or [c])[0] # # Achtung: # a and b or c # funktioniert nicht, da generell c geliefert wird, wenn b falsch ist. Durch # den Trick mit der Liste klappt es, weil [b] auch dann wahr ist, wenn b selbst # falsch ist. import sys a = sys.argv[1] b = sys.argv[2] c = sys.argv[3] print (a and [b] or [c])[0] # statt der Listen könnte man auch Tupel verwenden print (a and (b,) or (c,))[0]
ab Python 2.5 nutzbare Notation:
# statt # a ? b : c # ab Python 2.5: # b if a else c import sys a = sys.argv[1] b = sys.argv[2] c = sys.argv[3] print b if a else c
Index:
#!/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)
#!/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)
#!/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)
#!/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
#!/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
# 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]
#!/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
#!/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
# -*- 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
#!/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]))]
# -*- 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
#!/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))
#!/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)
#!/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
#!/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]))
# 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))
#!/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)
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
#!/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())
#!/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'>)
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__
# spezielle Sichtbarkeitsregeln im Klassen-Körper a = 'global a' class X(): # außerhalb von Methoden erfolgt der Zugriff auf Attribute der Klasse über # einfache Namen; in Methoden sind dagegen für Klassen- und Instanz-Attribute # immer voll qualifizierte Namen zu verwenden a = 'a in class' b = a.replace('a ', 'b ') # X.a statt a ist nicht zulässig, da X nicht sichtbar ist # # NameError: name 'X' is not defined def hello(self): print('hello ', self) def print_vars(self, my = a): # hier ist X.a als a sichtbar print(my) # a in class try: hello(1) # hello() ist nicht sichtbar except Exception as e: print(repr(e)) # NameError("name 'hello' is not defined",) X.hello(2) # X.hello() ist sichtbar print(self.a) # a in class print(X.a) # a in class print(a) # global a print(self.b) # b in class print(b) # NameError: name 'b' is not defined # im Klassenkörper ist hello() sichtbar hello(3) x = X() x.print_vars()
#!/usr/bin/env python3 # -*- coding: utf8 -*- """ Demo für die Mixin-Nutzung: - SocketServer stellt ForkingMixIn und ThreadingMixIn bereit - wir nutzen hier das ForkingMixIn beim SimpleXMLRPCServer """ from __future__ import print_function import sys try: from xmlrpc.server import SimpleXMLRPCServer # Python 3 from socketserver import ForkingMixIn py3 = True except ImportError: # Python 2 from SimpleXMLRPCServer import SimpleXMLRPCServer from SocketServer import ForkingMixIn py3 = False class MyXMLRPCServer(ForkingMixIn, SimpleXMLRPCServer): def verify_request(self, request, client_address): host, port = client_address if host != '127.0.0.1': print('invalid client', host, file = sys.stderr) return False if py3: return super().verify_request(request, client_address) # MyXMLRPCServer ist wegen seiner Basisklassen bei Python 2 eine old-style # class; daher funktioniert super() nicht und wir referenzieren die # Basisklasse über deren Namen und müssen den Parameter self übergeben return SimpleXMLRPCServer.verify_request(self, request, client_address) # vom XML-RPC-Server bereitgestellte Additions-Funktion def add(x, y): return x + y server = MyXMLRPCServer(('', 45000)) server.register_function(add) server.serve_forever()Hier ein kleiner XML-RPC-Klient:
#!/bin/env python3 import xmlrpc.client, sys # bei Python 2 ist xmlrpclib zu importieren host = sys.argv[1] if len(sys.argv) > 1 else 'localhost' s = xmlrpc.client.ServerProxy('http://%s:45000' % host) print(s.add(3, 4))
>>> class A(int, list): pass ... Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: multiple bases have instance lay-out conflict # bei Python 2: # # ... # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # TypeError: Error when calling the metaclass bases # multiple bases have instance lay-out conflict
#!/bin/env python3 """ Nutzung einer simplen Metaklasse """ # eigene Metaklasse als Unterklasse der Standard-Metaklasse type definieren und # die Funktion __str__ überschreiben class MyMeta(type): def __str__(cls): return "Beautiful class '%s'" % cls.__name__ # die Klasse MyClass erzeugen, die MyMeta als Metaklasse nutzt class MyClass(metaclass = MyMeta): pass # Bei Python 2 kann man die Metaklasse über das Klassenattribut oder über die # globale Variable __metaclass__ zuweisen: # # class MyClass(object): # # object kann hier entfallen: class MyClass(): # # # die Metaklasse zuweisen # __metaclass__ = MyMeta # die String-Darstellung von MyClass ausgeben, also # Beautiful class 'MyClass' print(MyClass) x = MyClass() print(type(x)) print(x.__class__) # ohne explizite Angabe der Metaklasse wird als Metaklasse der Typ der ersten # Basisklasse verwendet, sofern eine solche angegeben wurde class MySubClass(MyClass): # MySubClass bekommt die Metaklasse von MyClass pass y = MySubClass() print(type(y)) print(type(type(y)))
Beispiel:
#!/bin/env python3 """ name mangling """ class C(object): # Konstruktor der Klasse C, der dem Attribut __x einen Wert zuweist; dieses # Attribut wird automatisch in _C__x umbenannt def __init__(self): self.__x = 99 # Objekt der Klasse C anlegen o = C() # Wert von __x ausgeben print(o._C__x) # OK try: print(o.__x) # Ausnahme: 'C' object has no attribute '__x' except AttributeError as obj: print(obj) o.__x = 88 # legt nun o.__x an print(o.__x) # OK
Beispiel:
#!/bin/env python3 """ dynamische Polymorphie; späte Bindung """ class C(object): # Konstruktor, der die Attribute a und b füllt def __init__(self): self.a = 'a' self.b = 'b' # Methode zur Ausgabe von Attribut a def ma(self): print(self.a) # Methode zur Ausgabe von Attribut b def mb(self): print(self.b) # Objekt c der Klasse C anlegen c = C() # eine neue Methode mc des Objekts c erzeugen, die dessen Methode ma # entspricht c.mc = c.ma # über mc wird hier de facto ma gerufen; diese Entscheidung wird erst hier # dynamisch getroffen c.mc() # nun wird mb an mc zugewiesen c.mc = c.mb # über mc wird nun de facto mb gerufen c.mc()
Beispiel:
#!/usr/bin/env python # -*- coding: utf8 -*- """ gebundene und ungebundene Methoden """ from __future__ import print_function import types liste = [1, 2, 3] # die Methode "append" gebunden und ungebunden bereitstellen gebunden = liste.append ungebunden = liste.__class__.append # entspricht: list.append print(gebunden) # <built-in method append of list object at 0x...> print(ungebunden) # <method 'append' of 'list' objects> # Aufruf der gebundenen Methode gebunden(4) # eine 4 an die Liste anhängen print(liste) # [1, 2, 3, 4] # Aufruf der ungebundenen Methode ungebunden(liste, 5) # eine 5 an die Liste anhängen print(liste) # [1, 2, 3, 4, 5] # Benutzerdefierte Funktionen besitzen eine Methode __get__() und sind daher # non-overriding descriptors. Die Deskriptor-Methode __get__() wird beim # Zugriff auf ein entsprechendes Attribut eines Objekts (z.B. liste.append) # gerufen und liefert eine gebundene Methode. Man kann __get__() auch direkt # rufen: def f(): pass print(f.__get__('')) # Python 2: <bound method ?.f of ''> # Python 3: <bound method f of ''> # eigene Listen-Klasse vom eingebauten Typ "list" ableiten; hier sieht man die # "bound method" bzw. "unbound method" explizit class mylist(list): # Methode append() wird überschrieben def append(self, x): print('appending', x) # Methode append() der Basis- bzw. Superklasse wird gerufen super(mylist, self).append(x) # bei Python 3 kann man super() ohne Argumente nutzen: # super().append(x) # # ohne das empfohlene super() ginge es auch so: # list.append(self, x) # eine Instanz von "mylist" kreieren ml = mylist((1, 2, 3)) print(ml) # die Methode 'append' wieder gebunden und ungebunden bereitstellen gebunden = ml.append ungebunden = mylist.append print(gebunden) # <bound method mylist.append of [1, 2, 3]> print(ungebunden) # Python 2: <unbound method mylist.append> # Python 3: <function mylist.append at 0x...> print(type(gebunden)) # Python 2: <type 'instancemethod'> # Python 3: <class 'method'> print(type(ungebunden)) # Python 2: <type 'instancemethod'> # Python 3: <class 'function'> print(isinstance(gebunden, types.MethodType)) # True print(isinstance(ungebunden, types.MethodType)) # Python 2: True # Python 3: False # Aufruf der gebundenen Methode gebunden(4) # appending 4 print(ml) # [1, 2, 3, 4] ml.append(5) # appending 5 print(ml) # [1, 2, 3, 4, 5] # Aufruf der ungebundenen Methode ungebunden(ml, 6) # appending 6 print(ml) # [1, 2, 3, 4, 5, 6] mylist.append(ml, 7) # appending 7 print(ml) # [1, 2, 3, 4, 5, 6, 7] # eine Standard-Liste weiß von unserem mylist.append() nichts l = list((1, 2, 3)) l.append(99) print(l) # [1, 2, 3, 99] # Bei nutzerdefinierten Klassen werden die gebundenen und bei Python 2 auch die # ungebunden Methoden durch ein Objekt vom Typ types.MethodType repräsentiert. # Das ist ein dünner Wrapper um ein normales Funktionsobjekt, der diese # Attribute definiert: # # m.__doc__ Dokumentations-String # m.__name__ Name der Methode # m.__class__ Klasse, in der die Methode definiert wurde # m.__func__ Funktionsobjekt, das die Methode implementiert # m.__self__ Instanz, an die die Methode gebunden ist (None im Falle von # ungebundenen Methoden) # # Ab Python 3 fehlt bei ungebundenen Methoden der Wrapper vom Typ # types.MethodType. Es handelt sich hier um das reine Funktionsobjekt, das die # Methode implementiert. Außerdem erfolgt für den Parameter self keine # Typprüfung mehr, während Python 2 einen TypeError wirft, wenn man der # ungebundenen Methode ein Objekt übergibt, das keine Instanz der Klasse ist, # für die man die ungebundene Methode ruft. class liste(): def append(self): 'special append' pass l = liste() gebunden = l.append ungebunden = liste.append for obj in gebunden, ungebunden: # __name__ taucht bei dir() hier nicht auf, wir ergänzen es daher attribute = dir(obj) + ['__name__'] for name in '__doc__', '__name__', '__class__', '__func__', '__self__': if name in attribute: attr = getattr(obj, name) print(name, attr, type(attr)) print() # Python 2: # # __doc__ special append <type 'str'> # __name__ append <type 'str'> # __class__ <type 'instancemethod'> <type 'type'> # __func__ <function append at 0x...> <type 'function'> # __self__ <__main__.liste instance at 0x...> <type 'instance'> # # __doc__ special append <type 'str'> # __name__ append <type 'str'> # __class__ <type 'instancemethod'> <type 'type'> # __func__ <function append at 0x...> <type 'function'> # __self__ None <type 'NoneType'> # # Python 3: # # __doc__ special append <class 'str'> # __name__ append <class 'str'> # __class__ <class 'method'> <class 'type'> # __func__ <function liste.append at 0x...> <class 'function'> # __self__ <__main__.liste object at 0x...> <class '__main__.liste'> # # __doc__ special append <class 'str'> # __name__ append <class 'str'> # __class__ <class 'function'> <class 'type'>
Beispiel für eine statische Funktion:
#! /bin/env python3 """ Button-Listen mit Tkinter (Python-Interface für das Toolkit Tk) """ from tkinter import * class Button_Row(object): """Realisierung einer Button-Zeile in einem eigenen Fenster""" # 2 Klassen-Variablen: # - aktuelle Window-Nummer # - Tk-Instanz window_nr = 1 tk = None def button_print(self, win_nr, bnr): """Window-Nummer und Text von Button i der Button-Liste ausgeben""" print('Window %s, %d, %s' % (repr(self), win_nr, self.button_list[bnr]['text'])) # wir deklarieren "quit()" über den Decorator "@staticmethod" als statische # Methode, so dass sie kein implizites erstes Argument "self" hat und für # Instanzen sowie die Klasse gleichermaßen gerufen werden kann; Decorators # existieren seit Python 2.4 und beziehen sich auf die jeweils unmittelbar # folgende Funktionsdefinition @staticmethod def quit(): """Programm beenden""" print('Bye') exit(0) def __init__(self, n): """Button-Liste aufbauen; n ist die Anzahl der Buttons""" if not self.tk: # falls noch keine Tk-Instanz existiert, ist eine zu erzeugen (also bei # Fenster 1); der lesende Zugriff auf die Klassenvariable tk kann über # über die Instanz (mit self.tk) oder über die Klasse (mit Button_Row.tk) # erfolgen, der schreibende Zugriff muss über die Klasse erfolgen Button_Row.tk = self.window = Tk() else: # ab Fenster 2 erzeugen wir jeweils ein neues Toplevel-Fenster self.window = Toplevel() # einen Titel-String an den Window-Manager übergeben self.window.wm_title('Window %s' % self.window_nr) # die Button-Liste initialisieren self.button_list = [] if n < 1: # mindestens einen Button legen wir an n = 1 # schrittweise die einzelnen Buttons erzeugen for bnr in range(n): b = Button(self.window) if bnr < n-1: # normalen Text-Button erzeugen b['text'] = 3 * str(bnr) b['fg'] = 'red' # durch eine Lambda-Funktion mit einem die Window- und die # Button-Nummer enthaltenden Default-Wert können wir die aktuelle # Button-Nummer an die Funktion button_print übergeben, da unsere # Lambda-Funktion parameterlos aufgerufen werden kann print('__init__, %s, %s, %s' % (repr(self), self.window_nr, bnr)) b['command'] = lambda args = (self.window_nr, bnr): self.button_print(*args) else: # Quit-Button erzeugen b['text'] = 'QUIT' b['fg'] = 'green' b['command'] = self.quit # den aktuellen Button packen b.pack({'side': 'left'}) # und an die Liste anhängen self.button_list.append(b) # Window-Nummer in der Klassenvariablen "Button_Row.window_nr" erhöhen Button_Row.window_nr += 1 # die Anweisung # self.window_nr += 1 # würde eine Instanzvariable mit dem Wert 2 anlegen und die Klassenvariable # unverändert beim Wert 1 belassen # 3 Button-Leisten erzeugen und dann die Steuerung an die Mainloop von Tk # übergeben Button_Row(6) Button_Row(7) Button_Row(4) Button_Row.tk.mainloop()
für den Attribut-Zugriff werden spezielle Methoden gerufen:
Methode | Erläuterung |
---|---|
__getattribute__(self, name) | Rückgabe des Attributs self.name, wobei die Standard-Implementierung dieser Methode das Attribut zunächst unter den Properties bzw. Overriding-Deskriptoren der Klasse, danach im Dictionary __dict__ der Instanz und anschließend rekursiv in den Dictionaries der Klasse sowie aller Basisklassen sucht (der erste Treffer wird verwendet); Details folgen unten |
__getattr__(self, name) | Rückgabe des Attributs self.name, falls es beim normalen Zugriff über __getattribute__() nicht gefunden wurde; Standard-Verhalten von __getattr__() ist der Auswurf der Ausnahme AttributeError |
__setattr__(self, name, wert) | setzt für das Attribut self.name den Wert wert |
__delattr__(self, name) | löscht das Attribut self.name |
Sofern es sich bei einem Attribut um ein Deskriptor-Objekt handelt, werden dessen spezielle Methoden __get__(), __set__() und __delete__() gerufen.
Hier nun die Details zu den Regeln für den Attributzugriff bei der typischerweise verwendeten Standard-Implementierung von __getattribute__() und __setattr__():
Im Gegensatz zum Auslesen von Attributwerten, wobei die o.g. lookup procedures Anwendung finden, gelten beim Setzen (Binden) eines Attributs kaum besondere Regeln. Im Standardfall wird im Dictionary __dict__ der betreffenden Instanz oder Klasse der Eintrag mit dem Schlüssel name angelegt oder modifiziert, also objekt.__dict__['name'] . Lediglich Overriding-Deskriptoren verändern dieses Verhalten, indem deren Methode __set__ gerufen wird. Die Zuweisung objekt.name = wert entspricht dann dem Aufruf type(od).__set__(od, objekt, wert).
Beispiel:
#!/bin/env python3 """ Demo des Zugriffs auf Objekt-Attribute inkl. Properties und Deskriptoren """ import sys # Verbose-Modus einschalten? verbose = len(sys.argv) > 1 and sys.argv[1] == 'v' # Definition einer eigenen Property-Klasse, die das Deskriptor-Protokoll # implementiert, also mindestens eine der folgenden drei Methoden: # # __get__(self, instanz, klasse) # liefert den Attribut-Wert oder generiert die Ausnahme AttributeError # # __set__(self, instanz, wert) # setzt einen Wert für das Attribut # # __delete__(self, instanz) # löscht das Attribut # class myproperty(object): def __init__(self, fget, fset, fdel): self.fget = fget self.fset = fset self.fdel = fdel def __get__(self, instanz, klasse): if verbose: print(' myproperty __get__ %s, %s, %s' % (repr(self), repr(instanz), repr(klasse))) return self.fget(instanz) def __set__(self, instanz, wert): if verbose: print(' myproperty __set__ %s, %s, %s' % (repr(self), repr(instanz), wert)) return self.fset(instanz, wert) def __delete__(self, instanz): if verbose: print(' myproperty __delete__ %s, %s' % (repr(self), repr(instanz))) return self.fdel(instanz) # Eigene Property-Klassen sind meist entbehrlich, da man die durch den # Dekorator @property (inkl. @property_name.setter und property_name.deleter) # bzw. die eingebaute Funktion "property()" erzeugten Properties nutzen kann. # eine Klasse mit einem Property-Attribut, das ein Deskriptor-Objekt der # Property-Klasse "myproperty" ist; die Definition solcher Property-Attribute # ist nur auf der Klassen-Ebene, nicht aber auf der Instanz-Ebene zulässig class C(object): def __init__(self): if verbose: print(' __init__ %s' % repr(self)) self.a = 99 def __getattr__(self, name): if verbose: print(' __getattr__ %s, %s' % (repr(self), name)) return '%s existiert nicht!!' % name if verbose: def __getattribute__(self, name): print(' __getattribute__ %s, %s' % (repr(self), name)) return super(C, self).__getattribute__(name) def __setattr__(self, name, wert): print(' __setattr__ %s, %s, %s' % (repr(self), name, wert)) return super(C, self).__setattr__(name, wert) def __delattr__(self, name): print(' __delattr__ %s, %s' % (repr(self), name)) return super(C, self).__delattr__(name) # Funktionen, die vom Deskriptor-Objekt gerufen werden; # sie verwalten den Wert der Property "p" im Instanz-Attribut "__p", das # wegen des Präfixes '__' als '_C__p' gespeichert wird def myget(self): if verbose: print(' myget %s' % repr(self)) return '*** %s' % self.__p def myset(self, wert): if verbose: print(' myset %s, %s' % (repr(self), wert)) self.__p = 2 * wert def mydel(self): if verbose: print(' mydel %s' % repr(self)) del self.__p # Property-Attribut p als Deskriptor-Objekt ausbilden p = myproperty(myget, myset, mydel) # Instanz "c" der Klasse "C" erzeugen c = C() print(c.a) # ruft c.__getattribute__('a') print(c.b) # ruft c.__getattribute__('b'); # da 'b' nicht existiert, folgt nun # c.__getattr__('b') c.a = 100 # ruft c.__setattr__('a', 100) c.b = 101 # ruft c.__setattr__('b', 101) print(c.a) # ruft c.__getattribute__('a') print(c.b) # ruft c.__getattribute__('b') del c.b # ruft c.__delattr__('b') # beim Attribut-Zugriff auf das Deskriptor-Objekt werden die speziellen # Deskriptor-Methoden verwendet c.p = 15 # ruft c.myset(15) print(c.p) # ruft c.myget() del c.p # ruft c.mydel() print(c.p) # ruft c.myget() # interne Umsetzung der zuvor gezeigten 4 Zugriffe: C.__dict__['p'].__set__(c, 15) print(C.__dict__['p'].__get__(c, C)) C.__dict__['p'].__delete__(c) print(C.__dict__['p'].__get__(c, C))
Wie oben beschrieben, muss man bei den Deskriptoren zwischen overriding (implementieren __get__() und __set__()) und nonoverriding descriptors (implementieren nur __get__(), aber kein __set__()) unterscheiden:
#!/bin/env python3 """ Wirkungsweise von overriding/nonoverriding descriptors und properties """ # nonoverriding descriptor: nur __get__, aber kein __set__ wird implementiert class non_od(object): def __get__(self, instance, cls): print('get in non_od: %s, %s, %s' % (self, instance, cls)) return 1000 # overriding descriptor: __set__ wird auch implementiert class od(object): def __get__(self, instance, cls): print('get in od: %s, %s, %s' % (self, instance, cls)) return 1000 def __set__(self, instance, value): print('set in od: %s' % value) # Test-Klasse, die beide Arten von Deskriptoren sowie eine Property nutzt class test_descr(object): a = non_od() b = od() # eine Property wirkt wie ein overriding descriptor, auch wenn auf die # Defintion eines Setters verzichtet wird @property def p(self): return 101 # Test-Objekt erzeugen t = test_descr() print('t = %s' % t) print('t.__dict__ = %s' % t.__dict__) # der nonoverriding descriptor wirkt hier, weil es keine Instanzvariable a gibt print('t.a = %s' % t.a) # Instanzvariable a anlegen t.a = 80 print('t.__dict__ = %s' % t.__dict__) # die Instanzvariable a hat Vorrang vor dem nonoverriding descriptor print('t.a = %s' % t.a) print('===') # versuche, die Instanzvariable p zu setzen; dies scheitert, weil die Property # p keinen Setter definiert try: t.p = 200 except Exception as e: print('Zuweisung an t.p scheiterte: %s' % e) # über __dict__ kann man p setzen, da hier der Setter keine Rolle spielt t.__dict__['p'] = 200 print('t.__dict__ = %s' % t.__dict__) # trotz der existierenden Instanzvariablen p wird der Getter der Property # aufgerufen print('t.p = %s' % t.p) # einen Setter für Property p definieren @test_descr.p.setter def mysetter(self, wert): print('mysetter: setze p=%s in %s' % (wert, self.__dict__)) self.__dict__['p'] = wert # den neuen Setter aktivieren test_descr.p = mysetter # einen Wert über den Setter zuweisen t.p = 300 print('t.__dict__ = %s' % t.__dict__) # einen Wert direkt über __dict__ zuweisen t.__dict__['p'] = 400 print('t.__dict__ = %s' % t.__dict__) # dennoch wirkt beim Lesen wieder der Getter der Property print('t.p = %s' % t.p) print('===') # die Zuweisung an b wird vom overriding descriptor abgefangen und ist daher # wirkungslos t.b = 90 print('t.__dict__ = %s' % t.__dict__) print('t.b = %s' % t.b) print('===') # auch eine Zuweisung an b über das Instanz-Dictionary setzt den overriding # descriptor beim Auslesen nicht außer Kraft t.__dict__['b'] = 100 print('dict = %s' % t.__dict__) print('t.b = %s' % t.b) print('===') # Löschung des overriding descriptors in der Klasse del test_descr.b # nun wird die Instanzvariable b gesetzt und wirkt t.b = 110 print('t.__dict__ = %s' % t.__dict__) print('t.b = %s' % t.b) print('===') # der overriding descriptor wird wieder aktiviert test_descr.b = od() # Zuweisungen an b werden daher ignoriert t.b = 200 print('t.__dict__ = %s' % t.__dict__) # der overriding descriptor wirkt erneut und kommt auch beim Auslesen von t.b # zum Tragen, wogegen ein nonoverriding descriptor hier keine Wirkung hätte, # da b im Instanz-Dictionary von t vorkommt print('t.b = %s' % t.b)
Hier findet man viele Links und Online-Dokumentationen, u.a. das Python-Tutorial, eine (teilweise etwas schwer zu lesende) formale Sprachbeschreibung sowie eine umfangreiche Dokumentation der Module, die man als Programmierer immer zur Hand haben sollte.
eine sehr nützliche, umfangreiche Kurzreferenz für Programmierer (bis Python 2.7, kein Python 3)
ein kurzer Rundgang zum Kennenlernen der Sprache sowie nützliche Links zu freien Python-Tutorials; die englische Fassung von "Learn python in Y minutes" ist meist umfangreicher/aktueller als die deutsche Fassung und daher zu empfehlen
noch ein kurzer Python-Rundgang
ein weiteres kurzes Tutorial von Stavros Korokithakis zu Python 3
Python-Umgebung von ActiveState, deren Windows-Version nützliche Windows-Erweiterungen enthält und u.a. auch die IDE PythonWin anbietet
Python-Implementierung in Java
Python-Implementierung für .NET und Mono
Python-Implementierung in Python
umfangreiche Sammlung von Python-Software und Zusatz-Modulen
bietet u.a. eine interaktive Python-Shell mit einem deutlich erweiterten Funktionsumfang gegenüber der Standard-"Shell" von CPython: Syntax-Highlighting, automatische Komplettierungen, dynamische Objekt-Introspektion, Suche in Namensräumen u.v.m.
eine standardmäßig mit CPython verteilte IDE
komplette IDE für Python und Ruby
leistungsfähige IDE für Eclipse, die für Python, Jython and IronPython genutzt werden kann
vollwertige IDE für Python mit freier Community Edition und proprietärer Professional Edition, die u.a. eine Unterstützung von Web-Frameworks (Django, Flask, Google App Engine, Pyramid und web2py), Datenbanken und SQL bietet
leistungsfähige IDE für die wissenschaftliche Programmierung mit Python, die zahlreiche prominente Pakete auf diesem Gebiet integriert, u.a. NumPy, SciPy, Matplotlib, pandas, IPython, SymPy, Cython
This handcrafted guide exists to provide both novice and expert Python developers a best practice handbook to the installation, configuration, and usage of Python on a daily basis.