Leverage the power of code recycling with functions. For interactive reading and executing code blocks and find b04-pyfun.ipynb, or install Python and JupyterLab locally.
Watch this section as a video
Watch this section as a video on the @Hydro-Morphodynamics channel on YouTube.
Was sind Funktionen?¶
Funktionen sind eine bequeme Möglichkeit, Code in praktische, wiederverwendbare und besser lesbare Blöcke zu unterteilen, die zur Strukturierung von Code beitragen. Funktionsblöcke können parametrische Argumente akzeptieren und sind wiederverwendbar. Daher sind Funktionen ein Schlüsselelement, um Code zu teilen und in Teams zu arbeiten. Die Grundstruktur einer Python-Funktion beinhaltet:
A
defkeyword followed by a function name with arguments in parentheses and a code block.Die Art von Argumenten, die eine Funktion empfangen kann, sind:
Erforderliche Argumente:
argStandard-Keyword-Argumente (mit Standardwerten):
arg=valueWillkürliche Positionsargumente (optional):
*argsWillkürliche (optionale) Keyword-Argumente:
**kwargs
Durch die Verwendung optionaler (Keyword-)Argumente werden Funktionen robuster und flexibler. Der Codeblock einer Funktion ist eingerückt, ähnlich wie Schleifen:
def my_function(argument1, *args, **kwargs):
something = ... # do something with argument1, *args, and **kwargs
return somethingEin Grundbeispiel¶
Nur eine Handvoll Länder (insbesondere die Vereinigten Staaten) verwenden immer noch imperiale Einheiten im täglichen Leben, während die meisten Länder die *Système international d’unités * (SI-Einheiten) verwenden. Lassen Sie uns eine einfache Funktion schreiben, um Benutzern der imperialen Einheit zu helfen, * Fuß * (imperial) in * Meter * (SI) umzuwandeln.
In the following example, the function name is feet_to_meter and the function accepts one argument, which is feet. The function returns the feet argument multiplied with a conversion_factor of 0.3048, which corresponds to the conversion factor from feet to meters. In this simple example, the conversion_factor variable cannot be modified externally and only exists in the namespace of the function.
def feet_to_meter(feet):
conversion_factor = 0.3048
return conversion_factor * feetFunktionsaufrufe¶
To call a function, it must be defined before the call. The function may be defined in the same script or in another script, which can then be imported as a module (read more about modules and packages in the next section). Then we can call, for example, the above-defined feet_to_meter function as follows:
feet_value = 10
print("{0} feet are {1} meters.".format(feet_value, feet_to_meter(feet_value)))10 feet are 3.048 meters.
Optionale Argumente *args¶
Replacing the non-optional feet argument in the above function with an optional argument *args enables the conversion of as many length values as the function receives. The following lines explain step-by-step how that works.
Stellen Sie sicher, dass jeder die Eingabe- und Ausgabeparameter der Funktion versteht, indem Sie inline docstrings mit einem Paar von dreifachen Doppel-Aposttrophen (
""") hinzufügen, die Eingabeparameter (:params parameter_name: definition) und die Funktionsrückgabe (:output: definition) umfassen.By default, we will assume that multiple values are provided. Therefore, a list called
value_listis instantiated at the beginning of the function, whileconversion_factorremains the same as before.A for-loop over
*argsidentifies and processes the arguments provided. Why a for-loop?
Python automatically collects*argsinto a tuple, and therefore, we can iterate over*args, even though the provided values were not passed as a list or tuple.The for-loop in the
trycode block includes atry-exceptstatement to verify if the provided values (arguments) are numeric and can be converted to meters. If thetryblock runs successfully, the expressionarg * conversion_factorappends the converted argumentargtovalue_list.Eventually, the
returnkeyword returns the value list.
def feet_to_meter(*args):
"""
:param name: numeric values in feet
:output: returns list of values in meter
"""
value_list = []
conversion_factor = 0.3048
for arg in args:
try:
value_list.append(arg * conversion_factor)
except TypeError:
print(str(arg) + " is not a number.")
return value_listMit der neu definierten und flexibleren Funktion können wir nun feet_to_meter mit so vielen Argumenten wie nötig anrufen:
print("Function call with 3 values: ")
print(feet_to_meter(3, 1, 10))
print("Function call with no value: ")
print(feet_to_meter())
print("Function call with non-numeric values:")
print(feet_to_meter("just", "words"))
print("Function call with mixed numeric and non-numeric values:")
print(feet_to_meter("just", "words", 2))Function call with 3 values:
[0.9144000000000001, 0.3048, 3.048]
Function call with no value:
[]
Function call with non-numeric values:
just is not a number.
words is not a number.
[]
Function call with mixed numeric and non-numeric values:
just is not a number.
words is not a number.
[0.6096]
Optionale Keyword-Argumente **Kwargs¶
In the last paragraphs, we made the feet_to_meter function more flexible so that it can now receive as many arguments as needed. Until now, the internal conversion_factor variable cannot be modified from outside the function, which limits flexibility. For instance, imagine we are writing this function for a historian. In the past, imperial units were widespread in many cultures (e.g., Greek, Roman, or Chinese) with varying length definitions between 0.250 m and 0.335 m. That means the historian will need flexibility regarding the conversion factor, while we still want to use 0.3048 m as the default value. This requirement can be implemented with optional keyword arguments **kwargs and this is how it works in the code block below:
Fügen Sie
**kwargsnach*argsin der FunktiondefKlammern hinzu (die Reihenfolge von*args, **kwargsist wichtig).Keep
conversion_factor = 0.3048as the default value (we want the function to be functional also without any keyword argument provided).Ähnlich wie die
*args-Anweisung identifiziert Python automatisch Variablen, die mit**beginnen, als optionale Keyword-Argumente (eigentlich spielen die Namen args und kwargs keine Rolle - die*-Zeichen sind wichtig). Der Unterschied zu*argsist, dass Python**kwargsals Wörterbuch identifiziert.Eine For-Loop iteriert über das kwargs-Wörterbuch und die
if-Anweisung identifiziert jedes optionale Keyword-Argument, das die Zeichenfolge"conv"als conversion factor enthält.Eine
try-except-Anweisung testet, ob der angegebene Wert für das Keyword-Argument numerisch ist, indem Sie eine Konvertierung infloat()versuchen.
Der Rest der Funktion bleibt unverändert.
def feet_to_meter(*args, **kwargs):
"""
:param *args: numeric values in feet
:output: returns list of values in meter
"""
value_list = []
conversion_factor = 0.3048
for key, value in kwargs.items():
if "conv" in key:
try:
conversion_factor = float(value)
print("Using conversion factor = " + str(value))
except (ValueError, TypeError):
print(str(value) + " is not a number (using default value 0.3048).")
for arg in args:
try:
value_list.append(arg * conversion_factor)
except TypeError:
print(str(arg) + " is not a number.")
return value_listTesten Sie verschiedene Umrechnungsfaktoren mit der neu definierten Flexibilität der feet_to_meter-Funktion:
print("Function call with 3 values and a conversion factor of 0.25: ")
print(feet_to_meter(3, 1, 10, conv_factor=0.25))
print("Function call with 3 values and a conversion factor of 1/7 with slightly different name: ")
print(feet_to_meter(3, 1, 10, conversion_factor=1/7))
print("Function call with 2 values with default conversion factor: ")
print(feet_to_meter(25, 10))Function call with 3 values and a conversion factor of 0.25:
Using conversion factor = 0.25
[0.75, 0.25, 2.5]
Function call with 3 values and a conversion factor of 1/7 with slightly different name:
Using conversion factor = 0.14285714285714285
[0.42857142857142855, 0.14285714285714285, 1.4285714285714284]
Function call with 2 values with default conversion factor:
[7.62, 3.048]
Default Keyword Argumente¶
Keyword arguments can also be defined by default. The below example shows how the conversion_factor can be default-defined in the def function parentheses. Note that conversion_factor must be defined after any optional arguments *args.
def feet_to_meter(*args, conversion_factor=0.3048):
"""
:param *args: numeric values in feet
:output: returns list of values in meter
"""
value_list = []
for arg in args:
try:
value_list.append(arg * conversion_factor)
except TypeError:
print(str(arg) + " is not a number.")
return value_listNow we can use feet_to_meter with or without or with a conversion factor and after a list of values:
print("Function call with a conversion factor of 0.313 and two values: ")
print(feet_to_meter(1, 10, conversion_factor=0.313))
print("Function call with 3 values without any conversion factor: ")
print(feet_to_meter(3, 1, 10))Function call with a conversion factor of 0.313 and two values:
[0.313, 3.13]
Function call with 3 values without any conversion factor:
[0.9144000000000001, 0.3048, 3.048]
Funktion Wrapper und Dekorateure¶
If multiple functions contain similar lines, chances are that those functions can be further factorized by using function wrappers and decorators. A typical example is a license checkout (e.g. to use a commercial Python module/package, such as Esri’s arcpy) or if we want to use a recurring error statement with try - except statements.
Betrachten Sie zum Beispiel zwei oder mehr Funktionen, die numerische Ausgaben von Benutzereingaben empfangen, verarbeiten und erzeugen sollen. Diese Funktionen können so aussehen:
def multiply_arguments(*args):
result = 1.0
try:
for arg in args:
result *= arg
print("The result is: " + str(result))
except TypeError:
print("ERROR: The calculation could not be performed (input arguments: %s)" % str(args))
except ValueError:
print("ERROR: The calculation could not be performed (input arguments: %s)" % str(args))
return result
def sum_up_arguments(*args):
result = 0.0
try:
for arg in args:
result += arg
except TypeError:
print("ERROR: The calculation could not be performed (input arguments: %s)" % str(args))
except ValueError:
print("ERROR: The calculation could not be performed (input arguments: %s)" % str(args))
return resultBoth functions involve the statement print("The result is: " + str(result)) to print the results to the Python console (e.g., to get some intermediate information) and to run only on valid (i.e., numeric) input with the help of exception (try - except) statements. However, we want our functions to focus on the calculation only and this is where a wrapper function helps.
A wrapper function can be defined by first defining a standard function (e.g., def verify_result) and then passing another function (func) as an argument. In this function (verify_result), we can then place a nested def wrapper() function that will embrace func. It is important to use both optional *args and optional keyword **kwargs in the wrapper function and the call to func to make the wrapper as flexible as possible.
def verify_result(func):
def wrapper(*args, **kwargs):
try:
result = func(*args, **kwargs)
print("Success. The result is %1.3f." % float(result))
return result
except TypeError:
print("ERROR: The calculation could not be performed because of at least one non-numeric input (input arguments: %s)" % str(args))
return 0.0
except ValueError:
print("ERROR: The calculation could not be performed because of non-numeric input (input arguments: %s)" % str(args))
return 0.0
return wrapperNow, we can use an @-decorator to wrap the above math functions in the verify_result(fun) function. When Python reads the beautiful, code-decorating @ sign, it automatically looks for the wrapper function defined after the @ sign to wrap the following function.
@verify_result
def multiply_arguments(*args):
result = 1.0
for arg in args:
result *= arg
return result
@verify_result
def sum_up_arguments(*args):
result = 0.0
for arg in args:
result += arg
return resultDie beiden Funktionen (multiply_arguments und sum_up_arguments) können wie gewohnt aufgerufen werden, zum Beispiel:
multiply_arguments(3, 4)
multiply_arguments(3, 4, "not a number")
sum_up_arguments(3, 4)
sum_up_arguments("absolutely", "no", "valid", "input")Success. The result is 12.000.
ERROR: The calculation could not be performed because of at least one non-numeric input (input arguments: (3, 4, 'not a number'))
Success. The result is 7.000.
ERROR: The calculation could not be performed because of at least one non-numeric input (input arguments: ('absolutely', 'no', 'valid', 'input'))
0.0The above wrapper function returns the wrapped function results, too. However, to use built-in function attributes (e.g., the function’s name with __name__, the function’s docstring with __doc__, or the module in which the function is defined with __module__) outside of the wrapper, we need the wrapper function to return the wrapped (decorated) function itself. This can be done as follows:
def error_func(*args, **kwargs):
return 0.0
def verify_result(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except TypeError:
print("ERROR: The calculation could not be performed because of at least one non-numeric input (input arguments: %s)" % str(args))
return error_func(*args, **kwargs)
except ValueError:
print("ERROR: The calculation could not be performed because of non-numeric input (input arguments: %s)" % str(args))
return error_func(*args, **kwargs)
return wrapperNote the difference: the wrapper function now returns func(*args, **kwargs) instead of the numeric variables as result. If the function cannot be executed because of invalid input, the wrapper will return an error function (error_func), which ensures the consistency of the wrapper function. One may think that the error function returning 0.0 is obsolete because the exception statements could directly return 0.0. However, 0.0 is a float variable, while error_func is a function and the function wrapper should always return the same data type, regardless of an exception raise (error) or a successful execution. This is what makes code consistent.
This paragraph showed examples of using the decorators in the shape of an @ sign to wrap (embrace) a function. Decorators are also a useful feature in Python classes, for example, when a class function returns static values. Read more about decorators in classes later in the chapter on object orientation and classes.
Iteratoren und Generatoren¶
Ein Merkmal der Datentypen list, tuple und dictionary ist ihre Iterabilität, die durch ihre eingebaute __iter__ bereitgestellt wird. Daher ist Iterabilität der Grund, warum wir schreiben können:
for e in [1, 2, 3]: print(e)1
2
3
Besides iterations, Python also enables the creation of generators (i.e., generator functions). Instead of using a return statement, a generator function ends with one (or more) yield statement(s), returning data as long as a next() function (inherent step in iterations) is called. An application of a generator is, for example, the flattening of nested lists (i.e., remove sub-lists and write all variables directly into a non-nested list):
from collections.abc import Iterable
def flatten(nested_list):
for e in nested_list:
if isinstance(e, Iterable) and not isinstance(e, str):
for x in flatten(e):
yield x
else:
yield e
a_nested_list = [[1, 2, 3], ["a", "b", "c"]]
flattened_list = list(flatten(a_nested_list))
print(flattened_list)[1, 2, 3, 'a', 'b', 'c']
Lambdafunktionen¶
Lambda (λ) calculus is a formal language for expressing computation-based function abstraction and was introduced in the 1930s by the mathematician Alonzo Church. Lambda functions originate from functional programming and represent short, anonymous (i.e, without a name) functions. Although Python is not inherently a functional programming language, functional concepts were implemented early in Python, for example with the map(), filter(), and reduce() functions and also the lambda operator.
In Python kann eine anonyme (namenlose) Lambda-Funktion eine beliebige Anzahl von Argumenten annehmen, aber nur einen Ausdruck haben. Die Argumente bestehen aus einer kommagetrennten Liste von Variablen und der Ausdruck verwendet diese Argumente. Die syntax der lambda-Funktionen ist:
lambda arguments : expression
The following example illustrates a lambda function with one argument and adds 1 to the argument:
add_one = lambda number : number + 1
print(add_one(1))2
Das war nett, aber ziemlich nutzlos. Hier ist ein Beispiel für eine etwas nützlichere Lambda-Funktion, die zwei Eingabeargumente zusammenfasst:
sum_up = lambda x, y : x + y
print(sum_up(1, 5))6
Die oben gezeigte Funktion zur Umwandlung von Füßen in Meter kann auch als Lambda-Funktion geschrieben werden:
feet_to_meter = lambda ft_value : ft_value * 0.3048
print(feet_to_meter(10))3.048
Using a lambda function made the code shorter. In addition, to evaluate the feet_to_meter lambda function for multiple values, we can use the map() function. The syntax of a map() function is:
result = map(function, sequence)
where sequence can be a list or a tuple. Thus, to evaluate a tuple of four values, we can write:
four_ft_values = (4, 9.7, 7, 2)
print(list(map(feet_to_meter, four_ft_values)))[1.2192, 2.95656, 2.1336, 0.6096]
The list() function converts the map() output into a list to evaluate the map() function (otherwise, the result would be something like <map object at ...>).
Wenn die Funktion feet_to_meter nicht an einer anderen Stelle im Code benötigt wird, kann man auch schreiben:
print(list(map(lambda x : x * 0.3048, (4, 9.7, 7, 2))))[1.2192, 2.95656, 2.1336, 0.6096]
Eine weitere Funktion von Python ist die Funktion filter(function, list), die eine elegante Lösung darstellt, um die Elemente aus einer Liste herauszufiltern, für die die Funktion True zurückgibt. Der folgende Codeblock zeigt ein filter, das alle Zahlen aus einer some_numbers-Liste eliminiert, die durch drei geteilt werden kann.
some_numbers = list(range(1, 10))
print(list(filter(lambda x: x % 3, some_numbers)))[1, 2, 4, 5, 7, 8]
Früher wurde die reduce()-Funktion zum Zusammenführen von Listeneingaben in einen Wert in Python implementiert. Pythons ursprünglicher Autor Guido van Rossum entfernte es jedoch aus dem eingebauten Namensraum in Python 3 — es lebt jetzt in functools.reduce (lesen Sie seinen Beitrag ]), weshalb es hier nicht als eingebautes Element dargestellt wird.
Learning Success Check-up¶
Machen Sie den Lernerfolgstest für dieses Jupyter-Notebook].
Unfold QR Code
