Dokumentation in Python
Wir hatten am Anfang des Kurses Literale ausgewertet.
- Konsoleninteraktion
"abc"
'abc'
In einer Funktionsdefinition befindliche Auswertungsanweisungen werden weiterhin ausgeführt, aber der Wert des Ausdrucks wird nicht ausgegeben, wie dies bei der Eingabe eines Ausdrucks in die Konsole geschähe.
main.py
def f():
"abc"f()
- Protokoll
- (keine Ausgabe)
Ein Dokumentationstext wird normalerweise in dreifachen Anführungszeichen als erste Anweisung einer Funktion geschrieben. Es wären aber auch einfache Anführungszeichen erlaubt.
main.py
def zehnfach( x ):
"""Ergibt das Zehnfache des Argumentwerts
x -- eine Zahl
>>> zehnfach( 2 )
20
>>> zehnfach( 50 )
500
>>> zehnfach( 0 )
0
>>> zehnfach( -8 )
-80
"""
return 11 * x
Der Dokumentationstext wird bei der Erklärung der Funktion verwendet.
- Protokoll
help( zehnfach )
Help on function zehnfach in module __main__:
zehnfach(x)
Ergibt das Zehnfache des Argumentwerts
x -- eine Zahl
>>> zehnfach( 2 )
20
>>> zehnfach( 50 )
500
>>> zehnfach( 0 )
0
>>> zehnfach( -8 )
-80
Wenn Beispiele in der gezeigten Schreibweise verwendet wurden, können sie automatisch für Überprüfungen der Funktion verwendet werden.
- Protokoll
from doctest import testmod
testmod()
**********************************************************************
File "__main__", line 4, in __main__.zehnfach
Failed example:
zehnfach( 2 )
Expected:
20
Got:
22
**********************************************************************
File "__main__", line 6, in __main__.zehnfach
Failed example:
zehnfach( 50 )
Expected:
500
Got:
550
**********************************************************************
File "__main__", line 10, in __main__.zehnfach
Failed example:
zehnfach( -8 )
Expected:
-80
Got:
-88
**********************************************************************
1 items had failures:
3 of 4 in __main__.zehnfach
***Test Failed*** 3 failures.
TestResults(failed=3, attempted=4)
Wir zeigen im folgenden eine korrigierte Fassung der Funktionsdefinition.
- Neue Funktionsdefinition
def zehnfach( x ):
"""Ergibt das Zehnfache des Argumentwerts
x -- eine Zahl
>>> zehnfach( 2 )
20
>>> zehnfach( 50 )
500
>>> zehnfach( 0 )
0
>>> zehnfach( -8 )
-80
"""
return 10 * x
Die Überprüfung ergibt dann keine Fehlermeldungen mehr.
- Protokoll
testmod()
TestResults(failed=0, attempted=4)
Relevanz des Testens
Eine Python -Implementation findet oft Fehler in Funktionsdefinitionen nicht, die von Übersetzern anderer Programmiersprachen in entsprechenden Programmen gefunden werden würden.
main.py
def f():
print( l )print( 'Hallo Welt!' )
- Protokoll
Hallo Welt!
Erst durch Testen können solche Fehler gefunden werden.
main.py
def f():
"""
>>> f()
1"""
print( l )from doctest import testmod
testmod()print( 'Hallo Welt!' )
- Protokoll (gekürzt)
NameError: name 'l' is not defined
Hallo Welt!
In vielen anderen Programmiersprachen würde ein falscher Variablenname (oder ein unpassender Typ) oft schon beim Übersetzen eines Programmes als Fehler erkannt und gemeldet werden, ohne daß ein extra Testaufruf notwendig wird.
Daher ist es in Python noch wichtiger als in anderen Programmiersprachen, Software zu testen.
Funktionen sind testbare Software-Einheiten. Erst durch die Zerlegung eines großen Programmes in viele kleine Funktionen läßt sich ein großes Programm noch sinnvoll dokumentieren und testen (indem die einzelnen Funktionen dokumentiert und getestet werden, aus denen es besteht). Dies ist eine große Hilfe bei der Beherrschung großer Programme.
Testen von Werten und Ausgaben ⃗
Über die Konsolenschreibweise in Dokumentationskommentaren können sowohl Rückgabewerte als auch Ausgaben getestet werden.
main.py
def f():
"""
>>> f()
20"""
return 20from doctest import testmod
testmod()
- Protokoll
main.py
def f():
"""
>>> f()
20"""
print( 20 )from doctest import testmod
testmod()
- Protokoll
Es ist zu beachten, daß das »>>>« innerhalb des Dokumentationstextes so weit eingerückt sein muß wie die erwartete Ausgabe.
main.py
def f():
""">>> f()
20"""
print( 20 )from doctest import testmod
testmod()
- Protokoll
Expected:
20Got:
20
Ausdrücken des Quelltextes ⃗
Geht nur bei in Python geschriebenen Funktionen!
main.py
from inspect import getsource
def f():
print( l )print( getsource( f ))
- Protokoll
def f():
print( l )
main.py
from inspect import getsource
from doctest import testmod
print( getsource( testmod ))
- Protokoll
def testmod(m=None, name=None, globs=None, verbose=None,
- …
Quellen
Die Stilfibel für Dokumentationskommentare
Die Stilfibel PEP 257 enthält Hinweise zum Stil von Dokumentationskommentaren.
- Die Stilfibel für Dokumentationskommentare PEP 257
https://www.python.org/dev/peps/pep-0257/
Übungsaufgaben
/ Refaktor
In dem folgenden Programm greifen viele Funktionen auf globale Variablen zu.
Versuchen Sie, einen Teil dieser Zugriffe (im besten Fall: alle) zu eliminieren.
main.py
import urllib.request
def setup_a_direct_connection_to_the_internet():
"""this is a hack to impede the use of any proxy"""
urllib.request.getproxies = lambda: {}
urllib.request.proxy_bypass = lambda host, proxies=None: 1def setup_globals():
global URI; URI = 'https://example.com'
global context; context = __import__( 'ssl' )._create_unverified_context()
global headers; headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64)' }
global online; online = Truedef fetch_page():
global source
if online:
request = urllib.request.Request( URI, headers=headers )
html = urllib.request.urlopen( request, context=context )
source = html.read()
with open( 'cache.bin', 'wb' ) as file: file.write( source )
else:
with open( 'cache.bin', 'rb' ) as file: source = file.read()def process_page():
global source
source = source.decode( 'utf-8' ).replace( '\t', ' ' )
print( 'source', f"{source}" )setup_a_direct_connection_to_the_internet()
setup_globals()
fetch_page()
process_page()- Protokoll
source <!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
width: 600px;
margin: 5em auto;
padding: 50px;
background-color: #fff;
border-radius: 1em;
}
a:link, a:visited {
color: #38488f;
text-decoration: none;
}
@media (max-width: 700px) {
body {
background-color: #fff;
}
div {
width: auto;
margin: 0 auto;
border-radius: 0;
padding: 1em;
}
}
</style>
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is established to be used for illustrative examples in documents. You may use this
domain in examples without prior coordination or asking for permission.</p>
<p><a href="http://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>