Verwendung des Python-Debuggers

Einführung

Bei der Softwareentwicklung wird beim * Debuggen * nach Problemen gesucht und diese behoben, die die ordnungsgemäße Ausführung der Software verhindern.

Der Python-Debugger bietet eine Debug-Umgebung für Python-Programme. Es unterstützt das Setzen von bedingten Haltepunkten, das zeilenweise Durchlaufen des Quellcodes, die Stapelprüfung und vieles mehr.

Interaktives Arbeiten mit dem Python-Debugger

Der Python-Debugger wird als Modul mit dem Namen "+ pdb " als Teil der Standard-Python-Distribution geliefert. Der Debugger ist auch erweiterbar und wird als die Klasse " Pdb " definiert. Sie können die https://docs.python.org/3/library/pdb.html[offizielle Dokumentation von ` pdb +`] lesen, um mehr zu erfahren.

Wir beginnen mit einem kurzen Programm mit zwei globalen variables, einem https: // www.digitalocean.com/community/tutorials/how-to-define-functions-in-python-3[function], das eine verschachtelte https://www.digitalocean.com/community/tutorials/how-to-construct- for-loops-in-python-3 [loop] und die Konstruktion + if name == 'main ': +, die die Funktion + nested_loop () + aufruft.

looping.py

num_list = [500, 600, 700]
alpha_list = ['x', 'y', 'z']


def nested_loop():
   for number in num_list:
       print(number)
       for letter in alpha_list:
           print(letter)

if __name__ == '__main__':
   nested_loop()

Wir können dieses Programm jetzt mit dem folgenden Befehl über den Python-Debugger ausführen:

python -m pdb looping.py

Das Befehlszeilen-Flag "+ -m " importiert ein beliebiges Python-Modul für Sie und führt es als Skript aus. In diesem Fall importieren wir das ` pdb +` - Modul und führen es aus, das wir wie oben gezeigt an den Befehl übergeben.

Wenn Sie diesen Befehl ausführen, erhalten Sie die folgende Ausgabe:

Output> /Users/sammy/looping.py(1)<module>()
-> num_list = [500, 600, 700]
(Pdb)

In der Ausgabe enthält die erste Zeile den aktuellen Modulnamen (wie mit "+ <Modul> " angegeben) mit einem Verzeichnispfad und die darauf folgende gedruckte Zeilennummer (in diesem Fall ist es " 1 ", aber wenn vorhanden) ist ein Kommentar oder eine andere nicht ausführbare Zeile, könnte eine höhere Zahl sein). Die zweite Zeile zeigt die aktuelle Quellcodezeile, die hier ausgeführt wird, da " pdb " eine interaktive Konsole zum Debuggen bereitstellt. Sie können den Befehl " help " verwenden, um seine Befehle zu lernen, und " help ", um mehr über einen bestimmten Befehl zu erfahren. Beachten Sie, dass sich die ` pdb +` - Konsole von der interaktiven Python-Shell unterscheidet.

Der Python-Debugger wird automatisch neu gestartet, wenn das Ende Ihres Programms erreicht ist. Wenn Sie die + pdb + Konsole verlassen möchten, geben Sie den Befehl + quit + oder + exit + ein. Wenn Sie ein Programm an einer beliebigen Stelle im Programm explizit neu starten möchten, können Sie dies mit dem Befehl + run + tun.

Verwenden des Debuggers zum Durchlaufen eines Programms

Wenn Sie mit Programmen im Python-Debugger arbeiten, verwenden Sie wahrscheinlich die Befehle "+ list", "+ step" und "+ next", um durch den Code zu navigieren. Wir werden diese Befehle in diesem Abschnitt durchgehen.

Innerhalb der Shell können wir den Befehl "+ list" eingeben, um den Kontext um die aktuelle Zeile herum abzurufen. Von der ersten Zeile des Programms + looping.py +, die wir oben angezeigt haben - + num_list = [500, 600, 700] + - sieht das so aus:

(Pdb)
 1  -> num_list = [500, 600, 700]
 2     alpha_list = ['x', 'y', 'z']
 3
 4
 5     def nested_loop():
 6         for number in num_list:
 7             print(number)
 8             for letter in alpha_list:
 9                 print(letter)
