فهرست عناوین

موضوع قبلی

6. Increment

موضوع بعدی

8. عملگرهای مقایسه

7. دامنهٔ کار متغیّر‌ها: محلّی (local) یا سراسری (global)

ابتدا، یک یادآوری دوستانه:

مهم

قانون شمارهٔ ۱

یادگیری ‌برنامه‌نویسی مثل یادگرفتن نوازندگی یک ساز است: باید برنامه بنویسید و انجام بدهید تا یاد بگیرید؛ نه این که فقط متن‌ها را بخوانید.

حالا که حواستان را به این نکته جمع کردم؛ مهم است که قبل مطالعهٔ بیشتر، خودتان به تنهایی این کارها را امتحان کنید.

این برنامه‌ها را اجرا کنید

اوّل این برنامه را اجرا کنید:

#برنامهٔ نخست
a = 2

def test():
    a = 3
    print("درون آزمایش ", a)

test()

سپس، این یکی را:

# برنامهٔ دوم
a = 2

def test():
    print("درون آزمایش", a)

test()
print("پس از آزمایش", a)

و یکی دیگر را:

# برنامهٔ سوم
a = 2

def test():
    print("درون آزمایش", a)
    a = 3

test()
print("پس از آزمایش", a)

و یکی بیشتر را:

# برنامهٔ چهارم
a = 2

def test():
    global a
    print("درون آزمایش", a)
    a = 3

test()
print("پس از آزمایش", a)

چی شد؟! ...

7.1. نیاز به دامنه

الآن که دارم این متن را می‌نویسم؛ برنامهٔ جاوا اسکریپتی محیط ریبرگ را راه می‌اندازد در بر گیرندهٔ تقریباً ۶۰۰۰ سطر کد است. این عدد احتمالاً کمی بیشتر از برنامه‌هایی است که تا به حال نوشته‌اید! (البتّه، چند تایی فایل اضافی برنامه‌ هست، که به پایتون نوشته شده!) کسانی که با هم کار می کنند برنامه‌هایی می‌نویسند؛ اگر میلیون‌ها سطر نداشته باشد، دارای صدها و هزاران سطر کد است. همان‌طور که می‌توانید تصّور کنید؛ پیدا کردن اسم‌های معنادار برای متغیّر‌هایی که معنای یکتایی در سرتاسر برنامه داشته باشند غیرممکن است. فرض کنید شما و من برای نوشتن یک برنامهٔ خیلی خیلی طولانی داریم همکاری می‌کنیم. اگر من مقدار متغیّر length (طول) را 32 تعیین کنم و شما یک متغیّر با همان نام را یک جای دیگر تعریف کنید و به آن مقدار 45 بدهید؛ وقتی بخواهیم از این متغیّر استفاده کنیم ممکن است مشکل داشته باشیم.

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

در اصلاح می‌گوییم این متغیّرها محلّی تابع هستند یا آن‌ها دامنهٔ محلّی دارند.

بیاید به اوّلین برنامهٔ بالا نگاه کنیم. ما یک متغیّر به نام a درون تابع ()test تعریف کردیم. این متغیّر محلّی تابع به حساب می‌آید. مقدار دادن به متغیّر درون تابع، مقدار متغیّر a بیرون از آن تابع را تغییر نمی‌دهد. این دو متغیّر، از هم متمایز محسوب می‌شوند.

در برنامهٔ دوم، متغیّر a را درون تابع ()test نکردیم. یعنی؛ سطری مثل این را درون تابع نداریم:

a = something

وقتی ما می‌خواهیم مقدار متغیّر را با print چاپ کنیم؛ پایتون تشخیص می‌دهد که این متغیّر احتمالاً بیرون و در یک رده بالاتر از تابع تعریف شده است (که به عنوان دامنهٔ سراسری متغیّر‌ها شناخته می‌شود). برای همین دنبال آن در دامنهٔ سراسری متغیّر‌ها می‌گردد و متغیّری با آن نام پیدا می‌کند و از آن استفاده می‌کند.

توجه

باورتان بشود یا نه، توضیح این‌جا ساده‌سازی است. یک کلیدواژهٔ دیگر پایتونی به نام nonlocal به معنی غیر محلّی هست که به متغیّرهایی با دامنه‌‌ی میانی -بین محلّی و سراسری- گفته می‌شود. با این همه، وقتی داشتم این آموزش‌ها را می‌نوشتم، هیچ مثالی مرتبط با محیط ریبرگ پیدا نکردم؛ که در آن استفاده از nonlocal مفید باشد.

در برنامهٔ سوم، پایتون می‌فهمد که متغیّری در محلّ تابع هست (یعنی درون تابع تعریف شده) چون این یک سطر هست:

a = something   #یک چیز برابر با سه این‌جا می‌آید

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

در نهایت، در برنامهٔ چهارم، ما این خط را اضافه کرده‌ایم:

global a

global یک پایتونی است که به پایتون می‌گوید متغیّر a که داخل که درون تابع به کار رفته همانی است که بیرون تابع (در دامنهٔ سراسری) تعریف شده است. بنابراین، این سطر اجرا می‌شود:

print("inside test", a)

از قبل مقدار شناخته شده‌ای دارد. در خط بعد، a = 3 مقدار a را تغییر می‌دهد به گونه‌ای که بعد از اجرای ()test این مقدرا جدید را در همه جا خواهد داشت.

مهم

هر جا یک برنامه‌نویس با تجربه یک تابع یک یا چند متغیّر در مقابل گزارهٔ global ردیف شده می‌بیند، نگران می‌شود. در عوض صرفاً تلاش برای فهمیدن تابع، برنامه‌نویس باید دریابد چه جای دیگری آن متغیّر(ها) مورد استفاده و انتساب قرار گرفته‌اند و چطور این کار می‌تواند کارکرد تابع را متأثّر کند.

برای همین، برنامه‌نویس‌های باتجربه همیشه تلاش می‌کنند از ابزارهای دیگری -که هنوز شما از آن‌ها اطّلاع ندارید- استفاده کنند تا جلوی استفاده از متغیّر‌های سراسری را بگیرند. با این وجود، یادگیری چگونگی استفاده از متغیّرهای سراسری مهم است و از آن مهم تر کِی* استفاده کردن از آن‌ها.

7.2. گیج شدید؟

افراد زیادی بار اوّلی که مفهوم دامنه‌ را می‌بینند برایشان گیج کننده است. شاید دلتان بخواهد برنامهٔ ۴ را دوبار (و دوباره!) اجرا کنید و چند باری توضیحات را بخوانید.

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