La parte declarativa de importar paquetes en Go es bastante aburrida y mundana. Solo necesita especificar la directiva de import
y enumerar los paquetes importados. Los IDE modernos hacen este trabajo por usted: ellos mismos sustituyen los paquetes en esta sección, lo cual es muy conveniente. Además, colapsan este bloque para que no interfiera con la vista del código. Te aconsejo que expandas este bloque y lo estudies detenidamente; tal vez encuentres algo inusual allí:
package main import ( "github.com/vigo5190/goimports-example/a" foo "github.com/vigo5190/goimports-example/a" . "github.com/vigo5190/goimports-example/b" _ "github.com/vigo5190/goimports-example/c" )
Si la importación estándar, importa con un sinónimo y _
conocí, luego importa desde .
No he visto antes.
Para empezar, vale la pena recordar cómo se lanzan los programas en Go.
Lo primero y más importante: en la raíz del proyecto (para bibliotecas y paquetes, de lo contrario) se encuentra el archivo main.go
, que, cuando se desarrolla, es ejecutado por
go run main.go
Una característica distintiva de este archivo es que el paquete declarado en él debe ser main
.
package main import ( "fmt" ) func main() { fmt.Println("Hello habr.com!") }
Esencialmente, el punto de entrada al programa es func main()
en el paquete main
. Pero este comportamiento puede ser pirateado un poco. La func init()
fue inventada para esto. Esta función se ejecutará antes de ejecutar func main()
. Esta función también se puede escribir en sus paquetes. Siempre se ejecutará cuando se importe un paquete (para ser precisos, se ejecutará una vez que se importe un paquete en su programa). También vale la pena entender que init()
se ejecutará cuando se ejecuten las pruebas de este paquete.
Ejemplos de paquete
El paquete a
solo exporta la variable, pero no la inicializa.
github.com/vigo5190/goimports-example/a El paquete b
exporta la variable y la inicializa a init()
.
github.com/vigo5190/goimports-example/b package b var Foo string func init() { Foo = "bar" }
El paquete c
exporta la variable, la inicializa en init()
y muestra el valor en stdout.
github.com/vigo5190/goimports-example/c package c import "fmt" var Foo string func init() { Foo = "bar" fmt.Printf("%#v\n", Foo) }
Importar "simple"
En este ejemplo, importamos 2 paquetes y enviamos los valores de las variables exportadas a stdout.
package main import ( "fmt" "github.com/vigo5190/goimports-example/a" "github.com/vigo5190/goimports-example/b" ) func main() { fmt.Printf("%#v\n", a.Foo) fmt.Printf("%#v\n", b.Foo) }
Nosotros obtenemos
go run main.go "" "bar"
Lo que realmente sucede en este código. En la sección de import
, se importan 2 paquetes b
. En el paquete a
se declara a
variable con un valor predeterminado (para cadenas: una cadena vacía). En el paquete b
, el valor de la variable se inicializó en init()
valor "bar"
. Para acceder a las variables de cada paquete, utilice una entrada de la forma <_>.<_>
.
Sinónimo de importación
package main import ( "fmt" "github.com/vigo5190/goimports-example/a" foo "github.com/vigo5190/goimports-example/b" bar "github.com/vigo5190/goimports-example/a" ) func main() { fmt.Printf("%#v\n", a.Foo) fmt.Printf("%#v\n", foo.Foo) fmt.Printf("%#v\n", bar.Foo) }
Nosotros obtenemos
go run main.go "" "bar" ""
Como puede ver en el ejemplo, al paquete b
asigna el sinónimo foo
. En este caso, el paquete a
importó varias veces, la segunda vez bajo la bar
alias.
Los paquetes se importan configurando sinónimos en varios casos:
- El nombre del paquete importado es inconveniente / feo / ... y quiero usar otro;
- El nombre del importado se cruza con el nombre de otro paquete;
- Quiero reemplazar sin problemas el paquete: las interfaces del paquete deben coincidir.
Ejemplo de uso justificable de un sinónimoPor ejemplo, al importar github.com/sirupsen/logrus
:
package db import( log "github.com/sirupsen/logrus" )
Importar subrayado
package main import ( "fmt" "github.com/vigo5190/goimports-example/a" _ "github.com/vigo5190/goimports-example/c" ) func main() { fmt.Printf("%#v\n", a.Foo) }
Nosotros obtenemos
go run main.go "bar" ""
Como muestra el código, importamos dos paquetes: a
y c
. Al mismo tiempo, el paquete c
precedido por _
y el paquete en sí no se utiliza de ninguna manera. Esta técnica se usa para ejecutar init()
desde un paquete.
En nuestro ejemplo, "bar"
apareció en la salida en la primera línea, porque esta salida está en la función de inicialización del paquete c
.
Ejemplo de uso justificado _Por ejemplo, al importar github.com/lib/pq
:
package db import( _ "github.com/lib/pq" )
en init()
lib/pq
el código es:
func init() { sql.Register("postgres", &Driver{}) }
que registrará el controlador.
Importar punto c
package main import ( "fmt" "github.com/vigo5190/goimports-example/a" . "github.com/vigo5190/goimports-example/b" ) func main() { fmt.Printf("%#v\n", a.Foo) fmt.Printf("%#v\n", Foo) }
Nosotros obtenemos
go run main.go "" "bar"
Importar con un punto agrega todos los campos exportados del paquete al alcance actual (más precisamente, el alcance del archivo). Y ahora puede trabajar con los campos del paquete importado como si estuvieran en su paquete.
Esta opción debe usarse con mucho cuidado, como se muestra a continuación.
Ejemplo 1 package main import ( . "fmt" ) func main() { Println("Hello, habr.com!") }
Obtenemos:
Hello, habr.com!
Ejemplo 2 package main import ( . "fmt" . "math" ) func main() { Printf("%v\n", Sqrt(9)) }
Obtenemos:
3
Importar con punto (y error)
package main import ( "fmt" . "github.com/vigo5190/goimports-example/a" . "github.com/vigo5190/goimports-example/b" ) func main() { fmt.Printf("%#v\n", Foo) }
Nosotros obtenemos
go run main.go
Como puede ver en el resultado, al importar paquetes con campos que se cruzan en el alcance actual, obtenemos un error de compilación.
Por lo tanto, piense de nuevo antes de usar dicha importación: puede obtener un error completamente inesperado.
Total
A pesar de las estrictas limitaciones de sintaxis, puedes hacer muchas cosas no estándar en Go. Las características de importación discutidas anteriormente demuestran que con solo un par de operadores puede cambiar mucho el comportamiento de un programa. Lo principal, cuando se utilizan todas estas oportunidades, es no dispararse en el pie . Y recuerde que es mejor escribir un código simple y comprensible que uno complejo y "genial".
PS
Ejemplos de código para jugar están en el github .