درس ۰۹: دستورهای کنترلی

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

این درس به بررسی دستورهای کنترلی پایتون در دو بخش «انتخاب» و «تکرار» اختصاص یافته است.

سطح: مقدماتی



انتخاب

با استفاده از دستور انتخاب می‌توان بر حسب شرایط برنامه در زمان اجرا تعیین کرد که آیا یک بلاک دستور اجرا شود یا خیر و همچنین از بین دو یا چند بلاک دستور کدام یک انتخاب و اجرا گردد. پایتون تنها یک ساختار انتخاب را ارایه می‌دهد که می‌تواند به سه شکل «تک انتخابی»، «دو انتخابی» و «چند انتخابی» پیاده‌سازی گردد؛ این ساختار به نام دستور if خوانده می‌شود و در ادامه بررسی خواهد شد.

دستور if

۱. ساختار ساده (تک انتخابی)

این ساختار یک دستور مرکب است که در سرآیند آن یک «شرط» (Condition) بررسی می‌گردد و تنها در صورتی که این شرط برقرار باشد بدنه اجرا خواهد گشت؛ در غیر این صورت مفسر از اجرای دستور(های) بدنه صرف نظر کرده و به سراغ نخستین دستور بعد از این ساختار می‌رود. این ساختار با استفاده از کلمه کلیدی if و الگویی مشابه پایین پیاده‌سازی می‌گردد:

if condition :
    StatementBlock

منظور از شرط عبارتی است که می‌توان آن را به یکی از مقدار‌های بولی (True یا False) ارزیابی نمود؛ در اینجا اگر شرط برابر True ارزیابی گردد بخش بدنه دستور if اجرا می‌گردد. به نمونه کدهای پایین توجه نمایید:

>>> a = 5
>>> b = 3
>>> if a > b:
...     print("a is greater than b")
...
a is greater than b
>>>
>>> if a == b:
...     print("a is equal to b")
...
>>>

در نمونه کد بالا شرط برابر False ارزیابی شده و از اجرای بدنه خودداری شده است؛ بنابراین هیچ متنی در خروجی چاپ نشده است.

>>> if a > b and a >= 0:
...     print("a is positive and greater than b")
...
a is positive and greater than b
>>>

همانطور که در نمونه کد بالا نیز مشاهده می‌شود می‌توان از عملگرهای منطقی (not ،or ،and) برای بررسی برقرار بودن (یا نبودن) همزمان چندین شرط بهره گرفت.

می‌دانیم که: عدد یک و تمام اعداد مخالف صفر در پایتون برابر مقدار بولی True و عدد صفر، اشیا خالی به مانند "" یا [] برابر مقدار False ارزیابی می‌شوند:

>>> if 1:
...     print("Condition is True")
...
Condition is True
>>>
>>> if []:
...     print("Condition is True")
...
>>>
>>> a = False

>>> if not a:
...     print("Condition is True")
...
Condition is True
>>>

می‌توان از ساختار if به شکل تودرتو (Nested) نیز بهره برد. در این حالت بدنه دستور if حاوی یک یا چند دستور if دیگر می‌شود که البته آن‌ها نیز می‌توانند حاوی دستور‌های if دیگری در بدنه خود باشند:

>>> d = {'name': 'Jhon', 'job': 'programmer', 'age': 40}

>>> if d['age'] >= 40:
...     if d['job'] == 'programmer':
...         print(d['name'])
...
Jhon
>>>

۲. ساختار همراه با else (دو انتخابی)

با استفاده از کلمه‌ کلیدی else می‌توان بلاکی را برای اجرا در حالتی که شرط برقرار نیست - زمانی که شرط if برابر مقدار بولی False ارزیابی می‌گردد - تعیین کرد. else یک بخش جدا است که سرآیند و بدنه مخصوص به خود را دارد؛ این سرآیند می‌بایست فاقد هر گونه شرطی باشد:

>>> a = False

