Table of Contents
تفاوت محدودهٔ متغیرها
در پایتون هر متغیر «جایی» دارد که در آن قابل استفاده است. به این «جا» میگوییم محدودهٔ متغیر (Scope). در این بخش روی دو نوع محدوده تمرکز میکنیم:
- محلی (Local)
- سراسری (Global)
درک درست این دو نوع محدوده کمک میکند از خطاهای گیجکننده جلوگیری کنید و کد تمیزتری بنویسید.
متغیر سراسری (Global)
متغیر سراسری متغیری است که بیرون از همهٔ توابع تعریف شده باشد.
- در همهٔ کد (بعد از نقطهای که تعریف شده) در دسترس است.
- داخل تابع هم قابل خواندن است (تا وقتی همنامِ یک متغیر محلی نشده باشد).
x = 10 # متغیر سراسری
def show_x():
print(x) # از x سراسری استفاده میکند
show_x() # خروجی: 10
print(x) # خروجی: 10
اینجا x بیرون از تابع تعریف شده، پس «سراسری» است و هم در تابع show_x و هم بعد از آن قابل استفاده است.
متغیر محلی (Local)
متغیر محلی متغیری است که داخل یک تابع تعریف میشود:
- فقط داخل همان تابع قابل استفاده است.
- بیرون از آن تابع اصلاً شناختهشده نیست.
def test():
y = 5 # متغیر محلی
print("داخل تابع:", y)
test()
print("بیرون تابع:", y) # خطا: NameError
متغیر y فقط درون تابع test وجود دارد. وقتی تابع تمام میشود، y دیگر در دسترس نیست.
تداخل نامها: وقتی محلی و سراسری همنام میشوند
اگر داخل تابع متغیری با همان نام یک متغیر سراسری تعریف کنید، متغیر محلی روی آن نام درون تابع «سایه» میاندازد.
x = 10 # سراسری
def test():
x = 5 # محلی؛ روی x سراسری سایه میاندازد
print("داخل تابع:", x)
test()
print("بیرون تابع:", x)خروجی:
- داخل تابع:
5(ازxمحلی استفاده میکند) - بیرون تابع:
10(ازxسراسری استفاده میکند)
این دو x در واقع دو متغیر جدا هستند که فقط نام یکسان دارند.
چرا نمیتوانیم بهسادگی سراسری را داخل تابع تغییر دهیم؟
اگر فقط بخواهید مقدار یک متغیر سراسری را بخوانید، مشکلی نیست. اما اگر بخواهید تغییرش دهید، پایتون فرض میکند که شما یک متغیر محلی جدید ساختهاید.
count = 0
def wrong():
count = count + 1 # خطا!
print(count)
wrong()این کد خطا میدهد، چون:
- درون تابع، پایتون میبیند که
countدر سمت چپ=آمده، - پس نتیجه میگیرد که
countیک متغیر محلی است، - اما قبل از اینکه مقداری به آن بدهیم، در عبارت
count + 1میخواهیم از آن استفاده کنیم، - بنابراین خطای
UnboundLocalErrorرخ میدهد.
پس: وجود count = ... داخل تابع، باعث میشود count در آن تابع محلی محسوب شود.
استفاده از `global` برای تغییر متغیر سراسری
اگر واقعاً لازم شود یک متغیر سراسری را از داخل تابع تغییر دهید، باید از کلمهٔ کلیدی global استفاده کنید و صریحاً بگویید که منظور شما متغیر سراسری است.
count = 0 # سراسری
def increase():
global count # از count سراسری استفاده کن
count = count + 1
increase()
increase()
print(count) # خروجی: 2
خط global count به پایتون میگوید:
هرجا در این تابع از count استفاده میکنم، منظورم همان متغیر سراسری است، نه یک متغیر محلی جدید.
چرا استفاده زیاد از `global` بد است؟
هرچند global وجود دارد، ولی استفادهٔ زیاد از آن معمولاً ایدهٔ خوبی نیست:
- فهمیدن اینکه کدام تابع چه چیزی را عوض میکند سخت میشود.
- اشکالزدایی مشکلتر میشود.
- توابع دیگر «خالص» یا قابل اعتماد نیستند، چون وابسته به وضعیت بیرونی هستند.
الگوی بهتر این است که:
- متغیرها را ورودیِ تابع کنید،
- و مقدار جدید را بهصورت خروجیِ تابع برگردانید.
def increase(count):
return count + 1
count = 0
count = increase(count)
count = increase(count)
print(count) # خروجی: 2در این روش:
- تابع چیزی را بیرون از خودش مستقیماً دستکاری نمیکند،
- فقط ورودی میگیرد و خروجی میدهد،
- و این باعث میشود کد قابلفهمتر و مطمئنتر باشد.
محدودهٔ توابع تو در تو (کمی عمیقتر)
گاهی داخل یک تابع، تابع دیگری تعریف میکنیم:
x = 10 # سراسری
def outer():
x = 20 # محلی برای outer
def inner():
x = 30 # محلی برای inner
print("داخل inner:", x)
inner()
print("داخل outer:", x)
outer()
print("بیرون همه:", x)محدودهها بهصورت «لایهلایه» کار میکنند:
- محلی تابع فعلی
- محدودهٔ توابع بیرونی (در صورت وجود)
- سراسری
- توابع و نامهای داخلی پایتون
در مثال بالا:
innerبهxخودش (۳۰) دسترسی دارد.- اگر
x = 30نبود و فقطprint(x)بود،innerسراغxدر تابعouterمیرفت (۲۰). - اگر آن هم نبود، سراغ
xسراسری میرفت (۱۰).
(جزئیات کامل این مدل جستجو معمولاً با نام LEGB شناخته میشود، که اگر نیاز باشد در بخشهای پیشرفتهتر میتوانید با آن آشنا شوید.)
اشتباههای رایج مبتدیان دربارهٔ محدوده
۱. انتظار داشتن از متغیر محلی که بیرونِ تابع هم کار کند
def make_number():
n = 5
make_number()
print(n) # خطا: NameError
راه درست: اگر لازم است مقدار را بیرون استفاده کنید، از تابع برگردانید (return) و بیرون در متغیری ذخیره کنید.
def make_number():
n = 5
return n
x = make_number()
print(x) # خروجی: 5۲. فراموش کردن اثر `global`
value = 100
def change():
global value
value = 0
change()
print(value) # خروجی: 0
این نوع کدها اگر زیاد شوند، فهمیدن اینکه چرا مقدار value عوض شده، سخت خواهد شد.
چند تمرین ذهنی (بدون حل آماده)
برای تمرین، به این کدها فکر کنید و سعی کنید روی کاغذ حدس بزنید چه خروجی دارند:
۱.
x = 1
def a():
x = 2
print("a:", x)
def b():
print("b:", x)
a()
b()
print("main:", x)۲.
x = 1
def a():
global x
x = 2
def b():
x = 3
print("b:", x)
a()
b()
print("main:", x)۳.
def outer():
msg = "hello"
def inner():
print(msg)
inner()
outer()اگر در فهم خروجیها تردید داشتید، آنها را در پایتون اجرا کنید و نتیجه را ببینید. تلاش برای حدس زدن پیش از اجرا، درک شما از محدودهٔ متغیرها را خیلی قویتر میکند.