10
11     if __name__ == '__main__':
(Pdb)

Die aktuelle Zeile wird mit den Zeichen "+ → +" angezeigt, in unserem Fall der ersten Zeile der Programmdatei.

Da es sich um ein relativ kurzes Programm handelt, erhalten wir fast das gesamte Programm mit dem Befehl "+ list " zurück. Ohne Angabe von Argumenten werden mit dem Befehl " list +" 11 Zeilen um die aktuelle Zeile herum angegeben. Sie können jedoch auch angeben, welche Zeilen eingeschlossen werden sollen, z.

(Pdb)
 3
 4
 5     def nested_loop():
 6         for number in num_list:
 7             print(number)
(Pdb)

Hier haben wir angefordert, dass die Zeilen 3-7 mit dem Befehl + list 3, 7 + angezeigt werden.

Um zeilenweise durch das Programm zu navigieren, können wir + step + oder + next + verwenden:

(Pdb)
> /Users/sammy/looping.py(2)<module>()
-> alpha_list = ['x', 'y', 'z']
(Pdb)
(Pdb)
> /Users/sammy/looping.py(2)<module>()
-> alpha_list = ['x', 'y', 'z']
(Pdb)

Der Unterschied zwischen "+ step " und " next " besteht darin, dass " step " innerhalb einer aufgerufenen Funktion anhält, während " next +" aufgerufene Funktionen ausführt, um nur in der nächsten Zeile der aktuellen Funktion anzuhalten. Wir können diesen Unterschied sehen, wenn wir mit der Funktion arbeiten.

Der Befehl "+ step " durchläuft die Schleifen, sobald die Funktion ausgeführt wird, und zeigt genau, was die Schleife tut, da sie zuerst eine Zahl mit " print (number) " ausgibt und dann zum Drucken durchläuft die Buchstaben mit ` print (letter) +`, Rückkehr zur Zahl, etc:

(Pdb)
> /Users/sammy/looping.py(5)<module>()
-> def nested_loop():
(Pdb)
> /Users/sammy/looping.py(11)<module>()
-> if __name__ == '__main__':
(Pdb)
> /Users/sammy/looping.py(12)<module>()
-> nested_loop()
(Pdb)
--Call--
> /Users/sammy/looping.py(5)nested_loop()
-> def nested_loop():
(Pdb)
> /Users/sammy/looping.py(6)nested_loop()
-> for number in num_list:
(Pdb)
> /Users/sammy/looping.py(7)nested_loop()
-> print(number)
(Pdb)
500
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb)
> /Users/sammy/looping.py(9)nested_loop()
-> print(letter)
(Pdb)
x
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb)
> /Users/sammy/looping.py(9)nested_loop()
-> print(letter)
(Pdb)
y
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb)

Stattdessen wird mit dem Befehl "+ next " die gesamte Funktion ausgeführt, ohne dass der schrittweise Vorgang angezeigt wird. Beenden Sie die aktuelle Sitzung mit dem Befehl " exit +" und starten Sie den Debugger erneut:

python -m pdb looping.py

Jetzt können wir mit dem Befehl + next + arbeiten:

(Pdb)
> /Users/sammy/looping.py(5)<module>()
-> def nested_loop():
(Pdb)
> /Users/sammy/looping.py(11)<module>()
-> if __name__ == '__main__':
(Pdb)
> /Users/sammy/looping.py(12)<module>()
-> nested_loop()
(Pdb)
500
x
y
z
600
x
y
z
700
x
y
z
--Return--
> /Users/sammy/looping.py(12)<module>()->None
-> nested_loop()
(Pdb)

Während Sie Ihren Code durchgehen, möchten Sie möglicherweise den Wert untersuchen, der an eine Variable übergeben wurde. Dies können Sie mit dem Befehl + pp + tun, der den Wert des Ausdrucks mithilfe von https: //docs.python hübsch ausgibt. org / 3 / library / pprint.html # module-pprint [+ pprint + module]:

(Pdb)
[500, 600, 700]
(Pdb)

