Ergänzungen zu lambda-Parametern in Python ⃗
Benennungsstil ⃗
In Namen, die aus mehreren Wörtern zusammengesetzt werden, wird ein Grundstrich zu Trennung der Wörter eingesetzt.
- Name
zeit_in_sekunden
- PEP 8
- Function and Variable Names
- Function names should be lowercase, with words separated by underscores as necessary to improve readability.
- Variable names follow the same convention as function names.
Vereinfachungsmöglichkeiten ⃗
Man beachte, daß der Ausdruck »( lambda x: sqrt( x ))« praktisch dasselbe ist wie »sqrt«, so daß solch eine lambda-Schreibweise normalerweise nur unnötig kompliziert wäre, da man stattdessen auch einfach »( sqrt )« schreiben kann.
- Protokoll
from math import sqrt # Quadratwurzel
( lambda x: sqrt( x ))( 16 )
4
- Protokoll
( sqrt )( 16 )
4
- Protokoll
sqrt( 16 )
4
Gültigkeitsbereich ⃗
Durch die Verwendung eines Namens zwischen »lambda« und dem nächsten »:« wird ein der Name als Parameter festgelegt (deklariert). Daher nennt man diese Verwendung auch eine Parameterdeklaration.
Der Gültigkeitsbereich einer Deklaration ist der Teil des Quelltextes, in welchem der deklarierte Name im Sinne jener Deklaration verwendet werden kann.
Im Falle eines lambda-Ausdrucks ist der Gültigkeitsbereich einer etwaigen Parameterdeklaration der lambda-Ausdruck selber.
Das folgende Beispiel zeigt einen gescheiterten Versuch, den Namen eines Parameters außerhalb seines Gültigkeitsbereiches auszuwerten.
- Protokoll (frei übersetzt und gekürzt)
( lambda x: x )( 3 )
3
x
Namensfehler: Ein Name 'x' wurde nicht definiert.
Wenn ein Name schon einen Wert außerhalb eines lambda-Ausdrucks hat, und auch als Parametername verwendet wird, so hat er innerhalb des Gültigkeitsbereichs der Parameterdeklaration die durch die Parameterdeklaration verliehene Bedeutung (wie man an der vorletzten Ausgabe des folgenden Protokoll erkennen kann); außerhalb des Gültigkeitsbereichs der Parameterdeklaration behält der Name die Bedeutung bei, welche er schon vor der Auswertung der lambda-Ausdrücke hatte (wie man an der letzten Ausgabe des folgenden Protokoll erkennen kann).
- Protokoll
from math import pi
pi
3.141592653589793
( lambda pi: pi+2 )( 3 )
5
pi
3.141592653589793
Weitere Beispiele ⃗
- Protokoll
from math import pi
pi
3.141592653589793
( lambda x: x )( 2 )
2
( lambda x: pi )( 2 )
3.141592653589793
( lambda pi: pi )( 2 )
2
( lambda pi: pi + pi )( 2 )
4
pi
3.141592653589793
Regeln zur Namenssuche ⃗
Wenn ein Name im inneren Ausdruck eines lambda-Ausdrucks verwendet wird, dann wird zunächst in der Bindungstabelle des lambda-Objekts nach einem Wert für den Namen gesucht, dann in der Bindungstabelle des den Ausdruck umgebenden Moduls und schließlich im Modul »builtins«.
- Auswertung eines Namens aus »builtins«
( lambda x: quit )( 0 )
Use quit() or Ctrl-Z plus Return to exit
- Auswertung eines Namens aus »__main__« (das aktuelle Modul)
from math import pi
( lambda x: pi )( 0 )
3.141592653589793
- Auswertung eines Namens aus dem lambda-Objekt
( lambda pi: pi )( 0 )
0
Mehrfache Anwendung derselbe Funktion ⃗
Die Grundstrichvariable »_« erlaubt es uns, zusammen mit »print«, ein- und dieselbe lambda-Funktion mehrfach hintereinander auf verschiedene Argumente anzuwenden.
- Konsolenprotokoll
lambda x: x * x + x + 1
<function <lambda> …>
print( _( 0 ))
1
print( _( 1 ))
3
print( _( 2 ))
7
print( _( _( 0 )))
3
Genauso kann aber auch eine Bindung an einen selbstgewählten Namen verwendet werden.
- Konsolenprotokoll
f = lambda x: x * x + x + 1
print( f( 0 ))
1
print( f( 1 ))
3
print( f( 2 ))
7
print( f( f( 0 )))
3
Wirkfunktionen ⃗
Auch Wirkfunktionen lassen sich in einem lambda-Ausdruck verwenden.
- Protokoll
f = lambda: print( 'alpha' )
f()
alpha
f()
alpha
Oben wird praktisch der Inkarnation von »print« mit dem Argumentwert «'alpha'» der Name »f« gegeben. Diese Inkarnation kann dann wiederholt durch Auswertung von »f()« gestartet werden.
Im folgenden wird einer Verbindung eines Funktors mit einem bestimmten Argumentwert ein Name gegeben.
- Protokoll
from os import system
cls = lambda: system( 'cls' )
cls()
cls()
no = lambda x: None
cls = lambda: no( system( 'cls' ))
cls()
cls()
- Protokoll
f = lambda s: print( 'alpha ' + s )
f( '0' )
alpha 0
f( '1' )
alpha 1
Die Reihenfolge der Auswertung von Operanden ⃗
Operanden werden von links nach rechts ausgewertet.
- Konsolenprotokoll
print( 1 )+ print( 2 )
1
2
TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType'
Die Fehlermeldung des vorherigen Beispiels kann durch eine etwas kompliziertere Schreibweise vermieden werden.
- Konsolenprotokoll
( lambda x: 0 )( print( 1 ))+( lambda x: 0 )( print( 2 ))
1
2
0
- Zitat *
- 6.15 Evaluation order
- Python evaluates expressions from left to right.
- The Python Language Reference, Release 3.9.0a3
Stilregel ⃗
Stilregel Der Gültigkeitsbereich eines Namens sollte immer möglichst klein sein.
Begründung Große Gültigkeitsbereiche überlappen sich auch in großen Bereichen. In jenen Bereich haben viele Namen dann schon eine Bedeutung (aus diesen verschiedenen Gültigkeitsbereichen). Für einen Programmierer, der jenen Bereich verstehen oder überarbeiten will, wird es schwierig, alle diese vielen Bedeutungen noch zu überblicken: das Programm ist unübersichtlich.
Beispielsweise erlaubt der kleine Gültigkeitsbereich der Parameterdeklaration in dem folgenden Beispiel, den beliebten Parameternamen »x« in zwei verschiedenen einander nahestehenden lambda-Ausdrücken zu verwenden, ohne daß befürchtet werden muß, daß die beiden »x« von der Python -Implementation miteinander verwechselt werden oder sich gegenseitig irgendwie stören.
- Protokoll
( lambda x: 2 * x )( 7 )
14
( lambda x: 3 * x )( 7 )
21
Das folgende Beispiel zeigt, daß der Parametername »pi« den Namen »pi« außerhalb des lambda-Ausdrucks nicht stört, da die Gültigkeit des Parameternamens auf den kleinen lambda-Ausdruck beschränkt ist.
- Protokoll
( lambda pi: pi )( 2 )
2
pi
3.141592653589793
Eine Funktion, die das Doppelte ihres Argumentwertes («x») ergibt: ›lambda x: 2*x‹.
- Protokoll
f = lambda x: 2*x
f( 7 )
14
Vereinfachung ⃗
- Verschachtelter Ausdruck
print( int( input() ) + int( input() ) )
- weniger Verschachtelungen
eingabe = lambda: int( input() )
print( eingabe() + eingabe() )
Einschlüsse *
Die Werte von Parametern eines umgebenden lambda-Ausdrucks werden in inneren lambda-Ausdrücken „eingeschlossen“.
- Protokoll
g = lambda x: lambda: x
f = g( 3 )
f()
3
x = 28
f()
3
Funktionen, die Funktionen mit Einschlüssen ergeben *
Im folgenden ist «f» eine Funktion, die selber wieder eine Funktion ergibt, welche den Argumentwert von «f» zu einer Zahl addiert.
So ist «f( 7 )» beispielsweise die Funktion «( lambda y: 7 + y )», welche «7» zu ihrem Argumentwert addiert.
- Protokoll
f = lambda x:( lambda y: x + y )
f( 7 )( 2 )
9
g = f( 7 )
g( 2 )
9
g( 4 )
11
Die Klammern hinter »x:« waren oben aber nicht nötig, sondern wurden nur zur Verdeutlichung eingefügt.
- Protokoll
f = lambda x: lambda y: x + y
f( 7 )( 2 )
9
f( 'abc' )( 'def' )
'abcdef'
Die gleichen Funktionen können auch mit Zeichenfolgen verwendet werden.
- Protokoll
g = f( 'alpha ' )
g( 'gamma' )
'alpha gamma'
g( 'epsilon' )
'alpha epsilon'
h = f( 'zeta ' )
h( 'epsilon' )
'zeta epsilon'
h( 'zeta' )
'zeta zeta'
Auswertungen von Zeichenfolgen *
Durch Auswertung einer Zeichenfolge mit ›eval‹ kann eine Zuordnung realisiert werden. Diese Möglichkeit spielt in der Praxis kaum eine Rolle, da es dafür bessere Techniken gibt, aber sie kann an dieser Stelle des Kurses die Möglichkeiten von ›eval‹ und »lambda« illustrieren.
- Protokoll
x1 = 'sehr gut'
x2 = 'gut'
i = 1
eval( 'x' + str( i ))
'sehr gut'
i = 2
eval( 'x' + str( i ))
'gut'
Damit können nun „Nachschlagefunktionen“ geschrieben werden.
- Protokoll
f = lambda i: eval( 'x' + str( i ))
f( 1 )
'sehr gut'
f( 2 )
'gut'
Wir können auch umgekehrt Zahlen ermitteln, die zu bestimmten Texten gehören (solange die Texte in Bezeichnern erlaubt sind).
- Protokoll
xgut = 2
xbefriedigend = 3
f = lambda s: eval( 'x' + s )
f( 'gut' )
2
f( 'befriedigend' )
3
Aufrufe von Zahlen ⃗
Eine Zahl kann nicht wie eine Funktion aufgerufen werden:
- Protokoll
NOP = 3
type( NOP )
<class 'int'>
- Protokoll
NOP
3
- Protokoll (verkürzt und übersetzt)
NOP()
Typfehler: ein Wert vom Typ 'int' kann nicht aufgerufen werden
Veranschaulichung der Aufrufbarkeit ⃗
Die Funktion «aufruf» ruft ihren Argumentwert auf.
- Protokoll
aufruf = lambda o: o()
Wir können damit testen, ob ein Objekt aufrufbar ist. Das Objekt ›random‹ erweist sich als aufrufbar, da es keine Fehlermeldung gibt, wenn man es an «aufruf» übergibt.
- Protokoll
from random import random
aufruf( random )
0.10304690019362062
Das Objekt ›3‹ ist hingegen nicht aufrufbar.
- Protokoll
aufruf( 3 )
TypeError: 'int' object is not callable
Die Fehlermeldung »'int' object is not callable« besagt auch genau das: Daß ein int-Objekt nicht aufrufbar ist.
(Die Funktion ›call‹ kann allerdings nur aufrufbare Objekte aufrufen, deren Aufruf kein Argument benötigt.)
Abstraktion ⃗
Beim Programmieren ist es entscheidend, daß bestimmte Informationen weggelassen („abstrahiert“) werden. Die beiden folgenden drei Ausdrücke multiplizieren beide einen durch ihren Quelltext nicht bestimmten Wert mit »[2]«.
- Ausdruck
lambda x: 2 * x
- Ausdruck
2 * input()
- Ausdruck
from random import random
2 * random()
Bei der Auswertung des ersten Ausdrucks wird der Wert bei einem Aufruf durch den Wert des Arguments festgelegt, bei der Auswertung des zweiten Ausdrucks wird er durch eine Benutzereingabe festgelegt, bei der Auswertung des dritten Ausdrucks durch Erzeugung einer Zufallszahl.
- Auswertung
( lambda x: 2 * x )( 7 )
14
- Protokoll
from ast import literal_eval
2 * literal_eval( input() )
7
14
- Protokoll
from random import random
2 * random()
1.452798472983382