در آموزش قبل با مفهوم هر یک از اسکوپهای لوکال و گلوبال آشنا شدیم و گفتیم که دسترسی به متغیرهای لوکال تنها در اسکوپ لوکال مربوطه امکانپذیر بوده و این در حالی است که از متغیرهای گلوبال در تمام اسکوپها میتوان استفاده کرد. همچنین دیدیم که استفاده از شناسۀ مشابه به منظور نامگذاری دو متغیر متعلق به دو اسکوپ متفاوت لوکال و گلوبال امکانپذیر میباشد. برای مثال، کد زیر را در نظر بگیرید:
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
ایجاد کردهایم اما این در حالی است که مفسر پایتون از مقدار منتسب به این متغیر استفاده نمیکند چرا که در هنگام تعریف فانکشن مذکور برای مفسر پایتون مشخص شده است که متغیر مورد استفاده در این فانکشن متغیری لوکال میباشد.