Die meisten Befehle in + pdb + haben kürzere Aliase. Für "+ step " lautet diese Kurzform " s " und für " next " " n ". Der Befehl ` help ` listet die verfügbaren Aliase auf. Sie können den zuletzt aufgerufenen Befehl auch aufrufen, indem Sie an der Eingabeaufforderung die Taste ` ENTER +` drücken.

Haltepunkte

In der Regel arbeiten Sie mit größeren Programmen als im obigen Beispiel. Daher möchten Sie sich wahrscheinlich bestimmte Funktionen oder Zeilen ansehen, anstatt ein gesamtes Programm durchzuarbeiten. Mit dem Befehl "+ break +" zum Festlegen von Haltepunkten führen Sie das Programm bis zum angegebenen Haltepunkt aus.

Wenn Sie einen Haltepunkt einfügen, weist der Debugger ihm eine Nummer zu. Die den Haltepunkten zugewiesenen Nummern sind aufeinanderfolgende Ganzzahlen, die mit der Nummer 1 beginnen, auf die Sie sich beziehen können, wenn Sie mit Haltepunkten arbeiten.

Haltepunkte können an bestimmten Zeilennummern platziert werden, indem die Syntax von + <Programmdatei>: <Zeilennummer> + wie unten gezeigt verwendet wird:

(Pdb)
Breakpoint 1 at /Users/sammy/looping.py:5
(Pdb)

Geben Sie "+ clear " und dann " y +" ein, um alle aktuellen Haltepunkte zu entfernen. Sie können dann einen Haltepunkt platzieren, an dem eine Funktion definiert ist:

(Pdb)
Breakpoint 1 at /Users/sammy/looping.py:5
(Pdb)

Um aktuelle Haltepunkte zu entfernen, geben Sie "+ clear " und dann " y +" ein. Sie können auch eine Bedingung einrichten:

(Pdb)
Breakpoint 1 at /Users/sammy/looping.py:7
(Pdb)

Wenn wir nun den Befehl "+ continue " eingeben, bricht das Programm ab, wenn die " number +" + x + "als größer als 500 ausgewertet wird (dh wenn sie in der zweiten Iteration von auf 600 gesetzt wird äußere Schleife):

(Pdb)
500
x
y
z
> /Users/sammy/looping.py(7)nested_loop()
-> print(number)
(Pdb)

Verwenden Sie den Befehl "+ break +" ohne Argumente, um eine Liste der aktuell ausgeführten Haltepunkte anzuzeigen. Sie erhalten Informationen zu den Besonderheiten der von Ihnen festgelegten Haltepunkte:

(Pdb)
Num Type         Disp Enb   Where
1   breakpoint   keep yes   at /Users/sammy/looping.py:7
   stop only if number > 500
   breakpoint already hit 2 times
(Pdb)

Wir können einen Haltepunkt auch mit dem Befehl "+ disable +" und der Nummer des Haltepunkts deaktivieren. In dieser Sitzung fügen wir einen weiteren Haltepunkt hinzu und deaktivieren dann den ersten:

(Pdb)
Breakpoint 2 at /Users/sammy/looping.py:11
(Pdb)
Disabled breakpoint 1 at /Users/sammy/looping.py:7
(Pdb)
Num Type         Disp Enb   Where
1   breakpoint   keep     at /Users/sammy/looping.py:7
   stop only if number > 500
   breakpoint already hit 2 times
2   breakpoint   keep    at /Users/sammy/looping.py:11
(Pdb)

Verwenden Sie zum Aktivieren eines Haltepunkts den Befehl "+ enable " und zum vollständigen Entfernen eines Haltepunkts den Befehl " clear +":

(Pdb) enable 1
Enabled breakpoint 1 at /Users/sammy/looping.py:7
(Pdb) clear 2
Deleted breakpoint 2 at /Users/sammy/looping.py:11
(Pdb)

