Name einer Python-Funktion in einem Stack-Trace

8

In Python2 und Python3 wird im Stack-Trace der __name__ einer Funktion nicht verwendet, stattdessen wird der ursprüngliche Name (der nach def angegeben wird) verwendet.

Betrachten Sie das Beispiel:

%Vor%

Die Ausgabe ist:

%Vor%

Warum? Wie ändere ich den Namen, der im Stack-Trace verwendet wird? Wo wird dann das Attribut __name__ verwendet?

    
Vadim Pushtaev 17.11.2016, 17:50
quelle

2 Antworten

7

Grundsätzlich hat also jede Funktion drei Dinge, die als Name der Funktion betrachtet werden können:

Der ursprüngliche Name des Codeblocks

Es ist in f.__code__.co_name gespeichert (wobei f das Funktionsobjekt ist). Wenn Sie def orig_name zum Erstellen einer Funktion verwenden, ist orig_name der Name. Für Lambas ist es <lambda> .

Dieses Attribut ist schreibgeschützt und kann nicht geändert werden. Die einzige Möglichkeit, eine Funktion mit dem benutzerdefinierten Namen in Runtime zu erstellen, die mir bekannt ist, ist exec :

%Vor%

(Es gibt auch mehr Low-Level Möglichkeit, dies zu tun das wurde in einem Kommentar zu der Frage erwähnt.)

Die Unveränderbarkeit von co_name macht eigentlich Sinn: Damit können Sie sicher sein, dass der Name, den Sie im Debugger (oder nur Stack-Trace) sehen, genau dem im Quellcode entspricht (zusammen mit dem Dateinamen und der Zeile) Nummer).

Das Attribut __name__ des Funktionsobjekts

Es ist auch ein Alias ​​für func_name .

Sie können es ändern ( orig_name.__name__ = 'updated name' ) und Sie tun dies auf jeden Fall täglich: @functools.wraps kopiert die __name__ der dekorierten Funktion in die neue.

__name__ wird von Tools wie pydoc verwendet, deshalb benötigen Sie @functools.wraps : Sie sehen also nicht die technischen Details jedes Decorators in Ihrer Dokumentation. Schauen Sie sich das Beispiel an:

%Vor%

Hier ist die pydoc Ausgabe:

%Vor%

Mit wraps gibt es kein Zeichen von decorated in der Dokumentation.

Name der Referenz

Eine weitere Sache, die Funktionsname genannt werden kann (obwohl es kaum ist), ist der Name einer Variablen oder eines Attributs, in dem die Referenz auf diese Funktion gespeichert ist.

Wenn Sie eine Funktion mit def name erstellen, wird das Attribut name zum aktuellen Bereich hinzugefügt. Im Fall von lambda sollten Sie das Ergebnis einer Variablen zuweisen: name = lambda: None .

Offensichtlich können Sie mehr als einen Verweis auf die gleiche Funktion erstellen und alle Referenzen können unterschiedliche Namen haben.

Die einzige Möglichkeit, all diese drei Dinge miteinander zu verbinden, ist die def foo -Anweisung, die das Funktionsobjekt mit __name__ und __code__.co_name gleich foo erstellt und sie dem foo -Attribut des aktueller Umfang. Sie sind jedoch in keiner Weise gebunden und können sich voneinander unterscheiden:

%Vor%

Ausgabe:

%Vor%

Pydoc:

%Vor%

Ich danke anderen Menschen für Kommentare und Antworten, sie haben mir geholfen, meine Gedanken und mein Wissen zu organisieren .

    
Vadim Pushtaev 17.11.2016, 19:57
quelle
2

Versucht, die CPython Implementierung zu erkunden, definitiv kein Experte. Wie in den Kommentaren erwähnt, wird beim Drucken des Stapeleintrags von f das Attribut f.__code__.co_name wird verwendet . Außerdem wird f.__name__ anfänglich auf f.__code__.co_name , aber wenn Sie ersteres ändern, wird letzteres nicht entsprechend geändert.

Deshalb habe ich versucht, das direkt zu ändern, aber es ist nicht möglich:

%Vor%

Warum gibt es zwei Möglichkeiten, den Namen einer Funktion zu sagen? Nun, laut der Dokumentation ist __name__ definiert für "class, function, Methode, Deskriptor oder Generatorinstanz ", so wird sie im Fall von Funktionen diesem Attribut zugeordnet, für andere Objekte wird sie auf etwas anderes abgebildet.

    
BlackBear 17.11.2016 18:16
quelle

Tags und Links