>>> if a:
...     print("Condition is True")
... else:
...     print("Condition is False")
...
Condition is False
>>>
>>> a = 7

>>> if a in [1, 2, 3]:
...     print("a is in list")
... else:
...     print("a is not in list")
...
a is not in list
>>>
>>> d = {'name': 'Bob', 'job': 'designer', 'age': 45}

>>> if d['age'] >= 40:
...     if d['job'] == 'programmer':
...         print(d['name'])
...     else:
...         print(d['name'], d['job'])  # Will be executed
... else:
...     if d['age'] >= 35:
...         print(d['name'], 'Between 35 and 40 years old')
...     else:
...         print(d['name'], 'Less than 35 years old')
...
Bob designer
>>>

۳. ساختار همراه با elif (چند انتخابی)

دستور if را می‌توان گسترش داد و بخش‌های بیشتری را با شرط‌های گوناگون ایجاد نمود؛ به این صورت که ابتدا شرط بخش if بررسی می‌گردد و چنانچه برابر True ارزیابی نگردد، شرط مربوط به نختسین بخش elif بررسی می‌گردد که اگر باز هم برابر True نشود شرط بخش elif بعدی بررسی خواهد شد و به همین صورت ادامه می‌یابد؛ در انتها نیز اگر هیچ کدام از شرط‌ها (if و elif) برابر True نشوند آنگاه بدنه مربوط به بخش else (در صورت وجود) اجرا می‌گردد. الگوی این ساختار مانند پایین است:

if condition_1:
    statements
elif condition_2:
    statements
elif condition_3:
    statements
else:
    statements
  • هر elif یک بخش جدا است که سرآیند و بدنه مخصوص به خود را دارد.
  • تعداد بخش‌های elif اختیاری است و محدودیتی در آن وجود ندارد.
  • بخش elif نمی‌تواند قبل از if یا بعد از else قرار بگیرد.
  • در این ساختار نیز وجود بخش else اختیاری است.

در این ساختار بخش‌ها به ترتیب از بالا به پایین بررسی می‌شوند و با True ارزیابی شدن شرط هر بخش، بدنه مربوط به آن اجرا و از بررسی دیگر بخش‌ها صرف نظر می‌گردد. به نمونه کد پایین توجه نمایید:

>>> percent = 60

>>> if percent == 100:
...    print('100 %')
... elif percent >= 75:
...    print('75-100 %')
... elif percent >= 50:
...    print('50-75 %')
... elif percent >= 25:
...    print('25-50 %')
... else:
...    print('less than 25 %')
...
50-75 %
>>>

اگر بخواهیم نمونه کد بالا را با استفاده از if های تودرتو پیاده‌سازی نماییم به شکل پایین خواهد شد:

>>> percent = 60

>>> if percent == 100:
...     print('100 %')
... else:
...     if percent >= 75:
...         print('75-100 %')
...     else:
...         if percent >= 50:
...             print('50-75 %')
...         else:
...             if percent >= 25:
...                 print('25-50 %')
...             else:
...                 print('less than 25 %')
...
50-75 %
>>>

چنانچه قصد دارید تمام شرط‌های مورد نظر بررسی شوند می‌توانید از چند دستور if به شکل متوالی استفاده نمایید:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# File: Documents/script.py
# Python 3.x

import sys

# Get script argument and convert it to an integer
percent = int(sys.argv[1])

if percent == 100:
    print('100 %')
if percent >= 75:
    print('75-100 %')
if percent >= 50:
    print('50-75 %')
if percent >= 25:
    print('25-50 %')
if percent < 25:
    print('less than 25 %')
user> cd Documents/

user> python script.py 60
50-75 %
25-50 %

دستور switch/case

در صورتی که سابقه برنامه‌نویسی با زبان‌های دیگری همچون C و Java را داشته باشید حتما با دستور switch نیز آشنا هستید؛ این دستور در زبان پایتون پیاده‌سازی نشده است.

