Reflection and introspection
Julia bietet eine Vielzahl von Laufzeit-Reflexionsfähigkeiten.
Module bindings
Die öffentlichen Namen für ein Module sind verfügbar über names(m::Module), was ein Array von Symbol Elementen zurückgibt, die die öffentlichen Bindungen darstellen. names(m::Module, all = true) gibt Symbole für alle Bindungen in m zurück, unabhängig vom öffentlichen Status.
DataType fields
Die Namen der DataType-Felder können mit fieldnames abgefragt werden. Zum Beispiel gibt der folgende Typ fieldnames(Point) ein Tupel von Symbols zurück, die die Feldnamen darstellen:
julia> struct Point
x::Int
y
end
julia> fieldnames(Point)
(:x, :y)Der Typ jedes Feldes in einem Point-Objekt wird im types-Feld der Point-Variablen selbst gespeichert:
julia> Point.types
svec(Int64, Any)Während x als Int annotiert ist, wurde y in der Typdefinition nicht annotiert, daher wird y standardmäßig vom Typ Any.
Typen werden selbst als eine Struktur namens DataType dargestellt:
julia> typeof(Point)
DataTypeBeachten Sie, dass fieldnames(DataType) die Namen für jedes Feld von DataType selbst zurückgibt, und eines dieser Felder ist das types-Feld, das im obigen Beispiel beobachtet wurde.
Subtypes
Die direkten Subtypen eines beliebigen DataType können mit subtypes aufgelistet werden. Zum Beispiel hat der abstrakte DataType AbstractFloat vier (konkrete) Subtypen:
julia> InteractiveUtils.subtypes(AbstractFloat)
5-element Vector{Any}:
BigFloat
Core.BFloat16
Float16
Float32
Float64Jeder abstrakte Subtyp wird ebenfalls in dieser Liste enthalten sein, aber weitere Subtypen davon nicht; die rekursive Anwendung von subtypes kann verwendet werden, um den vollständigen Typbaum zu inspizieren.
Beachten Sie, dass subtypes sich innerhalb von InteractiveUtils befindet, aber automatisch exportiert wird, wenn das REPL verwendet wird.
DataType layout
Die interne Darstellung eines DataType ist entscheidend, wenn man mit C-Code interagiert, und mehrere Funktionen stehen zur Verfügung, um diese Details zu inspizieren. isbitstype(T::DataType) gibt true zurück, wenn T mit C-kompatibler Ausrichtung gespeichert ist. fieldoffset(T::DataType, i::Integer) gibt den (Byte-)Offset für das Feld i relativ zum Beginn des Typs zurück.
Function methods
Die Methoden einer beliebigen Funktion können mit methods aufgelistet werden. Die Methodenzuordnungstabelle kann nach Methoden durchsucht werden, die einen bestimmten Typ akzeptieren, mit methodswith.
Expansion and lowering
Wie im Abschnitt Metaprogramming besprochen, gibt die Funktion macroexpand den unquoted und interpolierten Ausdruck (Expr) für ein gegebenes Makro zurück. Um macroexpand zu verwenden, muss der Ausdrucksblock selbst quote-iert werden (ansonsten wird das Makro ausgewertet und das Ergebnis wird stattdessen übergeben!). Zum Beispiel:
julia> InteractiveUtils.macroexpand(@__MODULE__, :(@edit println("")) )
:(InteractiveUtils.edit(println, (Base.typesof)("")))Die Funktionen Base.Meta.show_sexpr und dump werden verwendet, um S-expr-Stilansichten und tief verschachtelte Detailansichten für beliebige Ausdrücke anzuzeigen.
Schließlich gibt die Meta.lower-Funktion die lowered-Form eines beliebigen Ausdrucks zurück und ist von besonderem Interesse, um zu verstehen, wie Sprachkonstrukte auf primitive Operationen wie Zuweisungen, Verzweigungen und Aufrufe abgebildet werden:
julia> Meta.lower(@__MODULE__, :( [1+2, sin(0.5)] ))
:($(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 ─ %1 = 1 + 2
│ %2 = sin(0.5)
│ %3 = Base.vect(%1, %2)
└── return %3
))))Intermediate and compiled representations
Die Inspektion der gesenkten Form für Funktionen erfordert die Auswahl der spezifischen Methode zur Anzeige, da generische Funktionen viele Methoden mit unterschiedlichen Typ-Signaturen haben können. Zu diesem Zweck ist eine methodenspezifische Code-Senkung verfügbar, die mit code_lowered verwendet werden kann, und die typ-inferierte Form ist verfügbar mit code_typed. code_warntype fügt der Ausgabe von 4d61726b646f776e2e436f64652822222c2022636f64655f74797065642229_40726566 Hervorhebungen hinzu.
Näher an der Maschine kann die LLVM-Zwischenrepräsentation einer Funktion mit code_llvm ausgegeben werden, und schließlich ist der kompilierte Maschinencode mit code_native verfügbar (dies löst die JIT-Kompilierung/code-Generierung für jede Funktion aus, die zuvor nicht aufgerufen wurde).
Zur Vereinfachung gibt es Makroversionen der oben genannten Funktionen, die Standardfunktionsaufrufe übernehmen und die Argumenttypen automatisch erweitern:
julia> @code_llvm +(1,1)
; @ int.jl:87 within `+`
; Function Attrs: sspstrong uwtable
define i64 @"julia_+_476"(i64 signext %0, i64 signext %1) #0 {
top:
%2 = add i64 %1, %0
ret i64 %2
}Für weitere Informationen siehe @code_lowered, @code_typed, @code_warntype, @code_llvm, und @code_native.
Printing of debug information
Die oben genannten Funktionen und Makros nehmen das Schlüsselwortargument debuginfo, das das Niveau der ausgegebenen Debug-Informationen steuert.
julia> InteractiveUtils.@code_typed debuginfo=:source +(1,1)
CodeInfo(
@ int.jl:87 within `+`
1 ─ %1 = Base.add_int(x, y)::Int64
└── return %1
) => Int64Mögliche Werte für debuginfo sind: :none, :source und :default. Standardmäßig werden keine Debug-Informationen ausgegeben, dies kann jedoch geändert werden, indem Base.IRShow.default_debuginfo[] = :source gesetzt wird.