آموزش پایتون - دامنه متغیر

حوزه متغیر (Scope of a variable) در پایتون به عنوان ناحیه یا بخش مشخصی تعریف می‌شود که متغیر در آن برای کاربر قابل دسترسی است. حوزه یک متغیر به محل و نحوه تعریف آن بستگی دارد. در پایتون، یک متغیر می‌تواند دارای حوزه سراسری (global) یا محلی (local) باشد.

انواع حوزه متغیرها در پایتون

بر اساس حوزه، متغیرهای پایتون به سه دسته تقسیم می‌شوند -

  • متغیرهای محلی (Local Variables)

  • متغیرهای سراسری (Global Variables)

  • متغیرهای غیرمحلی (Nonlocal Variables)

متغیرهای محلی (Local Variables)

متغیر محلی درون یک تابع یا بلوک کد مشخص تعریف می‌شود. این متغیر تنها توسط تابع یا بلوکی که در آن تعریف شده قابل دسترسی است و حوزه محدودی دارد. به عبارت دیگر، حوزه متغیرهای محلی به تابعی که در آن تعریف شده‌اند محدود می‌شود و تلاش برای دسترسی به آن‌ها در خارج از این تابع منجر به خطا خواهد شد. همواره به یاد داشته باشید که متغیرهای محلی متعدد می‌توانند با نام یکسان وجود داشته باشند.

مثال

مثال زیر حوزه متغیرهای محلی را نشان می‌دهد.

def myfunction():
   a = 10
   b = 20
   print("variable a:", a)
   print("variable b:", b)
   return a+b
   
print (myfunction())

در کد بالا، ما به متغیرهای محلی از طریق خود تابع دسترسی پیدا کرده‌ایم. بنابراین، کد خروجی زیر را تولید می‌کند -

variable a: 10
variable b: 20
30

متغیرهای سراسری (Global Variables)

یک متغیر سراسری از هر بخش از برنامه قابل دسترسی است و خارج از هرگونه تابع یا بلوک کد تعریف می‌شود. این متغیر به بلوک یا تابع خاصی تعلق ندارد.

مثال

مثال زیر حوزه متغیر سراسری را نشان می‌دهد. ما می‌توانیم هم در داخل و هم در خارج از حوزه تابع به آن‌ها دسترسی داشته باشیم.

#global variables
name = 'Tut24'
marks = 50
def myfunction():
   # accessing inside the function
   print("name:", name)
   print("marks:", marks)
# function call   
myfunction()

کد بالا خروجی زیر را تولید خواهد کرد -

name: Tut24
marks: 50

متغیرهای غیرمحلی (Nonlocal Variables)

متغیرهایی در پایتون که نه در حوزه محلی و نه در حوزه سراسری تعریف شده‌اند، متغیرهای غیرمحلی نامیده می‌شوند. این متغیرها در توابع تو در تو (nested functions) استفاده می‌شوند.

مثال

مثال زیر نحوه کار متغیرهای غیرمحلی را نشان می‌دهد.

def yourfunction():
   a = 5
   b = 6 
   # nested function
   def myfunction():
      # nonlocal function 
      nonlocal a
      nonlocal b
      a = 10
      b = 20 
      print("variable a:", a)
      print("variable b:", b)
      return a+b
   print (myfunction())
yourfunction()

کد بالا خروجی زیر را تولید خواهد کرد -

variable a: 10
variable b: 20
30

فضای نام (Namespace) و حوزه متغیرهای پایتون

فضای نام مجموعه ای از شناسه ها مانند نام متغیرها، نام توابع، نام کلاس‌ها و غیره است. در پایتون، از فضای نام برای مدیریت حوزه متغیرها و جلوگیری از تداخل نام‌گذاری استفاده می‌شود.

پایتون انواع فضاهای نام زیر را ارائه می‌دهد -

  • فضای نام توکار (Built-in namespace) شامل توابع توکار و استثناهای توکار است. آن‌ها به محض بارگیری مفسر پایتون در حافظه بارگیری می‌شوند و تا زمانی که مفسر در حال اجرا است باقی می‌مانند.

  • فضای نام سراسری (Global namespace) شامل تمام نام‌های تعریف شده در برنامه اصلی است. این نام‌ها تا زمانی که برنامه در حال اجرا است در حافظه باقی می‌مانند.

  • فضای نام محلی (Local namespace) شامل نام‌های تعریف شده در داخل یک تابع است. آن‌ها تا زمانی که تابع در حال اجرا است در دسترس هستند.

این فضاهای نام درون یکدیگر قرار گرفته‌اند. نمودار زیر رابطه بین فضاهای نام را نشان می‌دهد.

Types Of Namespace

عمر یک متغیر مشخص محدود به فضای نامی است که در آن تعریف شده است. در نتیجه، دسترسی به متغیر موجود در فضای نام داخلی از هر فضای نام بیرونی امکان‌پذیر نیست.

تابع ()globals در پایتون

کتابخانه استاندارد پایتون شامل یک تابع توکار به نام globals() است. این تابع دیکشنری از نمادهایی را برمی‌گرداند که در حال حاضر در فضای نام سراسری در دسترس هستند.