دستور switch مقداری را دریافت می‌کند و سپس آن را با مقدارهای هر case درون ساختار خود به ترتیب مقایسه می‌کند؛ در صورتی که این مقدار با یکی از case ها برابر باشد، دستورهای مرتبط با آن case را اجرا کرده و از بررسی دیگر case ها صرف نظر می‌کند. همچنین اگر مقدار دریافتی با هیچ یک از case ها مطابقت نداشته باشد دستورهای مربوط به بخش default (در صورت وجود) را اجرا می‌کند. در پایین نمونه‌ایی از این دستور در زبان Java آورده شده است:

int success;
char grade = 'B';
switch (grade) {
            case 'A':
                    System.out.println("Excellent grade");
                    success = 1;
                    break;
            case 'B':
                    System.out.println("Very good grade");
                    success = 1;
                    break;
            case 'C':
                    System.out.println("Good grade");
                    success = 1;
                    break;
            case 'D':
            case 'E':
            case 'F':
                    System.out.println("Low grade");
                    success = 0;
                    break;
            default:
                    System.out.println("Invalid grade");
                    success = -1;
                    break;
}

برای پیاده‌سازی چنین ساختاری در پایتون می‌توان از if/elif/else استفاده کرد:

grade = 'B'
if grade == 'A':
    print('Excellent grade')
    success = 1
elif grade == 'B':
    print('Very good grade')
    success = 1
elif grade in ('D', 'E', 'F'):
    print('Low grade')
    success = 0
else:
    print('Invalid grade')
    success = -1

تکرار

گاهی نیاز پیدا می‌کنیم که بلاکی را چندین بار پشت سرهم اجرا نماییم. به ساختار تکرار «حلقه» (Loop) گفته می‌شود؛ در ادامه به بررسی ساختار دو حلقه ارایه شده در زبان پایتون خواهیم پرداخت.

دستور while

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

while condition :
    statements

شرط همواره می‌بایست از درون بدنه کنترل شود به گونه‌ای که در مرحله‌ خاصی برابر مقدار False ارزیابی گردد؛ در غیر این صورت یک حلقه بی‌نهایت ایجاد می‌شود که مفسر هیچگاه نمی‌تواند از اجرای آن خارج شود. برای نمونه اجرای دستور پایین هیچگاه توسط مفسر پایتون پایان نمی‌پذیرد و برای اتمام آن می‌بایست از سیستم عامل کمک گرفت:

>>> while 1:
...     print('Press Ctrl+C to stop!')
...
Press Ctrl+C to stop!
Press Ctrl+C to stop!
Press Ctrl+C to stop!
[..]

ولی در نمونه کد پایین مقدار متغیر a از درون بدنه کنترل و در هر بار اجرا یک واحد کاهش می‌یابد؛ بنابراین اجرای حلقه تنها تا زمانی که شرط نقض نشده باشد ادامه می‌یابد:

>>> a = 5

>>> while a > 0:
...     print(a)
...     a -= 1   # a = a - 1
...
5
4
3
2
1
>>>

در نمونه کد بالا بهتر می‌بود به جای عبارت a > 0 تنها از خود متغیر a به عنوان شرط حلقه استفاده نماییم؛ چرا که در هر مرتبه اجرا یک واحد از آن کم می‌شود و با رسیدن به مقدار صفر به صورت خودکار توسط مفسر پایتون به مقدار False ارزیابی و تکرار اجرای بدنه حلقه متوقف می‌گردد.

به عنوان نمونه‌ای دیگر،‌ فاکتوریل (Factorial) عدد ۱۰ را می‌توان به صورت پایین محاسبه کرد:

>>> a = 10

>>> n = 1
>>> while a >= 1:
...     n = n * a
...     a -= 1
...
>>> print(n)
3628800

دستور continue

