تو پایتون، هر متغییری که تعریف میکنیم در یک محدودهای خاص به نام Scope تعریف میشه. Scope مشخص میکنه که کدام بخش از برنامه، قادر به دسترسی به اون متغیره. برای درک بهتر، با مفهوم Namespace یا فضای نام آشنا بشیم...
Namespace به محیط یا فضایی گفته میشه که نامها (مثل متغیرها، توابع، یا کلاسها) در اون قرار دارن و با مقادیر خاصی مرتبط میشن. تصور کن یه اتاق داری که توش جعبههایی هست که هر جعبه برچسبی داره و داخل هر جعبه یه مقدار (مثل عدد یا لیست) قرار داره. این اتاق همون Namespaceعه که بهش اجازه میده بفهمه کدوم نام به کدوم مقدار اشاره میکنه. مثال :
x = 10
در اینجا متغییری به نام x داریم که به مقدار 10 اشاره میکنه. پایتون این نام و مقدار رو در یک Namespace ذخیره میکنه.
داخل پایتون چهار نوع Namespace وجود داره ولی قبل از بررسی این موارد، بزار با یک مثال تفاوت بین Namespace و Scope رو توضیح بدم.
مثال فروشگاه
به عکس بالا دقت کنین. یه فروشگاهی هست که قفسه های مختلفی داره. Namespace یه قانونه یا یک استاندارده که تعیین میکنه هر نوع خوراکی یا هر وسیلهایی، تو کدوم قفسه ها قرار بگیره ! مثلا تو قفسه اول تعیین شده که فقط محصولات شوینده (تمیز کننده) قرار بگیره، تو قفسه دوم تعیین شده که مثلا دستمال کاغذی قرار بگیره، تو قفسه سوم تعیین شده که انواع حبوبات قرار بگیره تااا آخر.
وقتی من وارد فروشگاه میشم، اگه برم سراغ قفسه اول، فقط میتونم به محصولات تمیز کننده دسترسی داشته باشم ! چون تو Scope یا محدوده قفسه اول هستم ! من نمیتونم تو فضای قفسه اول باشم ولی بخوام به حبوبات ها دسترسی داشته باشم، چرا که اون موارد تو دیگر Scope ها یا قفسه ها قرار گرفتهاند.
درنهایت، هر قفسه یک محدوده یا Scope هست که مقدار ها و محصولات خاصی رو داخل خودش تعریف کرده (نگه میداره) و به کلیت این کار که قفسه بندی انجام شده تا محصولات، درست و منظم قرار بگیرن و باهم تداخل نداشته باشن، Namespace گفته میشه.
انواع Namespace در پایتون
پایتون چهار نوع Namespace داره که هر کدوم محدوده یا Scope خاص خود را دارند (انگار که فروشگاه چهار قفسه داره و هر قفسه محدوده و محصولات خاص خودش رو داره) :
Built-in Namespace (داخلی) : شامل نام های پیشفرض پایتون مانند توابع داخلی (print یا len و...) و این نام ها همیشه و همه جا در دسترس هستند. میتونیم تمام نام های داخل این فضا (Built-in Namespace) رو با این دستور ببینیم : print(dir(__builtins__))
Global Namespace (سراسری) : شامل متغییرها و توابعی است که در سطح فایل تعریف شدهاند (بیرون از هر تابع یا کلاس) این Namespace در طول اجرای فایل فعاله. با دستور زیر میتونیم تمام نام های فعال تو Global Namespace رو ببینیم : print(globals())
Local Namespace (محلی) : شامل متغییرهایی است که داخل تابع یا بلوک کد تعریف شدهاند. این متغییرها فقط در همون تابع یا بلوک قابل دسترسی هستند. با دستور زیر میشه تمام نام های محلی (Local Namespace) هارو مشاهده کرد :
def my_function(): x = 10 print(locals()) # Result : {'x': 10}
و فضای نام آخر هست، Enclosed Namespace (تو در تو) : این نوع فضای نام زمانی ایجاد میشه که شما یک تابع داخلی توی یک تابع دیگه تعریف کنید. متغیرهایی که در تابع بیرونی تعریف میشوند ولی در فضای محلی تابع داخلی نیستند، متعلق به فضای نام Enclosed هستند. مثال :
def a(): x = 'salam' def b(): print(x) # این متغیر از فضای نام Enclosed میاد b() a()
جمع بندی :
پایتون اومده این Namespace هارو تعریف کرده. هر Namespace در یک محدوده خاصی قابل استفاده هست. مثلا اگه من داخل یک تابع، متغییری ایجاد کنم، این متغییر طبق Namespace ها، داخل Local Namespace قرار داره و فقط درون همون تابع قابل دسترسی هست ! یعنی محدوده ایی که براش تعیین شده تو همون تابع هست.
مرور + نکات بیشتر :
Built-in Namespace : همیشه و همه جا در دسترسه.
Global Namespace : در سطح فایل تعریف میشه و در طول اجرای فایل فعاله.
Local Namespace : در داخل تابع یا بلوک کد تعریف میشه و فقط در طول اجرای همان تابع یا بلوک قابل دسترسیه.
Enclosed Namespace : در توابع تو در تو وجود داره و متغیرهای تابع بیرونی رو شامل میشه که در تابع داخلی وجود ندارن. به مثالی که بالاتر زدیم دقت کن. اگه من بخوام مقدار متغییر x رو تغییر بدم بهم خطا میده. برای حل این مشکل باید به شکل زیر عمل کنم :
def one(): name = 'amirhosein' def two(): nonlocal name name += ' nazouri' print(name) # Result : amirhosein nazouri two() one()
با کلمه کلیدی nonlocal میتونم مقدار متغییرهای فضای Enclosed رو تغییر بدم.
در مورد Global Namespace هم به مثال زیر دقت کنیم :
dodo = 'didi' def one(): dodo = 'nadidi' def two(): print(dodo) # Result : nadidi two() one() print(dodo) # Result : didi # تغییری روی این متغییر که در فضای نام سراسری ایجاد شده بود اعمال نشده def one(): dodo = 'nadidi' def two(): global dodo print(dodo) # Result : didi two() one() # کلمه کلیدی 'گلوبال' میاد اون متغییری که در فضای سراسری ایجاد شده رو انتخاب میکنه ولی اگه چیزی نزارم یا از کلمه کلیدی 'نان لوکال' استفاده کنم میاد همون متغییر داخل فضاینام 'این کلوزد' رو استفاده میکنه def one(): dodo = 'nadidi' def two(): nonlocal dodo print(dodo) # Result : nadidi two() one()