آشنایی با مفهوم Scope در زبان برنامه نویسی پایتون


پارامترهای یک فانکشن و متغیرهایی که در زمان تعریف یک فانکشن در بدنه ی آن استفاده می شوند در Local Scope (لوکال اسکوپ یا دامنه ی محلی) آن قرار دارند و بالعکس، متغیرهایی که در خارج از بدنه ی تمام فانکشن ها تعریف می شوند در Global Scope (گلوبال اسکوپ یا دامنه ی سراسری) قرار دارند. متغیری که در اسکوپ لوکال قرار دارد اصطلاحا متغیر لوکال نامیده می شود و متغیری که در اسکوپ سراسری قرار دارد هم متغیر گلوبال نام دارد. اسکوپ یک متغیر یکی از این دو است؛ به عبارت دیگر یک متغیر یا لوکال است یا گلوبال و نمی تواند هم زمان در دو اسکوپ قرار گیرد.

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

یک اسکوپ لوکال زمانی ایجاد می شود که یک فانکشن فراخوانی می شود. هر متغیری که در این فانکشن آمده باشد در اسکوپ لوکال آن قرار می گیرد. زمانی که فانکشن یک مقدار را برمی گرداند (چه مقدار None و یا هر آبجکت دیگری)، این اسکوپ لوکال از بین می رود. در این صورت زمانی که فانکشن را مجدداً فراخوانی می کنید، متغیرهای لوکال مقادیری که در نوبت قبلی فراخوانی فانکشن به آن ها داده شده بود را به خاطر نخواهند داشت. در مورد اسکوپ ها قواعدی وجود دارد که باید از آن ها اطلاع داشته باشیم که در ادامه مهم ترین آن ها را مرور خواهیم کرد:

1- کدهایی که در اسکوپ گلوبال وجود دارند، نمی توانند به متغیرهای لوکال دسترسی داشته باشند و از آن ها استفاده کنند. برای مثال برنامه ی زیر را در نظر بگیرید:

def setName():
    name = "Narges"
setName()
print(name)

در این برنامه، فانکشنی با نام setName تعریف کرده ایم که در بدنه ی آن یک متغیر لوکال با نام name وجود دارد و کاری که فانکشن ()setName انجام می دهد این که در زمان فراخوانی، متغیر name را به استرینگ "Narges" منتسب می کند. در آخرین کد این برنامه می بینیم که از متغیر لوکال name به عنوان آرگومان تابع پرینت که در اسکوپ گلوبال برنامه قرار دارد -چون خارج از بدنه ی فانکشن است- استفاده شده است. برنامه ی خود را در فایل setName.py ذخیره کرده و آن را اجرا می کنیم. خروجی برنامه به صورت زیر است:

============== RESTART: D:/sokanacademy/Python/ setName.py ==============
Traceback (most recent call last):
  File " D:/sokanacademy/Python/ setName.py", line 5, in 
    print(name)
NameError: name 'name' is not defined
>>>

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

2- دسترسی به متغیرهای گلوبال در اسکوپ لوکال امکان پذیر است. برنامه ی زیر را در نظر بگیرید:

name = "Narges"
def printName():
    print(name)

printName()

در این برنامه، متغیر name از نوع گلوبال است -چون در بدنه ی هیچ فانکشنی قرار ندارد- و در دستور پرینت در بدنه ی فانکشن ()printName از آن استفاده شده است. اسکریپت برنامه را در فایلی با نام printName.py ذخیره می کنیم. خروجی حاصل از اجرای برنامه به صورت زیر است:

Narges

همان طور که می بینید، کدهای اسکوپ لوکال فانکشن به مقدار متغیر گلوبال name دسترسی دارد و از آن به عنوان آرگومان فانکشن پرینت استفاده کرده است.

3- کدهایی که در دامنه ی لوکال یک فانکشن قرار دارند نمی توانند به متغیرهای لوکال سایر اسکوپ ها دسترسی داشته باشند. هر زمان که یک فانکشن فراخوانی می شود یک اسکوپ لوکال ایجاد می شود و پس از پایان کار، این اسکوپ از بین می رود. برنامه ی زیر را در نظر بگیرید:

def setName():
    name = "Narges"
    
def getName():
    name = "Puya"
    setName()
    return name

print(getName())

برنامه را در فایلی با نام getName.py ذخیره می کنیم. اجرای برنامه از فانکشن پرینت که در اسکوپ گلوبال برنامه است آغاز می شود. آرگومان پرینت فانکشن ()getName است که با فراخوانی آن یک اسکوپ لوکال برای این فانکشن ایجاد و سپس دستورات بدنه ی آن به ترتیب اجرا می شوند. ابتدا یک متغیر لوکال با نام name ساخته می شود و به استرینگ "Puya" منتسب می شود. سپس فانکشن ()setName فراخوانی می شود. با فراخوانی این فانکشن یک اسکوپ لوکال جدید ایجاد می شود (توجه داشته باشید که دامنه های لوکال می توانند به طور هم زمان وجود داشته باشند.)