تابع ()globals را مستقیماً از خط فرمان پایتون اجرا کنید.

>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}

مشاهده می‌شود ماژول توکاری که شامل تعاریف همه توابع توکار و استثناهای توکار است، بارگیری شده است.

مثال

کد زیر را که شامل چند متغیر و یک تابع با چند متغیر دیگر در داخل آن است، ذخیره کنید.

name = 'Tut24'
marks = 50
result = True
def myfunction():
   a = 10
   b = 20
   return a+b
   
print (globals())

فراخوانی ()globals از داخل این اسکریپت، شیء دیکشنری زیر را برمی‌گرداند -

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000263E7255250>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:\\Users\\user\\examples\\main.py', '__cached__': None, 'name': 'Tut24', 'marks': 50, 'result': True, 'myfunction': <function myfunction at 0x00000263E72004A0>}

فضای نام سراسری اکنون شامل متغیرهای موجود در برنامه و مقادیر آن‌ها و همچنین شیء تابع است (و نه متغیرهای داخل تابع).

تابع ()locals در پایتون

کتابخانه استاندارد پایتون شامل یک تابع توکار به نام locals() است. این تابع دیکشنری از نمادهایی را برمی‌گرداند که در حال حاضر در فضای نام محلی تابع در دسترس هستند.

مثال

اسکریپت بالا را تغییر دهید تا دیکشنری فضاهای نام سراسری و محلی را از داخل تابع چاپ کند.

name = 'Tut24'
marks = 50
result = True
def myfunction():
   a = 10
   b = 20
   c = a+b
   print ("globals():", globals())
   print ("locals():", locals())
   return c
myfunction()

خروجی نشان می‌دهد که ()locals دیکشنری از متغیرها و مقادیر آن‌ها را که در حال حاضر در تابع در دسترس هستند برمی‌گرداند.

globals(): {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000169AE265250>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:\\Users\\mlath\\examples\\main.py', '__cached__': None, 'name': 'Tut24', 'marks': 50, 'result': True, 'myfunction': <function myfunction at 0x00000169AE2104A0>}
locals(): {'a': 10, 'b': 20, 'c': 30}

از آنجا که هر دو تابع ()globals و ()locals یک دیکشنری برمی‌گردانند، می‌توانید با استفاده از متد ()get دیکشنری یا عملگر اندیس به مقدار یک متغیر از فضای نام مربوطه دسترسی پیدا کنید.

print (globals()['name']) # displays Tut24
print (locals().get('a')) # displays 10

تداخل فضای نام در پایتون

اگر متغیری با نام یکسان هم در حوزه سراسری و هم در حوزه محلی وجود داشته باشد، مفسر پایتون به متغیر موجود در فضای نام محلی اولویت می‌دهد.

مثال

در مثال زیر، ما یک متغیر محلی و یک متغیر سراسری تعریف می‌کنیم.

marks = 50 # this is a global variable
def myfunction():
   marks = 70 # this is a local variable
   print (marks)
   
myfunction()
print (marks) # prints global value

این کد خروجی زیر را تولید خواهد کرد -

70
50

مثال

اگر تلاش کنید مقدار یک متغیر سراسری را از داخل یک تابع تغییر دهید، پایتون همان‌طور که در مثال زیر نشان داده شده است خطای UnboundLocalError رخ می‌دهد -

# this is a global variable
marks = 50 
def myfunction():
   marks = marks + 20
   print (marks)

myfunction()
# prints global value
print (marks) 

این کد پیام خطای زیر را تولید خواهد کرد -

   marks = marks + 20
           ^^^^^
UnboundLocalError: cannot access local variable 'marks' where it is not associated with a value

مثال

برای تغییر یک متغیر سراسری، می‌توانید آن را با ساختار دیکشنری به‌روزرسانی کنید، یا قبل از تغییر از کلمه کلیدی global برای ارجاع به آن استفاده کنید.

var1 = 50 # this is a global variable
var2 = 60 # this is a global variable
def myfunction():
   "Change values of global variables"
   globals()['var1'] = globals()['var1']+10
   global var2
   var2 = var2 + 20

myfunction()
print ("var1:",var1, "var2:",var2) #shows global variables with changed values

با اجرای کد، خروجی زیر تولید خواهد شد -

var1: 60 var2: 80

مثال

در نهایت، اگر سعی کنید به یک متغیر محلی در حوزه سراسری دسترسی پیدا کنید، پایتون خطای NameError رخ می‌دهد، زیرا متغیر موجود در حوزه محلی خارج از آن قابل دسترسی نیست.

var1 = 50 # this is a global variable
var2 = 60 # this is a global variable
def myfunction(x, y):
   total = x+y
   print ("Total is a local variable: ", total)

myfunction(var1, var2)
print (total) # This gives NameError

این کد پیام خطای زیر را تولید خواهد کرد -

Total is a local variable: 110
Traceback (most recent call last):
   File "C:\Users\user\examples\main.py", line 9, in <module>
   print (total) # This gives NameError
          ^^^^^
NameError: name 'total' is not defined

پیشرفت مطالعه: وضعیت خوانده‌شدن این درس فقط در همین مرورگر ذخیره می‌شود؛ بدون نیاز به حساب کاربری.