Haltepunkte in + pdb + bieten Ihnen viel Kontrolle. Einige zusätzliche Funktionen umfassen das Ignorieren von Haltepunkten während der aktuellen Iteration des Programms mit dem Befehl "+ Ignorieren " (wie in " Ignorieren 1 ") und das Auslösen von Aktionen an einem Haltepunkt mit dem Befehl " Befehle " (wie in "") Befehl 1 + `) und das Erstellen temporärer Haltepunkte, die automatisch gelöscht werden, wenn die erste Programmausführung den Punkt mit dem Befehl" + tbreak + "erreicht (für eine temporäre Unterbrechung in Zeile 3 können Sie beispielsweise" + tbreak 3 + "eingeben ).

Einbindung von + pdb + in Programme

Sie können eine Debugsitzung auslösen, indem Sie das Modul "+ pdb " importieren und die Funktion " pdb " " pdb.set_trace () +" über der Zeile einfügen, in der die Sitzung beginnen soll.

In unserem obigen Beispielprogramm fügen wir die Anweisung "+ import +" und die Funktion hinzu, in die wir den Debugger eingeben möchten. In unserem Beispiel fügen wir es vor der verschachtelten Schleife hinzu.

# Import pdb module
import pdb

num_list = [500, 600, 700]
alpha_list = ['x', 'y', 'z']


def nested_loop():
   for number in num_list:
       print(number)

       # Trigger debugger at this line
       pdb.set_trace()
       for letter in alpha_list:
           print(letter)

if __name__ == '__main__':
   nested_loop()

Durch Hinzufügen des Debuggers zu Ihrem Code müssen Sie Ihr Programm nicht auf besondere Weise starten oder daran denken, Haltepunkte festzulegen.

Wenn Sie das + pdb + - Modul importieren und die Funktion + pdb.set_trace () + ausführen, können Sie Ihr Programm wie gewohnt starten und den Debugger durchlaufen lassen.

Programmablauf ändern

Mit dem Python-Debugger können Sie den Programmfluss zur Laufzeit mit dem Befehl + jump + ändern. Auf diese Weise können Sie vorwärts springen, um die Ausführung von Code zu verhindern, oder rückwärts gehen, um den Code erneut auszuführen.

Wir werden mit einem kleinen Programm arbeiten, das eine Liste der Buchstaben erstellt, die in der Zeichenfolge "+ sammy =" sammy "+" enthalten sind:

letter_list.py

def print_sammy():
   sammy_list = []
   sammy = "sammy"
   for letter in sammy:
       sammy_list.append(letter)
       print(sammy_list)

if __name__ == "__main__":
   print_sammy()

Wenn wir das Programm wie gewohnt mit dem Befehl "+ python letter_list.py +" ausführen, erhalten wir die folgende Ausgabe:

Output['s']
['s', 'a']
['s', 'a', 'm']
['s', 'a', 'm', 'm']
['s', 'a', 'm', 'm', 'y']

Lassen Sie uns mit dem Python-Debugger zeigen, wie wir die Ausführung ändern können, indem wir erst nach dem ersten Zyklus * weiterspringen *. Wenn wir dies tun, werden wir feststellen, dass es eine Unterbrechung der https://www.digitalocean.com/community/tutorials/how-to-construct-for-loops-in-python-3 [+ for + gibt Schleife]:

python -m pdb letter_list.py
> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb)
 1  -> def print_sammy():
 2         sammy_list = []
 3         sammy = "sammy"
 4         for letter in sammy:
 5             sammy_list.append(letter)
 6             print(sammy_list)
 7
 8     if __name__ == "__main__":
 9         print_sammy()
10
11
(Pdb)
Breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb)
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb)
's'
(Pdb)
['s']
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb)
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb)
'a'
(Pdb)
Disabled breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb)
['s']
['s', 'm']
['s', 'm', 'm']
['s', 'm', 'm', 'y']

In der obigen Debug-Sitzung wird in Zeile 5 eine Unterbrechung eingefügt, um zu verhindern, dass der Code fortgesetzt wird. Anschließend wird der Code fortgesetzt (zusammen mit dem hübschen Drucken einiger Werte von "+ letter ", um anzuzeigen, was gerade passiert). Als nächstes verwenden wir den Befehl ` jump `, um zu Zeile 6 zu springen. Zu diesem Zeitpunkt wird die Variable " letter " gleich der Zeichenfolge "" a "" gesetzt, aber wir überspringen den Code, der diese zur Liste " sammy_list " hinzufügt. Wir deaktivieren dann den Haltepunkt, um die Ausführung wie gewohnt mit dem Befehl " continue " fortzusetzen, sodass "" niemals ein "" an " sammy_list +" angehängt wird.

Als nächstes können wir diese erste Sitzung beenden und den Debugger neu starten, um innerhalb des Programms zurückzuspringen und eine bereits ausgeführte Anweisung erneut auszuführen. Dieses Mal führen wir die erste Iteration der "+ für" -Schleife im Debugger erneut aus:

> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb)
 1  -> def print_sammy():
 2         sammy_list = []
 3         sammy = "sammy"
 4         for letter in sammy:
 5             sammy_list.append(letter)
 6             print(sammy_list)
 7
 8     if __name__ == "__main__":
 9         print_sammy()
10
11
(Pdb)
Breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb)
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb)
's'
(Pdb)
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb)
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb)
's'
(Pdb)
Disabled breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb)
['s', 's']
['s', 's', 'a']
['s', 's', 'a', 'm']
['s', 's', 'a', 'm', 'm']
['s', 's', 'a', 'm', 'm', 'y']

In der obigen Debugging-Sitzung haben wir in Zeile 6 eine Unterbrechung eingefügt und sind dann nach dem Fortfahren zu Zeile 5 zurückgesprungen. Wir haben auf dem Weg hübsch gedruckt, um zu zeigen, dass die Zeichenfolge '' '' zweimal an die Liste '+ sammy_list ' angehängt wurde. Wir haben dann die Pause in Zeile 6 deaktiviert und das Programm weiter ausgeführt. Die Ausgabe zeigt zwei Werte von '' '', die an ' sammy_list +' angehängt sind.

Einige Sprünge werden vom Debugger verhindert, insbesondere wenn bestimmte Flusssteuerungsanweisungen, die nicht definiert sind, ein- und ausgeblendet werden. Beispielsweise können Sie nicht in Funktionen springen, bevor Argumente definiert wurden, und Sie können nicht in die Mitte einer Anweisung + try: except + springen. Sie können auch nicht aus einem "+ finally +" - Block herausspringen.

Die Anweisung + jump + mit dem Python-Debugger ermöglicht es Ihnen, den Ausführungsfluss während des Debuggens eines Programms zu ändern, um festzustellen, ob die Flusssteuerung für verschiedene Zwecke geändert werden kann, oder um besser zu verstehen, welche Probleme in Ihrem Code auftreten.

Tabelle der gebräuchlichen + pdb + Befehle

Hier finden Sie eine Tabelle mit nützlichen "+ pdb +" - Befehlen sowie deren Kurzformen, die Sie bei der Arbeit mit dem Python-Debugger berücksichtigen sollten.

Command Short form What it does

args

a

Print the argument list of the current function

break

b

Creates a breakpoint (requires parameters) in the program execution

continue

c or cont

Continues program execution

help

h

Provides list of commands or help for a specified command

jump

j

Set the next line to be executed

list

l

Print the source code around the current line

next

n

Continue execution until the next line in the current function is reached or returns

step

s

Execute the current line, stopping at first possible occasion

pp

pp

Pretty-prints the value of the expression

quit or exit

q

Aborts the program

return

r

Continue execution until the current function returns

Weitere Informationen zu den Befehlen und zum Arbeiten mit dem Debugger finden Sie in der Python-Debugger-Dokumentation.

Fazit

Das Debuggen ist ein wichtiger Schritt in jedem Softwareentwicklungsprojekt. Der Python-Debugger "+ pdb +" implementiert eine interaktive Debugging-Umgebung, die Sie mit jedem Ihrer in Python geschriebenen Programme verwenden können.

Mit Funktionen, mit denen Sie Ihr Programm anhalten, überprüfen können, auf welche Werte Ihre Variablen eingestellt sind, und die Programmausführung Schritt für Schritt diskret durchführen können, können Sie die Funktionsweise Ihres Programms besser verstehen und Fehler finden, in denen sie vorhanden sind die Logik oder bekannte Probleme zu beheben.