این دستور در هر نقطه از بخش بدنه که آورده شود، دستورهای بعد از آن نادیده گرفته می‌شوند و جریان اجرا به ابتدای حلقه یعنی بخش سرآیند پرش می‌کند. برای نمونه می‌خواهیم اعداد صحیح زوجی که کوچکتر از ۱۰ هستند را بر روی خروجی نمایش دهیم. در نمونه کد پایین برای اعداد فرد دستور continue از ادامه اجرا و نمایش آن‌ها جلوگیری می‌کند و جریان اجرا را به ابتدای حلقه پرش می‌دهد:

>>> n = 10

>>> while n:
...     n -= 1
...     if n % 2 != 0:
...         continue
...     print(n)
...
8
6
4
2
0
>>>

البته مثال بالا را بدون continue نیز می‌توان به انجام رساند:

>>> n = 10
>>> while n:
...     n -= 1
...     if n % 2 == 0:
...         print(n)

دستور break

این دستور در هر نقطه از بخش بدنه که آورده شود، دستورهای بعد از آن نادیده گرفته می‌شوند و جریان اجرا از حلقه خارج می‌شود. در نمونه کد پایین با هر اجرای بدنه یک واحد به counter افزوده می‌شود و هرگاه مقدار آن برابر ۴ گردد، بدون توجه به شرط، اجرای حلقه متوقف می‌شود:

>>> counter = 0

>>> while counter < 100:
...     if counter == 4:
...         break
...     print(counter)
...     counter += 1
...
0
1
2
3
>>>

در while نیز می‌شود از بخش else استفاده نماییم؛ به این صورت که اگر حلقه به صورت طبیعی پایان پذیرد - و نه توسط دستور break - آنگاه بدنه else اجرا می‌گردد.

نمونه کد پایین بررسی می‌کند که آیا عدد n یک «عدد اول» (Prime number) هست یا خیر؛ این اعداد بزرگتر از یک بوده و به جز خود و عدد یک بر هیچ عدد دیگری بخش پذیر نیستند. بنابراین اگر عددی کوچکتر از n (به جز یک) پیدا شود که بر آن بخشپذیر باشد (یعنی باقی مانده تقسیم بر آن صفر باشد) اول نبودن عدد n ثابت می‌شود و حلقه به کمک دستور break متوقف می‌گردد:

>>> n = 23
>>> i = 2
>>> while i < n:
...     if n % i == 0:
...         print(n, "is not a prime number")
...         break
...     i += 1
... else:
...     print(n, "is a prime number")
...
23 is a prime number
>>>

دستور for

این دستور مرکب یک حلقه تکرار است که بر اساس تعداد عضوهای یک شی دنباله یا در حالت کلی‌تر یک شی تکرارکننده (iterator) - که در انتها بررسی خواهد شد - اجرای دستورهای بدنه را تکرار می‌کند. الگوی این دستور به شکل پایین است:

for target in object:
    statements

هر حلقه for دقیقا به تعداد عضوهای شی object تکرار می‌گردد؛ هر بار یک عضو از دنباله (یا تکرارکننده) object با حفظ ترتیب اعضا به متغیر target انتساب داده می‌شود و یک مرتبه بدنه اجرا می‌گردد؛ این روند تا پایان پیمایش عضوهای object ادامه می‌یابد. از متغیر target می‌توان در داخل بدنه استفاده کرد که در مرتبه نخست اجرای حلقه به عضو یکم و با اجراهای بعدی به عضوهای بعدی از object اشاره خواهد داشت. به نمونه کدهای پایین توجه نمایید:

>>> for item in [1, 2, 3]:
...     print(item)
...
1
2
3
>>>
>>> for char in 'python':
...     print(char)
...
p
y
t
h
o
n
>>>
>>> L = [(1, 2), (3,4), (5, 6)]

>>> for a, b in L:
...     print(a, b)
...
1 2
3 4
5 6
>>>

در نمونه کد بالا، از آنجا که هر عضو دنباله خود یک دنباله دو عضوی است، بنابراین از دو متغیر برای اشاره به شی پیمایش استفاده شده است.

>>> L = [(1, 2), (3,4), (5, 6)]

