Inhaltsverzeichnis[Anzeigen]

 

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  right 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 MOVED TO... Enclosing MOVED TO... Global MOVED TO... Built-In
  • TIP 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

  1. Rückgabewert
    1. die Funktion wird explizit über eine return Anweisung verlassen
      MOVED TO... der Rückgabewert bestimmt sich durch das oder die Argumente nach der return Anweisung
    2. die Funktion wird implizit durch das Abarbeiten des Funktionskörpers verlassen
      MOVED TO... der Rückgabwert ist None
  2. Funktionsaufruf
    SyntaxErläuterung
    func(wert) Standardfall: Position bestimmt die Übergabe
    func(name=wert) Schlüsselwort: name bestimmt die Übergabe
  3. Funktionsdefinition
    SyntaxErlä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
  • REFACTOR 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
  • REFACTOR 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)
  • HAND 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}

TIP 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 
  • TIP 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_
  }
  • HAND das Ergebnis
>>>for i in ("+","*","-","/"):

...     print i + ": " , selectLam[i](3,4) , selectDef[i](3,4)

...
+: 7 7
*: 12 12
-: -1 -1

/: 0 0 
  • TIP 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] 
  • HELP Wie kann man einfacher die Liste [1, 2, 3, 4, 5, 6, 7, 8, 9] erzeugen?
  • REFACTOR 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)]
  • TIP 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'}
  • REFACTOR 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 
  • REFACTORerzeuge 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] 
  • REFACTOR gib alle Zeilen der Datei /etc/serviceszurück, die nicht mit # beginnen
    • TIP 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
  • TIP 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')] 

TIP 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']
  • REFACTORSortiere die Variationen von Rainer und Michael
     ["Rainer", "michaeL", "rAiner", "michaEl", "raIner", "MichAel"]
  • HAND 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
    1. 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
      1. cmp: Sortierfunktion
      2. key: Sortierschlüssel
      3. reverse : Sortierreihenfolge
    • REFACTOR löse die Aufgabe, Strings nach ihrem numerischen Wert zu sortieren, mit den neuen Sortierfunktionen
  • HANDprogrammiere einen Filter für eine Datei
    1. entferne alle abschließenden Leerzeichen aus der Zeile
    2. entferne alle Leerzeilen aus der Datei
    3. Zeilenendzeichen müssen erhalten bleiben
    4. vergleiche die Länge der Datei mit der Länge des resultierenden Strings
      • TIP 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

HAND Erzeuge ein dreidimensionales Objekt und weise jedem Punkt in dem Raum einen Defaultwert zu.
TIP 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 

Funktionale Programmierung

Sprachen de>en YahooKEerror
len

Abonniere den Newsletter (+ pdf Päckchen)

Beiträge-Archiv

Sourcecode