Funktionen und Funktionale Programmierung
Funktionen
Elementare Syntax
- mittels des Schlüsselworts def wird eine Funktion definiert
def add(x,y): return x+y ... print add(3,4)
Prinzipien
- def erzeugt ein Objekt und weist es den Funktionsnamen add zu
- return sendet ein Objekt an den Aufrufer zurück
- die Argumente werden mittels call-by-object an die Funktion übergeben
Namensauflösung
- der Funktionskörper besitzt einen eigenen, lokalen Namensraum
- locals(): gibt den lokalen Namensraum aus
- globals(): gibt den globalen Namensraum aus mit global kann der globale Namensraum referenziert werden
x=1 y=1 def enclosing(): y=3 def local(): global x x=y local()
ergibt
>>> enclosing() >>> x 3 >>> y 1
- das Schlüsselwort globalbewirkt zwei Dinge:
- x aus dem globalen scope wird in den lokalen scope der Funktion local eingeführt, so daß die Anweisung x=y keine neue lokale Variable x erklärt
- die Zuweisung an x im lokalen Scope bleibt für den globalen Scope erhalten
- durch y=3 wird y im enclosing unabhängig vom globalen scope neu angelegt
Die Namensauflösung läßt sich auf die einfache LEGB-Regelreduzieren:
- LEGB Regel : Local Enclosing Global Built-In
- in Python 3 kann durch das Schlüsselwort nonlocal auf den umgebenen Scope schreibend zugegriffen werden
>>> n=5 >>> def outer(n): ... def inner(): ... nonlocal n ... n*=2 # bind in scope of outer ... inner() ... print (n) ... >>> outer(100) 200 >>> print (n) 5
Details
- Rückgabewert
- die Funktion wird explizit über eine return Anweisung verlassen
der Rückgabewert bestimmt sich durch das oder die Argumente nach der return Anweisung - die Funktion wird implizit durch das Abarbeiten des Funktionskörpers verlassen
der Rückgabwert ist None
- die Funktion wird explizit über eine return Anweisung verlassen
- Funktionsaufruf
Syntax Erläuterung func(wert) Standardfall: Position bestimmt die Übergabe func(name=wert) Schlüsselwort: name bestimmt die Übergabe - Funktionsdefinition
Syntax Erläuterung def func(name) Standardfall: Übereinstimmung durch Position oder name def func(name=wert) Angabe eines Defaultwertes def func(*name) sammle alle Positionsargumente in einem Tupel name auf def func(**name) sammle alle (Schlüssel,Wert) Paare in einem Dictionary name ein
Beispiele
- Schlüsselworte und Defaultargumente
def fPos(a,b,c): print a,b,c def fDef(a,b=2,c=3): print a,b,c
- mögliche Eingaben:
fPos(1,2,3) fPos(c=3,b=2,a=1) fPos(1,c=3,b=2) fDef(1) fDef(a=1) fDef(1,4) fDef(1,4,5) fDef(1,c=6)
- Beliebige Argumente
def fTup(*args): print args def fDict(**kargs): print kargs def fPosTupDict(a,*pargs,**kargs): print a, pargs, kargs
- mögliche Eingaben
fTup() fTup(1) fTup(1,2,3,4) fDict() fDict(a=1,b=2) fPosTupDict(1,2,3,x=1,y=2)
- schreibe eine Funktion makeDict , die ein Dictionary aus benamten Argument erzeugt
>>> newDict= makeDict(red=1, green=2, blue=3) >>> print newDict {'blue': 3, 'green': 2, 'red': 1} >>> classicDict= {'blue': 3, 'green': 2, 'red': 1} >>> print classicDict {'blue': 3, 'green': 2, 'red': 1}
dies ist die Funktionalität von **kargs:
es nimmt Paare von (Argumentname,Argumentwert) an und erzeugt ein Dictionaryeintrag mit dem gequotetenArgumentnamen
Funktionale Programmierung
- Python ist eine multiparadigmen Programmiersprache
- neben der objektorientierte unterstützt sie insbeondere die funktionale Programmierung
apply (deprecated)
- erlaubt es eine Funktion mit einem Tupel oder Dictionary aufzurufen
- Syntax:
apply( func [,args[,kwargs]] )
>>> def makeSum(x,y,z): return x+y+z ... >>> print makeSum(2,3,4) 9 >>> print apply(makeSum , (2,3,4)) 9 >>> l=[2,3,4] >>> print apply(makeSum ,l) 9 >>> print apply(makeSum , range(2,5) ) 9
- seit Python 2.3 ist folgende Syntax vorzuziehen
>>> print apply.__doc__ apply(object[, args[, kwargs]]) -> value Call a callable object with positional arguments taken from the tuple args, and keyword arguments taken from the optional dictionary kwargs. Note that classes are callable, as are instances with a __call__() method. Deprecated since release 2.3. Instead, use the extended call syntax: function(*args, **keywords). >>> l=[2,3,4] >>> makeSum( *l ) 9 >>> makeSum( *(range(2,5) ) ) 9 >>> d= {"x":2,"y":3,"z":4} >>> makeSum(**d) 9 >>> makeSum( **{"x":2,"y":3,"z":4}) 9 >>> makeSum( **{"z":2,"x":3,"y":4}) 9
- während * eine Sequenz derefenziert, dereferenziert ** ein Dictionary
lambda
- erzeugt eine anonyme Funktion, die an einen Namen gebunden werden kann
- Syntax:
lambda args: expression
a= lambda x,y: x+y sum=a(2,3) #sum=2+3=5 sum=(lambda x,y,z:x+y+z)(*(2,3,4)) sum=(lambda x,y,z:x+y+z)(**{"x":1,"y":2,"z":3}) #sum=2+3+4=9
- Lambda-Funktionen sind insbesondere als Ersatz für benamte Funktionen von Interesse und können so den Blick aufs wesentliche lenken
- beide select Anweisung sind äquivalent
selectLam={ "+": (lambda x,y: x+y), "-": (lambda x,y: x-y), "*": (lambda x,y: x*y), "/": (lambda x,y: x/y) }
und
def add_(x,y): return x+y def sub_(x,y): return x-y def mul_(x,y): return x*y def div_(x,y): return x/y selectDef={ "+": add_ , "-": sub_ , "*": mul_ , "/": div_ }
- das Ergebnis
>>>for i in ("+","*","-","/"): ... print i + ": " , selectLam[i](3,4) , selectDef[i](3,4) ... +: 7 7 *: 12 12 -: -1 -1 /: 0 0
- die Technik, Referenzen auf Methoden oder Funktionen zu hinterlegen, ist unter dem Namen Dispatch Table Wikipedia:Dispatch_table bekannt
map
- wende eine Funktion auf die Elemente einer Sequenz an und gib eine neue Liste zurück
- Syntax:
t= map(func,s)
def func(x): return x*x quad= map( func , range(1,10) ) quad=[1, 4, 9, 16, 25, 36, 49, 64, 81] quadLambda=map(lambda x: x*x, range(1,10)) quadLambda=[1, 4, 9, 16, 25, 36, 49, 64, 81]
- erwartet die Funktion n Argumente, dann müssen auch n Sequenzen angegeben werden
>>> map(lambda x,y: x+y , range(2,8), range(2,8)) [4, 6, 8, 10, 12, 14]
- natürlich können auch benamte Funktionen in map Ausdrücken verwendt werden
>>> map(abs,range(-10,10)) [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- falls die Funktion None ist, wird die Identität verwendet
- damit ist es auch möglich, Sequenzen zu einer Listen zusammenzuführen, wobei fehlende Einträge mit None aufgefüllt werden
>>> map(None, range(1,10 )) [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> map(None, range(1,4),range(101,105),range(1001,1010)) [(1, 101, 1001), (2, 102, 1002), (3, 103, 1003), (None, 104, 1004), (None, None, 1005), (None, None, 1006), (None, None, 1007), (None,None, 1008), (None, None, 1009)] >>> map(lambda x,y,z: x+y+z, range(1,4),range(101,104),range(1001,1004)) [1103, 1106, 1109]
- Wie kann man einfacher die Liste [1, 2, 3, 4, 5, 6, 7, 8, 9] erzeugen?
- berechne 2 ** i für i= -10 bis 10
zip
- zusammenführen von Sequenzen zu einer Liste, wobei die Länge der resultierende Liste dem Minimum der Längen des Sequenzen entspricht
- Syntax:
zip(s1,s2,...,sn)
>>> zip(range(1,4),range(101,104),range(1001,10000000)) [(1, 101, 1001), (2, 102, 1002), (3, 103, 1003)]
- zipist sehr hilfreich um aus Listen Dictionaries zu erzeugen
- erzeuge aus der Liste aller accounts und Ids eine Map
>>> print accs ['root', 'bin', 'daemon', 'lp', 'mail', 'games', 'wwwrun', 'ftp', 'nobody', 'irc', 'at','postfix', 'man', 'news', 'uucp', 'ldap', 'pop'] >>> print ids ['0', '1', '2', '4', '8', '12', '30', '40', '65534', '39', '25', '51', '13', '9', '10', '76', '67'] >>> dict( zip( accs, ids ) ) {'bin': '1', 'ftp': '40', 'daemon': '2', 'uucp': '10', 'postfix': '51', 'nobody': '65534', 'pop': '67', 'ldap': '76', 'games':'12', 'at': '25', 'lp': '4', 'news': '9', 'mail': '8', 'wwwrun': '30', 'irc': '39', 'root': '0', 'man': '13'}
- erzeuge das gleiche Dictionary aus
['root', '0', 'bin', '1', 'daemon', '2', 'lp', '4', 'mail', '8', 'games', '12', 'wwwrun', '30','ftp', '40','nobody', '65534', 'irc', '39', 'at', '25', 'postfix', '51', 'man', '13', 'news','9', 'uucp', '10', 'ldap', '76', 'pop', '67']
>>> from funktionenUndFunktionaleProgrammierung import * >>> myPasswd= dictFromList( passList ) {'bin': '1', 'ftp': '40', 'daemon': '2', 'uucp': '10', 'postfix': '51', 'nobody': '65534', 'pop': '67', 'ldap': '76', 'games':'12', 'at': '25', 'lp': '4', 'news': '9', 'mail': '8', 'wwwrun': '30', 'irc': '39', 'root': '0', 'man': '13'} >>> myPasswd["pop"] '67'
reduce
- wende eine Funktion mit zwei Eingabeparametern sukzessive auf die Elemente einer Sequenz an, um einen Wert auszugeben
der erste Eingabeparameter stelle das Ergebnis des vorherigen Rechenschrittes dar - Syntax:
reduce( func, s)
def sumup(x,y): return x+y sumup=reduce( sumup, range(1,11) ) sumup= reduce(lambda x,y: x+y, range(1,11) ) #sumup=55 fak= reduce(lambda x,y: x*y, range(1,11) ) #fak=3628800
- erzeuge aus der Liste von Wörter ["Only","for","testing","purpose","."] einen Satz mit Hilfe der
- reduce Funktion
- join Funktion der String Klasse
filter
- wende einen Filter auf eine Sequenz an und gib nur die Elemente in einer Sequenz zurück, für die der Filter True ergibt
- Funktionen, die ihre Eingabe zu True oder False evaluieren, werden Prädikate genannt
- Syntax:
filter( func, s )
a=filter(lambda x: x < 5, range(1,10)) a=[1, 2, 3, 4] b=filter(lambda x: round( math.sqrt( x ))**2 == x , range (1,20000)) b=[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401, 2500, 2601,2704, 2809, 2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, 4096, 4225, 4356,4489, 4624, 4761, 4900, 5041, 5184, 5329, 5476, 5625, 5776, 5929, 6084, 6241, 6400, 6561, 6724, 6889, 7056, 7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, 9216, 9409, 9604, 9801, 10000, 10201, 10404, 10609, 10816, 11025, 11236, 11449, 11664, 11881, 12100, 12321, 12544, 12769, 12996, 13225, 13456, 13689, 13924, 14161, 14400, 14641, 14884, 15129, 15376, 15625, 15876, 16129, 16384, 16641, 16900, 17161, 17424, 17689, 17956, 18225, 18496, 18769, 19044, 19321, 19600, 19881]
- gib alle Zeilen der Datei /etc/serviceszurück, die nicht mit # beginnen
- die Liste über alle Zeilen einer Datei erhält du mittels open("Dateiname").readlines()
- Beispiel
>>> new= filterSequenz(open("/etc/services").readlines() ,"#") >>> for i in new[::1000] : print i, ... tcpmux 1/tcp # TCP Port Service Multiplexer tpip 594/tcp # TPIP saism 1436/tcp # Satellite-data Acquisition System 2 jwclient 1938/tcp # JetVWay Client Port vytalvaultvsmp 2547/udp # vytalvaultvsmp amt-cnf-prot 3054/tcp # AMT CNF PROT pda-gate 4012/udp # PDA Gate bctp 8999/tcp # Brodos Crypto Trade Protocol
list comprehension
- erzeuge eine neue Liste durch das sukzessive Iterieren über bestehende Sequenzen
- die hinzugefügten Elemente können Ausdrücke sein, die vorgegebenen Bedingungen genügen müssen
- die Mächtigkeit dieser Konstrukts entspricht der Mächtigkeit der vorgestellten Methoden map und filter
- es gilt als guter Stil in Python im Zweifelsfall list comprehension anzuwenden
- Syntax:
[ expression for item1 in sequence1 for item2 in sequence2 ... for itemN in sequenceN if condition ]
diese Syntax ist äquivalent zu:
s=[] for item1 in sequence1: for item2 in sequence2: ... for itemN in sequenceN: if condition: s.append(expression)
- ein paar Variationen:
a=map(lambda x: x*x, range(1,10 )) a=[ x*x for x in range(1,10)] #a=[1, 4, 9, 16, 25, 36, 49, 64, 81] b=filter(lambda x: x < 5, range(1,10) ) b=[x for x in range(1,10) if x < 5 ] #b=[1, 2, 3, 4] c=filter( lambda x: round( math.sqrt( x ))**2 == x , range (1,20000) ) c=[ x for x in range( 1,20000) if round( math.sqrt( x ))**2 == x ] #c=[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401, 2500, 2601,2704, 2809, 2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, 4096, 4225, 4356,4489, 4624, 4761, 4900, 5041, 5184, 5329, 5476, 5625, 5776, 5929, 6084, 6241, 6400, 6561, 6724, 6889, 7056, 7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, 9216, 9409, 9604, 9801, 10000, 10201, 10404, 10609, 10816, 11025, 11236, 11449, 11664, 11881, 12100, 12321, 12544, 12769, 12996, 13225, 13456, 13689, 13924, 14161, 14400, 14641, 14884, 15129, 15376, 15625, 15876, 16129, 16384, 16641, 16900, 17161, 17424, 17689, 17956, 18225, 18496, 18769, 19044, 19321, 19600, 19881] d=[(a,b) for a in range(1,5) for b in ("a","b")] #d=[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b'), (4, 'a'), (4, 'b')]
Das Decorate-Sort-Undecorate Idiom (auch bekannt als Schwartzian transform) hilft, um eine Liste nach einem vorgegebenen Kriterium zu sortieren.
Sortiere die numerischen Strings nach ihrem numerischen Wert.
- so nicht:
>>> strings= [ str(i) for i in range(7,12) ] >>> print strings ['7', '8', '9', '10', '11'] >>> strings.sort() >>> print strings ['10', '11', '7', '8', '9']
- aber so
>>> decorate= [ ( int(i),i) for i in strings ] >>> decorate.sort() >>> undecorate=[ y for x,y in decorate ] >>> print undecorate ['7', '8', '9', '10', '11']
- Sortiere die Variationen von Rainer und Michael
["Rainer", "michaeL", "rAiner", "michaEl", "raIner", "MichAel"]
- case-insensitive
- case-insensitive und stabil ( DeWikipedia:Stabiles_Sortierverfahren ). verwende die built-in Funktion enumerate
- seit Python 2.4 ist das sortieren mit Python built-in Funktionalität deutlich einfacher, denn die Funktionen sort wurde erweitert und die freie Funktion sortedeingeführt
- während sort stabil auf der Liste sortiert, gibt sorted, eine neue, sortierte Liste zurück
- beide Funktionen unterstützen die Schlüsselwörter cmp , key und reverse
- cmp: Sortierfunktion
- key: Sortierschlüssel
- reverse : Sortierreihenfolge
- löse die Aufgabe, Strings nach ihrem numerischen Wert zu sortieren, mit den neuen Sortierfunktionen
- programmiere einen Filter für eine Datei
- entferne alle abschließenden Leerzeichen aus der Zeile
- entferne alle Leerzeilen aus der Datei
- Zeilenendzeichen müssen erhalten bleiben
- vergleiche die Länge der Datei mit der Länge des resultierenden Strings
- die Methoden rstrip, join von String und len einer Sequenz lösen die Einzelschritte
>>> test = cal.cleanFile("/home/grimm/schulung/Python/Beispiele/grundlagen.py") >>> print test (1320, 1270) >>> test = cal.cleanFile("/etc/services") >>> print test (304796, 304758)
Beispiel
Erzeuge ein dreidimensionales Objekt und weise jedem Punkt in dem Raum einen Defaultwert zu.
Löse die Aufgabe iterativ oder Wie sind die Beispiele aus Python Grundlagen entstanden?
- erzeuge die Indizes
>>> [ (i,j,k ) for i in range( 1,3) for j in range(1,2) for k in range(1,10)] [(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 1, 4), (1, 1, 5), (1, 1, 6), (1, 1, 7), (1, 1, 8), (1, 1, 9), (2, 1, 1), (2, 1, 2), (2, 1, 3), (2, 1, 4), (2, 1, 5), (2, 1, 6), (2, 1, 7), (2, 1, 8), (2, 1, 9)]
- weise jedem Punkt einen Defaultwert zu
>>> zip( [ (i,j,k ) for i in range( 1,3) for j in range(1,2) for k in range(1,10)], [0]*2*1*9 ) [((1, 1, 1), 0), ((1, 1, 2), 0), ((1, 1, 3), 0), ((1, 1, 4), 0), ((1, 1, 5), 0), ((1, 1, 6), 0), ((1, 1, 7), 0), ((1, 1, 8), 0), ((1, 1, 9), 0), ((2, 1, 1), 0), ((2, 1, 2), 0), ((2, 1, 3), 0), ((2, 1, 4), 0), ((2, 1, 5), 0), ((2, 1, 6), 0), ((2, 1, 7), 0), ((2, 1, 8), 0), ((2, 1, 9), 0)]
- erlaube den punktweisen Zugriff durch ein Dictionary
>>> dict( zip( [ (i,j,k ) for i in range( 1,3) for j in range(1,2) for k in range(1,10)], [0]*2*1*9 ) ) {(1, 1, 9): 0, (1, 1, 8): 0, (1, 1, 5): 0, (1, 1, 4): 0, (1, 1, 7): 0, (1, 1, 6): 0, (1, 1, 1): 0, (1, 1, 3): 0, (1, 1, 2): 0, (2, 1, 8): 0, (2, 1, 9): 0, (2, 1, 6): 0, (2, 1, 7): 0, (2, 1, 4): 0, (2, 1, 5): 0, (2, 1, 2): 0, (2, 1, 3): 0, (2, 1, 1): 0
- parametrisiere alles, indem du eine Funktion darum legst
>>> def make3DObjekt(x,y,z,default): ... return dict( zip( [ (i,j,k ) for i in range( 1,x+1) for j in range(1,y+1) for k in range(1,z+1)], [default]*x*y*z ) ) ...
- probiere es aus
>>> print make3DObjekt(1,2,3,10) {(1, 2, 1): 10, (1, 2, 2): 10, (1, 2, 3): 10, (1, 1, 1): 10, (1, 1, 3): 10, (1,1, 2): 10} >>> print make3DObjekt(1,0,0,5) {} >>> print make3DObjekt(3,3,3,0) {(3, 1, 3): 0, (3, 1, 2): 0, (3, 1, 1): 0, (3, 3, 1): 0, (3, 3, 3): 0, (3, 3, 2): 0, (1, 2, 1): 0, (1, 2, 2): 0, (1, 2, 3): 0, (2, 2, 3): 0, (2, 2, 2): 0, (2, 2, 1): 0, (3, 2, 2): 0, (3, 2, 3): 0, (3, 2, 1): 0, (1, 3, 3): 0, (1, 3, 2): 0, (1, 3, 1): 0, (1, 1, 1): 0, (1, 1, 3): 0, (1, 1, 2): 0, (2, 3, 1): 0, (2, 3, 2):0, (2, 3, 3): 0, (2, 1, 2): 0, (2, 1, 3): 0, (2, 1, 1): 0} >>> cube=make3DObjekt(3,3,3,0) >>> print cube[1,1,2] 0 >>> cube[1,1,2]= 10 >>> cube[1,1,3]= 20 >>> print cube[1,1,2] + cube[1,1,3] 30
- Worin sich funktionale Programmierung auszeichnet (Grundzüge der funktionalen Programmierung) und wie ihr es in Python einsetzen könnt (Funktionale Programmierung in Python) , wird in den zitierten Artikeln erklärt.
Weiterlesen...