>>> for both in L:
...     a, b = both
...     print(a, b)
...
1 2
3 4
5 6
>>>

در نمونه کد بالا، متغیر both در هر مرتبه تکرار به یک شی تاپل اشاره دارد.

>>> a, *b, c = (1, 2, 3, 4)
>>> a, b, c
(1, [2, 3], 4)

>>> for a, *b, c in [(1, 2, 3, 4), (5, 6, 7, 8)]:
...     print(a, b, c)
...
1 [2, 3] 4
5 [6, 7] 8
>>>
>>> d = {'name': 'Jhon', 'job': 'designer', 'age': 40}

>>> for key in d:
...     print(key)
...
name
job
age
>>>

در حالت عادی برای یک شی دیکشنری،‌ کلیدهای آن پیمایش می‌شوند.

>>> d = {'name': 'Jhon', 'job': 'designer', 'age': 40}

>>> d.items()
dict_items([('name', 'Jhon'), ('job', 'designer'), ('age', 40)])

>>> for key, value in d.items():
...     print(key, value)
...
name Jhon
job designer
age 40
>>>

توجه

معمولا از حلقه for در مواقعی که تعداد تکرار مشخص باشد و از حلقه while زمانی که تعداد تکرار نامشخص است استفاده می‌شود.

مانند حلقه while در اینجا نیز می‌توان از دستورهای continue و break استفاده کرد. همچنین حلقه for می‌تواند شامل بخش else باشد.

مثال تشخیص عدد اول در حلقه while را با استفاده از حلقه for بازنویسی می‌کنیم:

>>> n = 23

>>> for i in range(2, n):
...     if n % i == 0:
...         print(n, "is not a prime number")
...         break
... else:
...     print(n, "is a prime number")
...
23 is a prime number
>>>

