Alles, was man über Pakete in Go wissen muss

Paketinitialisierung

Wenn wir ein Go-Programm ausführen, folgt der Go-Compiler einer bestimmten Ausführungsreihenfolge für Pakete, Dateien in einem Paket und Variablendeklarationen in diesen Dateien.

Paketbereich

Ein Bereich ist eine Region in einem Codeblock, in der eine definierte Variable zugänglich ist. Ein Paketbereich ist ein Bereich innerhalb eines Pakets, in dem eine deklarierte Variable innerhalb eines Pakets (über alle Dateien des Pakets) zugänglich ist. Dieser Bereich ist der oberste Block einer jeden Datei im Paket.

Schauen Sie sich den Befehl go run an. Diesmal wird nicht eine Datei ausgeführt, sondern ein glob-Muster, um alle Dateien im Paket app zur Ausführung einzuschließen.

Go ist schlau genug, um den Einstiegspunkt der Anwendung herauszufinden, der entry.go ist, weil er die Funktion main hat. Wir können auch einen Befehl wie den folgenden verwenden (die Reihenfolge der Dateinamen spielt keine Rolle).

go run src/app/version.go src/app/entry.go

💡 Sie können auch den Befehl go run app verwenden, der dasselbe tut, und dies ist ein besserer Ansatz, um ein Paket auszuführen. Der Befehl go install oder go build erfordert einen Paketnamen, der alle Dateien innerhalb eines Pakets einschließt, so dass wir sie nicht wie oben angeben müssen.

Zurück zu unserem Hauptproblem: Wir können die Variable version, die in der Datei version.go deklariert ist, von überall im Paket verwenden, auch wenn sie nicht exportiert wird (wie Version), da sie im Paketbereich deklariert ist.

Wenn die Variable version innerhalb einer Funktion deklariert worden wäre, wäre sie nicht im Paketumfang gewesen und das obige Programm hätte sich nicht kompilieren lassen.

Es ist nicht erlaubt, eine globale Variable mit demselben Namen im selben Paket neu zu deklarieren. Daher kann eine einmal deklarierte version Variable im Paketbereich nicht erneut deklariert werden. Aber man kann sie an anderer Stelle neu deklarieren.

Variableninitialisierung

Wenn eine Variable a von einer anderen Variable b abhängt, sollte b vorher definiert werden, sonst wird das Programm nicht kompiliert. Go befolgt diese Regel innerhalb von Funktionen.

Wenn diese Variablen jedoch im Paketumfang definiert sind, werden sie in Initialisierungszyklen deklariert. Schauen wir uns das folgende einfache Beispiel an.

Im obigen Beispiel wird zuerst c deklariert, weil sein Wert bereits deklariert ist. Im späteren Initialisierungszyklus wird b deklariert, weil es von c abhängt und der Wert von c bereits deklariert ist. Im letzten Initialisierungszyklus wird a deklariert und dem Wert von b zugewiesen. Go kann komplexe Initialisierungszyklen wie die folgenden handhaben.

Im obigen Beispiel wird zuerst c deklariert und dann b, da sein Wert von c abhängt und schließlich a, da sein Wert von b abhängt. Sie sollten jede Initialisierungsschleife wie unten vermeiden, bei der die Initialisierung in eine rekursive Schleife gerät.

Ein anderes Beispiel für den Geltungsbereich eines Pakets wäre, eine Funktion f in einer separaten Datei zu haben, die die Variable c aus der Hauptdatei referenziert.

Init-Funktion

Wie main-Funktion, initFunktion von Go aufgerufen, wenn ein Paket initialisiert wird. Sie nimmt keine Argumente entgegen und gibt keinen Wert zurück.

Die init-Funktion wird von Go implizit deklariert, daher kann man sie von nirgendwo referenzieren (oder wie init() aufrufen). Sie können mehrere init-Funktionen in einer Datei oder einem Paket haben. Die Reihenfolge der Ausführung von init-Funktionen in einer Datei richtet sich nach der Reihenfolge ihres Auftretens.

Sie können init-Funktionen überall im Paket haben. Diese init-Funktionen werden in lexikalischer Reihenfolge der Dateinamen (alphabetische Reihenfolge) aufgerufen.

Nachdem alle init-Funktionen ausgeführt wurden, wird die main-Funktion aufgerufen. Die Hauptaufgabe der init-Funktion besteht also darin, globale Variablen zu initialisieren, die nicht im globalen Kontext initialisiert werden können. Zum Beispiel die Initialisierung eines Arrays.

Da die for-Syntax im Paketbereich nicht gültig ist, können wir das Array integers der Größe 10 mit Hilfe der for-Schleife innerhalb der init-Funktion initialisieren.

Paket-Alias

Wenn Sie ein Paket importieren, erstellen Sie eine Variable unter Verwendung der Paketdeklaration des Pakets. Wenn Sie mehrere Pakete mit demselben Namen importieren, führt dies zu einem Konflikt.

// greet/parent.go
package greetvar Message = "Hey there. I am parent."// greet/greet/child.go
package greetvar Message = "Hey there. I am child."

Daher verwenden wir Paket-Alias. Wir geben einen Variablennamen zwischen dem Schlüsselwort import und dem Paketnamen an, der zur neuen Variable wird, um auf das Paket zu verweisen.

Im obigen Beispiel wird das Paket greet/greet nun durch die Variable child referenziert. Wie Sie sehen, haben wir das greet-Paket mit einem Unterstrich versehen.

Der Unterstrich ist ein spezielles Zeichen in Go, das als null-Container fungiert. Da wir das greet-Paket importieren, es aber nicht verwenden, wird sich der Go-Compiler darüber beschweren. Um das zu vermeiden, speichern wir die Referenz dieses Pakets in _ und der Go-Compiler wird es einfach ignorieren.

Ein Paket mit einem Unterstrich zu versehen, der scheinbar nichts bewirkt, ist manchmal ganz nützlich, wenn man ein Paket initialisieren, aber nicht benutzen will.

// greet/parent.go
package greetimport "fmt"var Message = "Hey there. I am parent."func init() {
fmt.Println("greet/parent.go ==> init()")
}// greet/greet/child.go
package greetimport "fmt"var Message = "Hey there. I am child."func init() {
fmt.Println("greet/greet/child.go ==> init()")
}

Das Wichtigste ist, dass ein importiertes Paket nur einmal pro Paket initialisiert wird. Wenn Sie also mehrere Import-Anweisungen in einem Paket haben, wird ein importiertes Paket nur einmal während der Ausführung des Hauptpakets initialisiert.