با فرخوانی فانکشن ()setName تنها دستور قرار گرفته در بدنه ی آن اجرا می شود. در واقع یک متغیر لوکال با شناسه ی name ایجاد می شود و به استرینگ "Narges" منتسب می شود. با اجرای این دستور فانکشن، آبجکتی از نوع None ریترن می شود و کار آن به پایان می رسد. در این شرایط، اسکوپ لوکال ایجاد شده توسط آن از بین می رود و همان طور که گفتیم متغیر لوکال مربوط به آن نیز از حافظه ی کامپیوتر پاک می شود. مجدداً مفسر وارد دامنه ی لوکال فانکشن ()getName می شود و با اجرای آخرین دستور آن مقدار متغیری با نام name برگشت داده می شود تا فانکشن پرینت آن را چاپ کند. اجازه دهید خروجی فانکشن پرینت را ببینیم:

Puya

همان طور که می بینید، دستور ریترن متغیر لوکال name که در دامنه ی لوکال فانکشن ()getName قرار داشت را بر می گرداند، اگرچه فانکشن setName متغیری با همین نام دارد که با فراخوانی آن به مقدار استرینگ "Narges" منتسب می شد، با این حال همان طور که گفتیم با پایان یافتن فراخوانی این فانکشن متغیر لوکال مربوط به آن نیز فراموش می شود. این اتفاق در مورد فانکشن ()getName نیز می افتد و با پایان یافتن اجرای دستورات این فانکشن، اسکوپ لوکال آن از بین می رود و متغیرهای لوکال مربوط به آن نیز از حافظه پاک می شوند.

4- زمانی که در یک اسکوپ کار می کنیم، نمی توانیم از یک شناسه برای نام گذاری دو متغیر متفاوت استفاده کنیم، با این حال اسکوپ های مختلف می توانند متغیرهایی متفاوت اما با نام یکسان داشته باشند. برای مثال ممکن است یک متغیر لوکال با نام name و یک متغیر گلوبال هم با همین نام در برنامه ی خود داشته باشیم. در مثال قبل نمونه ای از این قاعده را دیدیم که دو متغیر متفاوت با شناسه ی یکسان name در دو دامنه ی متفاوت وجود داشتند. البته به منظور جلوگیری از اشتباه و سردرگمی، بهتر است تا حد امکان از شناسه های یکسان در دامنه های متفاوت هم استفاده نکنیم.

اهمیت اسکوپ های لوکال در این است که اگر به دلیل مقداردهی نادرست به یک متغیر خطایی در برنامه پیش بیاید -در صورتی که تمام متغیرها در یک اسکوپ گلوبال قرار داشته باشند- به سختی می توانیم بفهمیم متغیر مورد نظر در کدام قسمت از کدها به صورت نادرست مقداردهی شده است، به این دلیل که تمام واحدهای برنامه می توانند روی آن تأثیر گذاشته باشند. با این حال زمانی که پایتون از اسکوپ های لوکال استفاده می کند، در واقع مسئولیت یک متغیر را به همان فانکشنی که متغیر در اسکوپ آن تعریف شده است واگذار می کند. در این صورت اگر خطایی در مقدار دهی متغیر مورد نظر رخ دهد، حتماً مربوط به جایی بوده که آن فانکشن را فراخوانی کرده ایم. پس در واقع استفاده از اسکوپ های لوکال فرآیند دیباگ کردن برنامه را تسهیل می کند و به خصوص در برنامه های بزرگ باید تا حد امکان از متغیرهای گلوبال کم تر استفاده کرد.

دانلود فایل‌های تمرین
لیست نظرات
کاربر میهمان
دیدگاه شما چیست؟
کاربر میهمان
surreal.iran
surreal.iran
نمیدونم چرا اما بهترین وبسایت اموزشی رو دارید :))))
کاربر میهمان
کاربر میهمان
سلام ممنون واسه این پست من چند تا مطلب انگلیسی و همینطور فارسی در مورد قاعده LEGB خوندم ولی هنوز یک چیزی واسم گنگ هست و اونم اینکه آیا این قاعده فقط در تابع صادق هست؟ یعنی منظور اینکه ما در تابع فقط متغیرهامون به لوکال گلوبال انکلوز تتقسیم میشوند؟
محمدهادی
محمدهادی
ای کاش پایتون در هر صورت متغیرهای بانام یکسانو خطا میگرفت!!!
به نظر شما بهتر نبود؟
Mrj
Mrj
با عرض سلام و خسته نباشید.
در سطر چهارم از اول ، دوبار کلمه دامنه را برای >>>دامنه ی سراسری<<<< نوشته اید.
لطفا اصلاح کنید.
با تشکر.
ادمین سایت
ادمین سایت
سلام
ممنون از تذکر شما
اصلاح گردید.

ارادتمند
تیم سکان آکادمی

در پاسخ به