تابع (range(stop:

این تابع [اسناد پایتون 3x] یک شی از نوع range را برمی‌گرداند؛ این شی یک دنباله تغییر ناپذیر است که معمولا از آن برای پیمایش در حلقه for استفاده می‌شود. با تبدیل شی range به نوع لیست خواهیم دید که این شی یک دنباله مرتب از اعداد صفر تا آرگومان stop (و نه خود آن) است؛ آرگومان stop می‌بایست یک عدد صحیح مثبت باشد:

>>> r = range(10)    # Python 3.x

>>> type(r)
<class 'range'>

>>> r
range(10)

>>> print(r)
range(10)

>>> list(r)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> tuple(r)
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

>>> import sys
>>> sys.getsizeof(r)
48

این تابع را می‌توان به صورت دو آرگومانی ((range(start, stop) نیز فراخوانی نمود که آرگومان یکم عدد آغازین دنباله را تعیین می‌کند و می‌تواند یک عدد منفی نیز باشد:

>>> list(range(2, 10))
[2, 3, 4, 5, 6, 7, 8, 9]

>>> list(range(-2, 10))
[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

در این حالت می‌توان از آرگومان سومی نیز برای تعیین گام یا فاصله بین اعداد بهره گرفت:

>>> list(range(2, 10, 2))
[2, 4, 6, 8]

>>> list(range(2, 10, 3))
[2, 5, 8]

>>> list(range(2, 10, 4))
[2, 6]
  • هر سه آرگومان می‌بایست از نوع صحیح باشند.

  • برای تعیین آرگومان stop منفی، می‌بایست آرگومان گام را نیز به شکل منفی تعیین نمود:

    >>> list(range(2, -10, -1))
    [2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
    
    >>> list(range(2, -10, -2))
    [2, 0, -2, -4, -6, -8]
    
    >>> list(range(-2, -10, -1))
    [-2, -3, -4, -5, -6, -7, -8, -9]
    
    >>> list(range(-2, -10, -2))
    [-2, -4, -6, -8]
    

در نسخه‌های 2x پایتون دو نسخه از این تابع وجود دارد: تابع range [اسناد پایتون 2x] و تابع xrange [اسناد پایتون 2x].

خروجی تابع range یک شی از نوع لیست است:

>>> r = range(10)    # Python 2.x

>>> type(r)
<type 'list'>

>>> r
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> import sys
>>> sys.getsizeof(r)
152

ولی خروجی تابع xrange یک شی از نوع xrange می‌باشد:

>>> r = xrange(10)    # Python 2.x

>>> type(r)
<type 'xrange'>

>>> r
xrange(10)

>>> list(r)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> import sys
>>> sys.getsizeof(r)
40

خروجی تابع xrange ساده‌تر و بهینه تر از خروجی تابع range است بنابراین معمولا پیشنهاد می‌شود که در حلقه for از تابع xrange استفاده شود؛ به همین دلیل می‌باشد که تابع range در نسخه‌های 3x پایتون حذف شده است و تنها تابع xrange باقی‌مانده که با نام و نوع range پیاده‌سازی شده است.

  • چگونگی استفاده و تعداد آرگومان‌های هر دو تابع همانند نسخه 3x است که پیش از این بررسی شد.

چند مثال‌ ساده دیگر:

>>> L = ['a', 'b', 'c', 'd']

>>> for i in range(len(L)):
...     print(L[i])
...
a
b
c
d
>>>
>>> s = 'pythonprogramminglanguage'

>>> for c in s[9:13]:
...     print(c)
...
g
r
a
m
>>>
>>> reven = range(0, 10, 2)
>>> list(reven)
[0, 2, 4, 6, 8]

>>> rodd = range(1, 10, 2)
>>> list(rodd)
[1, 3, 5, 7, 9]

>>> list(zip(reven, rodd))
[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]

>>> L = []
>>> for a, b in zip(reven, rodd):
...    L.append(a*b)
...
>>> L
[0, 6, 20, 42, 72]

می‌توان نتایج حلقه for را مستقیم در یک شی لیست قرار داد؛ برای نمونه دستور پایین را در نظر بگیرید:

>>> L = []
>>> for x in range(5):
...     L.append(x**2)
...
>>> L
[0, 1, 4, 9, 16]

که می‌توان خیلی ساده آن را به صورت پایین بازنویسی کرد:

>>> [x ** 2 for x in range(5)]
[0, 1, 4, 9, 16]

و به عنوان مثال‌هایی دیگر به نمونه کدهای پایین توجه نمایید:

>>> y = 7

>>> [y * x for x in range(10)]
[0, 7, 14, 21, 28, 35, 42, 49, 56, 63]
>>> L = [(1, 2), (3, 4), (5, 6)]

>>> [a + b for a, b in L]
[3, 7, 11]
>>> [a * b for a, b in zip(range(0, 10, 2), range(1, 10, 2))]
[0, 6, 20, 42, 72]
>>> [(a, b) for a, b in zip(range(0, 10, 2), range(1, 10, 2))]
[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]

از دستورهای مرکب پایتون می‌توان در داخل بدنه یکدیگر بهره برد که البته این موضوع برای دستورهای for و while نیز صادق است. از هر دو این دستورها می‌توان بر حسب نیاز در داخل بدنه یکدیگر یا به شکل تودرتو استفاده کرد:

>>> for i in range(1, 5):
...     for j in range(0, i):
...         print(i)
...
1
2
2
3
3
3
4
4
4
4
>>>

به نمونه کد بالا توجه نمایید؛ با هر بار تکرار حلقه یکم تمام دستورهای بدنه آن که شامل یک حلقه دیگر است اجرا می‌گردد. از متغیر i درون حلقه داخلی نیز استفاده شده است. در بار نخستِ اجرای حلقه بیرونی مقدار i برابر عدد 1 قرار داده می‌شود که در این صورت اجرای حلقه داخلی تنها یک بار تکرار می‌گردد (1 == ((len(range(0, 1) و یک مقدار 1 در خروجی نمایش داده می‌شود،‌ بار دوم i برابر عدد 2 می‌شود و در نتیجه حلقه داخلی دو بار تکرار می‌گردد که بر اثر آن دو مقدار 2 در خروجی چاپ می‌گردد. این روند تا پایان تکرار حلقه بیرونی ادامه می‌یابد.

تابع (یا دستور) print به صورت پیش‌فرض پس از اجرا و چاپ مقدار به سطر بعدی می‌رود. [در درس بعد چگونگی تغییر این رفتار بررسی خواهد شد]

اگر از پیش با زبان‌هایی نظیر C یا Java آشنایی دارید؛ برای درک بهتر ساختار حلقه for پایتون نمونه کد پایین که به زبان Java است را در نظر بگیرید:

int[][] array = { { 1, 2 }, { 3 }, { 4, 5, 6 } };

for ( int row = 0; row < array.length; row++ )
{
    for ( int column = 0; column < array[ row ].length; column++ )
        System.out.printf( "%d ", array[ row ][ column ] );

    System.out.println();
}

// Paul Deitel, Harvey Deitel "Java: How to Program" (9th Edition) page 270
1 2
3
4 5 6

که می‌توانیم آن را توسط زبان پایتون به شکل پایین پیاده‌سازی نماییم:

>>> array = ((1, 2), (3,), (4, 5, 6))
>>> for row in range(0, len(array)):
...     for column in range(0, len(array[row])):
...         print("%d " % array[row][column])
...     print()

تابع (enumerate(iterable:

علاوه‌ بر تابع ()range در حلقه‌های for می‌توانیم از تابع ()enumerate [اسناد پایتون] نیز استفاده کنیم. این تابع یک شی دنباله یا تکرارکننده را به عنوان آرگومان دریافت می‌کند و یک شی از نوع enumerate برمی‌گرداند:

>>> L = ['a', 'b', 'c']

>>> e = enumerate(L)

>>> type(e)
<class 'enumerate'>

>>> e
<enumerate object at 0x7fc76a6b92d0>
>>> print(e)
<enumerate object at 0x7fc76a6b92d0>

>>> import sys
>>> sys.getsizeof(e)
72

با تبدیل این شی به یک شی لیست مشاهده می‌شود که این شی عضوهای آرگومان ورودی خود را به شکل جفت‌هایی به همراه اندیس موقعیت آن‌ها ذخیره کرده است (index, value):

>>> list(e)
[(0, 'a'), (1, 'b'), (2, 'c')]

استفاده از این تابع در مواقعی که پیمایش یک دنباله غیر عددی یا بررسی اندیس دنباله حلقه را در نظر داشته باشید بسیار مفید است:

>>> s = 'python'

>>> for i, v in enumerate(s):
...     print('%s) %s' % (i, v * 7))
...
0) ppppppp
1) yyyyyyy
2) ttttttt
3) hhhhhhh
4) ooooooo
5) nnnnnnn
>>>
>>> s = 'python'

>>> [v * i for i, v in enumerate(s)]
['', 'y', 'tt', 'hhh', 'oooo', 'nnnnn']

این تابع همچنین یک آرگومان اختیاری با نام start دارد که با مقدار دادن به آن می‌توان عدد ابتدایی شمارش اندیس‌ها را تعیین نمود؛ مقدار پیش‌فرض این آرگومان عدد صفر است:

>>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']

>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]

>>> list(enumerate(seasons, start=1))
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

شی تکرارکننده

در این بخش قصد داریم با مفهوم iterator (تکرارکننده) در پایتون آشنا شویم. برای این منظور بهتر است ابتدا مفهوم iterable (تکرارپذیر) را بدانیم.

تمام انواع دنباله یک iterable هستند؛ در واقع به اشیایی با این قابلیت که بتوان در هر لحظه یک عضو درون آن را دستیابی نمود iterable گفته می‌شود. اکثر انواع آماده شی که در پایتون می‌شناسیم یک iterable است؛ انواع شی رشته، لیست، تاپل، دیکشنری، range ،zip (یا xrange) یا یک شی فایل (file) و هر شی از کلاسی که خودتان به همراه متد‌های ویژه ()__iter__ یا ()__getitem__ تعریف نمایید یک iterable هستند.

شی iterator با استفاده از تابع آماده ()iter [اسناد پایتون] ایجاد می‌شود؛ این تابع یک شی iterable را به عنوان آرگومان دریافت می‌کند و آن را در قالب یک شی iterator بر می‌گرداند:

>>> L = [1, 2, 3, 4, 5]
>>> type(L)
<class 'list'>

>>> itr = iter(L)

>>> type(itr)
<class 'list_iterator'>
>>> t = (1, 2, 3, 4, 5)
>>> type(t)
<class 'tuple'>

>>> itr = iter(t)

>>> type(itr)
<class 'tuple_iterator'>
>>> s = 'python'
>>> type(s)
<class 'str'>

>>> itr = iter(s)

>>> type(itr)
<class 'str_iterator'>
>>> d = {'name': 'Bob', 'age': 40}
>>> type(d)
<class 'dict'>

>>> itr = iter(d)

>>> type(itr)
<class 'dict_keyiterator'>

یک شی iterator این قابلیت را دارد که می‌توان عضوهای درون آن را یکی یکی با استفاده از متد ()__next__ (یا ()next در پایتون 2x) پیمایش کرد؛ این متد در بار نخستِ فراخوانی عضو یکم شی و در دفعات بعدی فراخوانی به ترتیب عضوهای بعدی را برمی‌گرداند:

>>> L = [1, 2, 3, 4, 5]
>>> itr = iter(L)
>>> # Python 3.x

>>> itr.__next__()
1
>>> itr.__next__()
2
>>> itr.__next__()
3
>>> # Python 2.x

>>> itr.next()
1
>>> itr.next()
2
>>> itr.next()
3

با فراخوانی پی در پی این متد و رسیدن به انتهای پیمایش؛ زمانی که دیگر عضوی برای برگرداندن وجود ندارد یک خطا - البته درست این است که بگوییم یک استثنا (Exception) - با نام StopIteration گزارش می‌گردد:

>>> itr.__next__()
4
>>> itr.__next__()
5
>>> itr.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

این دقیقا همان کاری است که در دستور for به انجام می‌رسد. زمانی که از یک دنباله برای پیمایش در این دستور استفاده می‌کنید؛ for در پشت صحنه آن را به یک iterator تبدیل و سپس پیمایش یک به یک عضوها را آغاز می‌کند. در هر لحظه‌ که StopIteration رخ دهد، متوجه پایان دنباله شده و تکرار حلقه را پایان می‌بخشد.

در آینده توسط درس استثنا‌ها در پایتون خواهید دید که می‌توان با ایجاد iterator و استفاده از دستور try/except [که در همان درس خواهید آموخت] یک حلقه while را به مانند حلقه for پیاده‌سازی کرد.

با استفاده از ماژول itertools می‌توانید iterator های بی‌نهایت (Infinite) یا بدون توقف ایجاد نمایید. برای نمونه تابع cycle درون این ماژول، شی iterator ای می‌سازد که در انتهای پیمایش متوقف نمی‌شود و از نو به ابتدای شی برگشته و عضو یکم را برمی‌گرداند:

>>> import itertools

>>> L = [1, 2, 3, 4, 5]

>>> itr = itertools.cycle(L)

>>> type(itr)
<class 'itertools.cycle'>

>>> itr.__next__()
1
>>> itr.__next__()
2
>>> itr.__next__()
3
>>> itr.__next__()
4
>>> itr.__next__()
5
>>> itr.__next__()
1
>>> itr.__next__()
2

این ماژول شامل تابع‌های کاربردی بسیاری است که برای مطالعه بیشتر می‌توانید به صفحه آن در اسناد پایتون مراجعه نمایید.



😊 امیدوارم مفید بوده باشه

لطفا دیدگاه و سوال‌های مرتبط با این درس خود را در کدرز مطرح نمایید.