در آموزش قبل با مفهوم هر یک از اسکوپهای لوکال و گلوبال آشنا شدیم و گفتیم که دسترسی به متغیرهای لوکال تنها در اسکوپ لوکال مربوطه امکانپذیر بوده و این در حالی است که از متغیرهای گلوبال در تمام اسکوپها میتوان استفاده کرد. همچنین دیدیم که استفاده از شناسۀ مشابه به منظور نامگذاری دو متغیر متعلق به دو اسکوپ متفاوت لوکال و گلوبال امکانپذیر میباشد. برای مثال، کد زیر را در نظر بگیرید:
def setScope():
scope = "local"
scope = "global"
setScope()
print(scope)
در کد فوق، فانکشنی به نام ()setScope تعریف کردهایم و گفتهایم در صورت فراخوانی، استرینگ «local» را به متغیری تحت عنوان scope اختصاص دهد و در سطر بعد هم متغیری گلوبال تحت عنوان scope تعریف کرده و استرینگ «global» را به آن منتسب کردهایم. حال فانکشن ()setScope را فراخوانی میکنیم که در نتیجه متغیری لوکال با همان شناسۀ scope ایجاد شده و استرینگ «local» به آن منتسب میشود.
اکنون دو متغیر با نامی یکسان اما هویتی متفاوت داریم که متغیر لوکال با پایان کار فانکشن از حافظۀ کامپیوتر حذف میشود و از همین روی دستور پایانی استرینگ منتسب به متغیر گلوبالِ scope را در خروجی چاپ میکند. برنامۀ فوق را در فایلی به نام setScope.py ذخیره کرده و اجرا میکنیم که نتیجۀ حاصل از آن بدین صورت خواهد بود:
global
همانطور که میبینید، فراخوانی فانکشن ()setScope هیچ تأثیری روی متغیر گلوبال scope نداشته است اما فرض کنید بخواهیم متغیر scope را به صورت متغیری گلوبال در اسکوپ لوکال فانکشن ()setScope تعریف کنیم که برای این منظور از کیورد global استفاده کرده و مثال قبل را به صورت زیر بازنویسی میکنیم:
def setScope():
global scope
scope = "local" # This is a global variable.
scope = "global"
setScope()
print(scope)
در کد فوق فانکشنی به نام ()setScope تعریف کرده و متغیری تحت عنوان scope ایجاد کردهایم و با بهکارگیری کیورد global در ابتدای شناسۀ آن به مفسر پایتون اعلام کردهایم که در این فانکشن شناسۀ scope به یک متغیر گلوبال ارجاع میدهد و بنابراین هیچ متغیر لوکالی با این شناسه نسازد و در ادامه استرینگ «local» را به آن منتسب میکنیم. در سطر بعد نیز متغیر گلوبال دیگری به نام scope تعریف کرده و استرینگ «global» را به آن منتسب کردهایم سپس فانکشن ()setScope را فراخوانی کرده و در سطر بعد مقدار منتسب به متغیر scope را به فانکشن ()print دادهایم تا آن را در خروجی چاپ کند. اسکریپت فوق را در فایلی به نام setGlobalScope.py ذخیره کرده و آن را اجرا میکنیم که نتیجۀ حاصل از اجرا بدین ترتیب خواهد بود:
localدر واقع، اجرای برنامه از خط چهارم آغاز میشود و یک متغیر گلوبال با شناسۀ scope ایجاد شده و استرینگ «global» به آن منتسب میشود و در ادامه فانکشن ()setScope فراخوانی شده و متغیر گلوبال دیگری با همان شناسه ایجاد میکند و استرینگ «local» به آن منتسب میشود که منجر بدین خواهد شد تا مقدار پیشین منتسب به متغیر گلوبالِ scope به استرینگ «local» تغییر پیدا کند و در ادامه مقدار این متغیر به فانکشن ()print داده شده و در خروجی چاپ میشود.
به طور کلی، چند قاعدۀ کلی به منظور تشخیص لوکال یا گلوبال بودن یک متغیر وجود دارد که عبارتند از:
- اگر یک متغیر در اسکوپ گلوبال (خارج از بدنۀ تمامی فانکشنهای برنامه) تعریف و استفاده شود، متغیری گلوبال است.
- اگر شناسۀ متغیری در بدنۀ فانکشن با کیورد global آغاز شود، متغیر مذکور گلوبال است.
- تمامی متغیرهایی که در بدنۀ یک فانکشن تعریف شده و مقداری به آنها منتسب میشود، متغیری لوکال هستند مگر آنکه پیش از شناسۀ آنها از کیورد global استفاده شود.
- اگر متغیری در بدنۀ یک فانکشن استفاده شود اما مقداری به آن منتسب نشود، متغیر مذکور گلوبال است.
برای درک بهتر هر یک از قواعد فوق مثالی را در ادامه آوردهایم:
def setGlobalScope():
global scope
scope = "global" # this is the global
def setScope():
scope = "local" # this is the local
def printScope():
print(scope) # this is the global
scope = None # this is the global
setGlobalScope()
setScope()
printScope()
در کد فوق، فانکشنی تحت عنوان ()setGlobalScope تعریف کرده و گفتهایم در صورت فراخوانی، متغیری گلوبال به نام scope ساخته و استرینگ «global» را به آن منتسب کند و در ادامه فانکشن دیگری به نام ()setScope تعریف کردهایم که در صورت فراخوانی، متغیری به نام scope میسازد و استرینگ «local» را به آن منتسب میکند (این متغیر در یک دستور انتسابی و در بدنۀ فانکشن ایجاد خواهد شد که از همین روی یک متغیر لوکال خواهد بود.) در ادامه فانکشنی به نام ()printScope تعریف کردهایم که در صورت فراخوانی، متغیری به نام scope را به عنوان آرگومان ورودی به فانکشن ()print میدهد تا مقدار منتسب به آن را در خروجی چاپ کند که در این فانکشن متغیر scope در دستور انتسابی به کار نرفته است که از همین روی یک متغیر گلوبال میباشد. در سطر بعد متغیری گلوبال به نام scope ایجاد کرده و مقدار آن را برابر با None یا به عبارتی «خالی» قرار دادهایم و در ادامه هر یک از فانکشنهای فوقالذکر را فراخوانی کردهایم. حال کد فوق را در فایلی به نام sameName.py ذخیره کرده و آن را اجرا میکنیم که خروجی حاصل بدین صورت خواهد بود:
global
مفسر پایتون اجرای برنامۀ فوق را از دستور scope = None شروع میکند که بدین ترتیب یک متغیر گلوبال به نام scope بدون هیچ مقداری ایجاد کرده سپس فانکشن ()setGlobalScope فراخوانی شده و متغیری گلوبال با شناسۀ یکسانِ scope ایجاد میشود و از همین روی مقدار این متغیر گلوبال به استرینگ «global» تغییر پیدا میکند و در ادامه فانکشن ()setScope فراخوانی شده و متغیری لوکال تحت عنوان scope در آن ایجاد شده و با پایان یافتن کار فانکشن نیز استرینگ اختصاصیافته به آن از حافظۀ سیستم پاک میشود.
در نهایت، فانکشن ()printScope فراخوانی شده و مقدار منتسب به متغیر scope را به عنوان آرگومان ورودی گرفته و آن را چاپ میکند (متغیر scope در فانکشن ()printScope در یک دستور انتسابی نیامده است و از همین روی یک متغیر گلوبال میباشد که آخرین مقدار منتسب به آن توسط فانکشن ()print در خروجی چاپ میشود.)
به طور کلی، میتوان گفت که هر یک از متغیرهای تعریفشده در بدنۀ یک فانکشن به صورت همزمان میتوانند تنها یکی از دو حالت لوکال و یا گلوبال باشند؛ به عبارت دیگر، در یک فانکشن نمیتوان از شناسهای یکسان برای نامگذاری دو متغیر لوکال و گلوبال استفاده کرد و برای تعریف یک متغیر گلوبال در بدنۀ فانکشن نیز حتماً میباید از کیورد global استفاده کرد. همچنین در استفاده از متغیرهای لوکال داخل بدنۀ فانکشن ابتدا باید آبجکتی به آنها منتسب کنیم که برای درک بهتر این موضوع مثال زیر را مد نظر قرار میدهیم:
def printScope():
print(scope) # Error!
scope = "local scope" # this is the local
scope = "global scope" # this is the global
printScope()
در کد فوق، فانکشنی تحت عنوان ()printScope تعریف کردهایم که در صورت فراخوانی، متغیری با شناسۀ scope را به عنوان آرگومان ورودی به فانکشن ()print میدهد تا مقدار منتسب به آن را در خروجی چاپ کند و در ادامه متغیری به نام scope ایجاد کرده و استرینگ «local scope» را به آن منتسب کند (این متغیر در یک دستور انتسابی و در بدنۀ یک فانکشن ایجاد میشود که از همین روی مفسر پایتون آن را متغیری لوکال میشناسد.) در سطر بعد متغیری گلوبال به نام scope ایجاد کرده و استرینگ «global scope» را به آن منتسب کردهایم و در نهایت فانکشن ()printScope را فراخوانی میکنیم. حال اسکریپت فوق را در فایلی به نام error.py ذخیره کرده و آن را اجرا میکنیم که بدین ترتیب در خروجی خواهیم داشت:
Traceback (most recent call last):
File "D:/sokanacademy/Python/error.py", line 6, in
printScope()
File " D:/sokanacademy/Python/error.py ", line 2, in printScope
print(scope) # Error!
UnboundLocalError: local variable 'scope' referenced before assignment
همانطور که میبینید، اجرای برنامۀ فوق منجر به ارور شده است چرا که مفسر پایتون متغیر scope را به دلیل مقداردهی آن در بدنۀ فانکشن یک متغیر لوکال شناخته است اما با این حال دستور سطر دوم پیش از ایجاد این متغیر لوکال اجرا میشود؛ به عبارتی، آرگومان ورودی به فانکشن ()print یک متغیر لوکال است که مفسر پایتون نمیتواند آن را پیدا کند چرا که هنوز ایجاد نشده است!
البته لازم به یادآوری است که پیش از فراخوانی فانکشن ()printScope متغیری گلوبال با شناسۀ یکسانِ scope ایجاد کردهایم اما این در حالی است که مفسر پایتون از مقدار منتسب به این متغیر استفاده نمیکند چرا که در هنگام تعریف فانکشن مذکور برای مفسر پایتون مشخص شده است که متغیر مورد استفاده در این فانکشن متغیری لوکال میباشد.
