if __name__ == “__main__”: در پایتون چه کار می کند؟
if __name__ == "__main__":
print("Hello, World!")
جواب کوتاه
این کد boilerplate است که از کاربران در برابر فراخوانی تصادفی اسکریپت در زمانی که قصد ندارند آن را اجرا کنند محافظت می کند. در اینجا برخی از مشکلات رایج در هنگام حذف این گارد محافظ از یک اسکریپت آورده شده است:
اگر اسکریپت بدون محافظ را در اسکریپت دیگری وارد کنید (به عنوان مثال،my_script_without_a_name_eq_main_guard را وارد کنید)، اسکریپت دوم باعث میشود اسکریپت اولی در import time و با استفاده از آرگومانهای خط فرمان اسکریپت دوم اجرا شود. این تقریباً یک اشتباه است. اگر یک کلاس سفارشی در اسکریپت guardless دارید و آن را در یک فایل pickle ذخیره میکنید، حذف آن در یک اسکریپت دیگر باعث وارد شدن اسکریپت بدون محافظ میشود.
برای درک بهتر چرایی و چگونگی اهمیت این موضوع، باید یک گام به عقب برداریم تا بفهمیم پایتون چگونه اسکریپتها را مقداردهی اولیه میکند و چگونه با مکانیزم وارد کردن ماژول خود تعامل دارد.
هر زمان که مفسر پایتون یک فایل منبع را می خواند، دو کار انجام می دهد:
- چند متغیر خاص مانند __name__ را تنظیم می کند و سپس
- تمام کدهای موجود در فایل را اجرا می کند.
بیایید ببینیم این چگونه کار می کند و چگونه با سؤال شما در مورد بررسی های __name__ که همیشه در اسکریپت های پایتون می بینیم ارتباط دارد.
نمونه کد
بیایید از یک نمونه کد کمی متفاوت استفاده کنیم تا نحوه عملکرد import و اسکریپت ها را بررسی کنیم. فرض کنید موارد زیر در فایلی به نام foo.py وجود دارد.
# Suppose this is foo.py.
print("before import")
import math
print("before function_a")
def function_a():
print("Function A")
print("before function_b")
def function_b():
print("Function B {}".format(math.sqrt(100)))
print("before __name__ guard")
if __name__ == '__main__':
function_a()
function_b()
print("after __name__ guard")
متغیرهای ویژه (ُSpecial Variables)
هنگامی که مفسر پایتون یک فایل سورس را می خواند، ابتدا چند متغیر خاص را تعریف می کند. در این مورد، ما به متغیر __name__ اهمیت می دهیم.
وقتی ماژول شما برنامه اصلی است
اگر ماژول خود (فایل منبع) را به عنوان برنامه اصلی اجرا می کنید، به عنوان مثال.
python foo.py
مفسر رشته سخت کد شده (hard-coded) “__main__” را به متغیر __name__ اختصاص می دهد، یعنی.
# It's as if the interpreter inserts this at the top
# of your module when run as the main program.
__name__ = "__main__"
وقتی ماژول شما توسط دیگری import می شود
از طرف دیگر، فرض کنید ماژول دیگری برنامه اصلی است و ماژول شما را import می کند. این به این معنی است که عبارتی مانند این در برنامه اصلی وجود دارد، یا در ماژول دیگری که برنامه اصلی import می کند:
# Suppose this is in some other main program.
import foo
مفسر فایل foo.py شما را جستجو میکند (همراه با جستجوی چند نوع دیگر)، و قبل از اجرای آن ماژول، نام “foo” را از عبارت import به متغیر __name__ اختصاص میدهد.
# It's as if the interpreter inserts this at the top
# of your module when it's imported from another module.
__name__ = "foo"
اجرای کد ماژول
پس از تنظیم متغیرهای ویژه، مفسر تمام کدهای موجود در ماژول را در یک زمان اجرا می کند. ممکن است بخواهید پنجره دیگری را در کنار نمونه کد باز کنید تا بتوانید این توضیحات را دنبال کنید.
همیشه
- رشته “قبل از وارد کردن” (بدون نقل قول) را چاپ می کند.
- ماژول math را بارگذاری می کند و آن را به متغیری به نام math اختصاص می دهد. این معادل جایگزینی math import با عبارت زیر است (توجه داشته باشید که __import__ یک تابع سطح پایین در پایتون است که یک رشته می گیرد و import واقعی را تحریک (trigger) می کند):
# Find and load a module given its string name, "math",
# then assign it to a local variable called math.
math = __import__("math")
- رشته “before function_a” را چاپ می کند.
- بلوک def را اجرا می کند، یک شی تابع ایجاد می کند، سپس آن شی تابع را به متغیری به نام function_a اختصاص می دهد.
- رشته “before function_b” را چاپ می کند.
- بلوک دف دوم را اجرا می کند، شی تابع دیگری ایجاد می کند، سپس آن را به متغیری به نام function_b اختصاص می دهد.
- رشته “قبل از __name__ guard” را چاپ می کند.
فقط زمانی که ماژول شما برنامه اصلی باشد
اگر ماژول شما برنامه اصلی باشد، می بیند که __name__ در واقع روی “__main__” تنظیم شده است و این دو تابع را فراخوانی می کند و رشته های “Function A” و “Function B 10.0” را چاپ می کند.
فقط زمانی که ماژول شما توسط دیگری وارد شود
(در عوض) اگر ماژول شما برنامه اصلی نیست اما توسط برنامه دیگری وارد شده است، __name__ “foo” خواهد بود، نه “__main__” و از متن دستور if عبور می کند.
همیشه
در هر دو حالت رشته “پس از __name__ guard” را چاپ می کند.
خلاصه
به طور خلاصه، آنچه در این دو مورد چاپ می شود به شرح زیر است:
# What gets printed if foo is the main program
before import
before function_a
before function_b
before __name__ guard
Function A
Function B 10.0
after __name__ guard
# What gets printed if foo is imported as a regular module
before import
before function_a
before function_b
before __name__ guard
after __name__ guard
چرا این روش درست کار می کند؟
ممکن است به طور طبیعی تعجب کنید که چرا کسی این را می خواهد. خوب، گاهی اوقات شما می خواهید یک فایل .py بنویسید که هم می تواند توسط برنامه های دیگر و/یا ماژول ها به عنوان یک ماژول استفاده شود و هم می تواند به عنوان خود برنامه اصلی اجرا شود.
مثال ها:
ماژول شما یک کتابخانه است، اما می خواهید یک حالت اسکریپت داشته باشید که در آن برخی از تست های واحد یا نسخه نمایشی را اجرا می کند.
ماژول شما فقط به عنوان یک برنامه اصلی استفاده می شود، اما دارای تعدادی تست واحد است، و چارچوب تست با وارد کردن فایل های .py مانند اسکریپت شما و اجرای توابع آزمایشی خاص کار می کند. شما نمی خواهید که اسکریپت را فقط به این دلیل که ماژول را وارد می کند، اجرا کند.
ماژول شما بیشتر به عنوان یک برنامه اصلی استفاده می شود، اما همچنین یک API مناسب برای برنامه نویسان برای کاربران پیشرفته ارائه می دهد.
فراتر از این مثالها، بسیار زیباست که اجرای یک اسکریپت در پایتون فقط تنظیم چند متغیر جادویی و وارد کردن اسکریپت است. “اجرا” اسکریپت یکی از عوارض جانبی وارد کردن ماژول اسکریپت است.
غذا برای فکر
سوال: آیا می توانم چندین بلوک بررسی __name__ داشته باشم؟ پاسخ: این کار عجیب است، اما زبان مانع شما نمی شود.
فرض کنید موارد زیر در foo2.py است. اگر در خط فرمان بگویید python foo2.py چه اتفاقی میافتد؟ چرا؟
# Suppose this is foo2.py.
import os, sys; sys.path.insert(0, os.path.dirname(__file__)) # needed for some interpreters
def function_a():
print("a1")
from foo2 import function_b
print("a2")
function_b()
print("a3")
def function_b():
print("b")
print("t1")
if __name__ == "__main__":
print("m1")
function_a()
print("m2")
print("t2")
حالا، ببینید اگر بررسی __name__ را در foo3.py حذف کنید چه اتفاقی میافتد:
# Suppose this is foo3.py.
import os, sys; sys.path.insert(0, os.path.dirname(__file__)) # needed for some interpreters
def function_a():
print("a1")
from foo3 import function_b
print("a2")
function_b()
print("a3")
def function_b():
print("b")
print("t1")
print("m1")
function_a()
print("m2")
print("t2")
هنگامی که به عنوان یک اسکریپت استفاده می شود چه کاری انجام می دهد؟ وقتی به عنوان ماژول import می شود؟
# Suppose this is in foo4.py
__name__ = "__main__"
def bar():
print("bar")
print("before __name__ guard")
if __name__ == "__main__":
bar()
print("after __name__ guard")
دیدگاهتان را بنویسید