توی زبان برنامهنویسی گولنگ، توابع init
برای انجام تنظیمات اولیه قبل از اجرای برنامه استفاده میشن. هر فایل Go میتونه یک یا چند تابع init
داشته باشه که به طور خودکار و بدون نیاز به فراخوانی مستقیم اجرا میشن. این توابع بیشتر برای آمادهسازی محیط، مانند تنظیمات پکیجها، مقداردهی اولیه متغیرهای بسته و یا انجام عملیاتهایی که باید قبل از شروع برنامه انجام شوند، به کار میروند.
برخی از ویژگیهای مهم توابع init
:
init
بر اساس ترتیب فایلهای موجود در پکیج هست.init
در یک پکیج، قبل از توابع main
اجرا میشن.
توابع init به اصطلاح در package block (محدودهای که در آن پکیج و اجزای مربوط به آن تعریف میشوند) بصورت زیر تعریف میشه:
فرض کنید یه پکیج بنام main داریم، روش تعریف یک تابع init بصورت زیر هست:
package main
import "fmt"
var x int
func init() {
x = 10
}
func main() {
fmt.Println(x) // خروجی: 10
}
همونطور که در کد بالا میبینید ما یک متغییرx در تابع init تعریف کرده و از آن در تابع main استفاده کردیم. بطور کلی توابع init
در محدوده ی بلاک تعریف و قبل از اجرای تابع main
برای تنظیمات و مقداردهی اولیه استفاده میشن.
بصورت کلی از توابع init در گولنگ برای کارهای زیر استفاده می شه:
مقداردهی اولیه متغیرها اگر در عبارت مقداردهی اولیه قابل انجام نباشد،
بررسی/تثبیت وضعیت برنامه،
ثبت نام (registering)،
اجرای محاسباتی که قرار هست فقط یکبار انجام شوند،
و...
همچنین هر چیزی که می تونید توی یک تابع معمولی قرار بدین را میتونید توی init قرار بدین.
در ادامه به برخی نکات مهم در مورد توابع init در گولنگ می پردازیم:
برای استفاده از یک یکیچ ایمپورت شده(imported)، ابتدا باید مقداردهی اولیه صورت گیرد. این کار توسط سیستم در زمان اجرای Go انجام میشه و شامل مراحل زیر هست ( در اینجا ترتیب اهمیت داره):
init
در داخل بسته.همچنین باید بدونیم که مقداردهی اولیه بسته تنها یک بار انجام میشود، حتی اگر بسته چندین بار وارد شود.
در زبان گولنگ یه بسته می تونه حاوی فایل های زیادی باشه. اما ترتیب مقدار دهی اولیه متغیرها و فراخوانی توابع init وقتی که چندین init در فایل های متفاوت داریم به چه صورته؟ ابتدا، مکانیزم وابستگی مقداردهی اولیه وارد عمل میشه. بعد باید تصمیمگیری بشه که آیا مقداردهی متغیرها در فایل a.go
باید زودتر انجام شود یا مثلاً در z.go
.
این موضوع به ترتیب فایلها که به کامپایلر ارائه میشود بستگی دارد. اگر فایل z.go
ابتدا توسط سیستم بیلد ارسال بشه، مقداردهی متغیرها در آنجا زودتر از a.go
انجام میشود. همین مورد برای اجرای توابع init
نیز صدق میکند. مشخصات زبان توصیه میکند که همیشه از همان ترتیب استفاده شود و نام فایلهای بسته را به ترتیب لغوی به کامپایلر ارسال کنید:
"برای اطمینان از رفتار قابل بازتولید مقداردهی اولیه، سیستمهای بیلد تشویق میشوند که فایلهای متعدد متعلق به یک بسته را به ترتیب نام فایلهای لغوی به یک کامپایلر ارائه دهند."
- ترتیب لغوی به معنی ترتیب حروف الفبایی نام فایلها هست. به عنوان مثال، اگر نام فایلهای شما a.go
, b.go
, c.go
باشد، باید به ترتیب a.go
، سپس b.go
، و در نهایت c.go
به کامپایلر ارسال شوند.
هدف از این توصیه این است که فرآیند مقداردهی اولیه (initialization) در برنامههای Go قابل پیشبینی و تکرارپذیر باشد. اگر همیشه فایلها به یک ترتیب مشخص و ثابت (مثلاً حروف الفبایی) به کامپایلر ارسال بشن، نتیجهی مقداردهی اولیه متغیرها و اجرای توابع init
نیز همیشه یکسان خواهد بود، که این موضوع به ایجاد برنامههایی پایدارتر و کمتر متغیر کمک میکند.
البته این روش ممکنه باعث ایجاد برنامه های کمتر قابل حمل شود یعنی دو محیط های متفاوت رفتار متفاوتی نشون خواهد داد.
مثال زیر رو ببیند:
// a.go
package main
import "fmt"
var a = c + b // 3 + 2 = 5
func init() {
fmt.Println("init a")
}
func main() {
fmt.Println("main")
}
// b.go
package main
var b = 2
func init() {
fmt.Println("init b")
}
// c.go
package main
var c = 3
func init() {
fmt.Println("init c")
}
توی کد بالا، ترتیب اجرای توابع init
و مقداردهی اولیه متغیرها به ترتیب فایلهایی که به کامپایلر ارسال شدهاند بستگی داره.
اگر سیستم بیلد فایلها را به ترتیب a.go
, b.go
, c.go
ارسال کنه، خروجی به این صورت خواهد بود:
init a
init b
init c
main
اما اگر ترتیب فایلها به صورت c.go
, b.go
, a.go
باشد، خروجی متفاوت خواهد بود:
init c
init b
init a
main
این تفاوت در خروجی نشان میده که وابستگی به ترتیب فایلها میتونه مشکلاتی در قابل حمل بودن برنامه ایجاد کنه. به همین دلیل توصیه می شه که از ترتیب لغوی(حروف الفبایی) برای ارسال فایل ها به کامپایلر استفاده بشه تا رفتار برنامه در همه محیط ها یکسان و قابل پیشبینی باشه.
ویژگی های توابع init:
عدم تعریف به صورت صریح:
این توابع نه آرگومان می گیرن و نه مقداری برمی گردونن. بر خلاف main، تابع init اعلان نشده است، بنابراین نمی توان به اون ارجاع داد، برای نمونه کد زیر یک خطا صادر میکنه:
package main
import "fmt"
func init() {
fmt.Println("init")
}
func main() {
init()
}
کد بالا رو اگه ران کنید با خطای undefined: init مواجه میشین. این خطا بخاطر این هست که شناسگر init
به عنوان یک تابع مستقل در برنامه تعریف نشده.
به عبارت دیگه، تابع init
نمیتونه به صورت مستقیم مانند سایر توابع مورد استفاده قرار بگیره. شبیه به شناسگر خالی (blank identifier) هست که با کاراکتر آندرلاین (_) نشان داده میشه.
بنابراین، تابع init
در Go یک تابع خاص هست که فقط برای مقداردهی اولیه و تنظیمات خاص قبل از شروع برنامه استفاده میشه و نمیتوان به آن مانند سایر توابع ارجاع داد یا اون رو فراخوانی کرد.
تعریف چند تابع init در یک فایل:
در زبان گولنگ میتونیم توی یک فایل چندین تابع init داشته باشیم، کد زیر رو ببینید:
فایل sandbox.go:
package main
import "fmt"
func init() {
fmt.Println("init 1")
}
func init() {
fmt.Println("init 2")
}
func main() {
fmt.Println("main")
}
فایل utils.go:
package main
import "fmt"
func init() {
fmt.Println("init 3")
}
خروجی دو فایل بالا بصورت زیر خواهد بود:
init 1
init 2
init 3
main
همونطور که میبینید در زمان اجرای برنامه، توابع init
به ترتیبی که در فایلها تعریف شدن اجرا میشن، و بعد از اونها تابع main
اجرا میشه.
بطور کلی توابع init
در Go برای انجام مقداردهیهای پیچیده و تنظیمات اولیه بسیار مفید هست و میتونه به صورت انعطافپذیری در بستهها و فایلهای مختلف تعریف بشه.
امیدوارم این آموزش برای شما مفید بوده باشه. اگر نظر یا پیشنهادی دارید از بخش نظرات برای ما ارسال کنید.
برای اطلاع از پاسخ به نظر شما می توانید ایمیل یا شماره موبایل خود را وارد نمایید. *
ایمیل و شماره موبایل شما کاملا مخفی خواهد ماند و در سایت نمایش داده نخواهد شد. *
هنوز برای این مطلب نظری ارسال نشده است!