درس آموزشی درس ۴ از ۵

حلقه‌ها در پایتون

با حلقه‌های for و while یاد بگیر چطور کارهای تکراری رو در پایتون انجام بدی؛ از range و شمارش تا break، continue، حلقه‌های تو در تو و تمرین‌های کاربردی.

مقدماتی ۲۹ دقیقه مطالعه بروزرسانی ۲۳ مهر ۱۴۰۵
فهرست این درس

فرض کن اسامی چند دوره رو توی یه لیست داریم و می‌خوایم همه رو نمایش بدیم. برای سه تا دوره شاید چند بار نوشتن print() راحت به نظر بیاد، ولی اگه تعدادشون به صد تا برسه چی؟ اینجا حلقه در پایتون کمکمون می‌کنه یک دستور رو برای داده‌های مختلف تکرار کنیم، بدون اینکه صد بار کد مشابه بنویسیم. توی این درس قراره بفهمیم هر تکرار چطور انجام می‌شه، فرق for و while چیه و چه زمانی باید جلوی ادامه حلقه رو بگیریم. از یه مثال ساده شروع کنیم:

courses = ["Python", "Linux", "Security"]

for course in courses:
    print(course)

خروجی :

Python
Linux
Security

اینجا فقط یک print() داریم، ولی پایتون برای هر عضو لیست اجراش می‌کنه. وقتی صحبت از حلقه‌های پایتون می‌شه، دو مسیر اصلی جلوی رومونه: یکی حرکت روی آیتم‌ها با for و یکی تکرار تا زمانی که شرط برقرار باشه با while. نمودار ساده بعدی تفاوت این دو رو نشون می‌ده.

for:
Iterable
↓
Item
↓
Body
↓
Next Item

while:
Condition
↓
True
↓
Body
↓
Update
↓
Check Again

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

حلقه در پایتون

حلقه در پایتون دقیقاً یعنی چی؟

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

حلقه در پایتون یعنی اجرای چندباره یک بخش از کد. گاهی می‌خوای تک‌تک آیتم‌های یه مجموعه رو بررسی کنی، گاهی هم لازم داری یک کار تا برقرار بودن شرطی ادامه پیدا کنه. برای حالت اول معمولاً for انتخاب طبیعی‌تریه و برای حالت دوم while. مثلاً «برای هر دانشجو اسمش رو چاپ کن» با for خواناتره، ولی «تا وقتی رمز درست وارد نشده دوباره سؤال کن» به while نزدیک‌تره.

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

آموزش حلقه for در پایتون؛ حرکت روی آیتم‌ها

حلقه for در پایتون روی آیتم‌های یک شیء قابل پیمایش یا Iterable حرکت می‌کنه. برخلاف تصور رایجی که از بعضی زبان‌های دیگه میاد، حتماً لازم نیست یک شمارنده دستی داشته باشی. لیست، رشته، تاپل، دیکشنری و range می‌تونن منبع تکرار باشن. ساختار اصلی رو اینجا ببین:

for item in iterable:
    code_to_run

مثلاً :

languages = ["Python", "Dart", "Ruby"]

for language in languages:
    print(language)

هر بار که حلقه اجرا می‌شه، پایتون یک عضو از languages برمی‌داره و به اسم language نسبت می‌ده. بعد بدنه حلقه اجرا می‌شه و می‌ریم سراغ عضو بعدی. به هر بار اجرای بدنه می‌گیم یک «دور» یا Iteration. لازم نیست از همون اول ساختار داخلی Iteratorها رو بدونی؛ فعلاً همین جریان رو دنبال کن.

Iterable
↓
Get next item
↓
Assign it to loop variable
↓
Run body
↓
Is there another item?
↓
Yes → repeat
No  → finish

متغیر حلقه دقیقاً چیه؟

در این کد :

for course in courses:
    print(course)

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

courses = ["Python", "Linux", "Security"]

for course in courses:
    print(course)

print(course)

آخرین خط مقدار Security رو چاپ می‌کنه. البته نباید طراحی برنامه رو روی استفاده از Variable حلقه بعد از Loop بنا کنی؛ مخصوصاً اگر Iterable خالی باشه، ممکنه اصلاً Assignmentی برای اون اسم اتفاق نیفته.

تغییر Variable حلقه باعث تغییر مسیر for نمیشه

این رفتار خیلی‌ها رو اول کار گیج می‌کنه. فرض کن بنویسی :

for number in range(5):
    print(number)
    number = 100

ممکنه انتظار داشته باشی با نوشتن number = 100 تکرار بعدی از ۱۰۰ شروع بشه، ولی در for آیتم بعدی رو منبع تکرار تعیین می‌کنه. بنابراین ابتدای دور بعد، پایتون مقدار بعدی range(5) رو دوباره به number نسبت می‌ده و روند حلقه طبق همون بازه ادامه پیدا می‌کنه.

0
1
2
3
4

پس Variable حلقه کنترل‌کننده منبع Iteration نیست؛ فقط در هر دور آیتم فعلی رو دریافت می‌کنه. این یکی از تفاوت‌های مهم مدل for در Python با مدل شمارنده‌ای بعضی زبان‌هاست.

حلقه for در پایتون

حلقه for روی String، List و Tuple

برای فهم بهتر حلقه در پایتون خوبه ببینی for فقط برای عددها نیست. روی String می‌تونی کاراکتر به کاراکتر حرکت کنی :

word = "Python"

for character in word:
    print(character)

روی List هم آیتم به آیتم حرکت می‌کنی :

scores = [18, 20, 16]

for score in scores:
    print(score)

و روی Tuple هم همین منطق وجود داره :

position = (35.7, 51.4)

for value in position:
    print(value)

برای همین می‌گیم در بحث حلقه در پایتون، for فقط برای شمارش نیست. همین شکل از تکرار روی رشته، لیست و تاپل کار می‌کنه؛ بعداً روی فایل‌ها و انواع دیگه هم باهاش روبه‌رو می‌شی. وقتی مفهوم «گرفتن آیتم بعدی» رو بفهمی، سراغ هر داده قابل پیمایش که بری، منطق کلی برات آشناست.

آموزش range در پایتون؛ شروع، توقف و قدم

برای شمارش یا تکرار به تعداد مشخص، آموزش range در پایتون به دردت می‌خوره. تابع range() یک شیء بازه‌ای می‌سازه که می‌تونی با for روی عددهاش حرکت کنی؛ قرار نیست حتماً کل عددها رو از اول داخل یک لیست بسازه. ساده‌ترین نمونه اینه:

for number in range(5):
    print(number)

خروجی :

0
1
2
3
4

حواست به این نکته باشه: در range(5) عدد ۵ جزو مقادیر تولیدشده نیست. از صفر شروع می‌کنیم و قبل از پنج متوقف می‌شیم. خیلی از باگ‌های «یک بار کمتر یا بیشتر» دقیقاً از فراموش کردن همین موضوع به وجود میان.

شروع و پایان در range

اگر بخوای نقطه شروع رو هم مشخص کنی، دو Argument میدی :

for number in range(2, 6):
    print(number)

خروجی :

2
3
4
5

یعنی start داخل بازه هست ولی stop داخلش نیست.

استفاده از step در range

مقدار سوم range() اندازه هر قدم رو مشخص می‌کنه:

for number in range(0, 10, 2):
    print(number)

خروجی :

0
2
4
6
8

برای حرکت معکوس هم Step منفی می‌دی :

for number in range(5, 0, -1):
    print(number)

خروجی :

5
4
3
2
1

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

خطای Off-by-one یعنی چی؟

یکی از اشتباه‌های پرتکرار در حلقه‌های پایتون خطای Off-by-one هست؛ یعنی حلقه دقیقاً یک دور کمتر یا بیشتر اجرا بشه. فرض کن می‌خوای اعداد ۱ تا ۵ رو چاپ کنی، ولی بازه رو این‌طور می‌نویسی؛ نتیجه فقط تا ۴ می‌ره:

for number in range(1, 5):
    print(number)

نسخه درست برای ۱ تا ۵ اینه :

for number in range(1, 6):
    print(number)

به‌جای اینکه فقط فرمول حفظ کنی، موقع نوشتن range() از خودت بپرس «آیا عدد انتهایی باید چاپ بشه یا نه؟». در یک حلقه در پایتون برای چاپ ۱ تا ۵ باید پایان رو ۶ بذاری. این فرق کوچیک در شمارنده‌ها، شماره صفحه‌ها و اندیس‌های لیست خیلی مهم می‌شه.

چه زمانی از for و چه زمانی از while استفاده کنیم؟

اگر دنبال آموزش for در پایتون یا آموزش while در پایتون هستی، بهتره هر کدوم رو با مسئله مخصوص خودش تمرین کنی؛ اولی برای حرکت روی آیتم‌ها و دومی برای تکرار بر اساس شرط.

برای انتخاب بین for و while یه سؤال کافیه: «می‌خوام روی اعضای یک مجموعه حرکت کنم یا منتظرم شرطی تغییر کنه؟». هر دو رو می‌شه برای بعضی مسئله‌ها به کار برد، ولی کدی بهتره که قصدت رو روشن‌تر برسونه. جدول زیر یه راهنمای سریع برای شروعه.

مسئلهانتخاب طبیعی‌تردلیل
حرکت روی اعضای List یا Stringforخود آیتم‌ها منبع Iteration هستن
تکرار تعداد مشخصfor + range()تعداد Iteration مشخصه
ادامه تا برقرار بودن یک شرطwhileپایان Loop وابسته به Condition هست
گرفتن ورودی تا رسیدن به مقدار معتبرwhileتعداد تلاش‌ها از قبل معلوم نیست
جستجو داخل Collectionforروی اعضای Collection حرکت می‌کنیم

مثلاً برای چاپ همه اسم‌های یک لیست، ساختن اندیس با while معمولاً کد رو بی‌دلیل طولانی می‌کنه. اما برای دریافت ورودی تا زمانی که کاربر پاسخ معتبر بده، while انتخاب طبیعی‌تریه. این‌ها پیشنهادن، نه قانون تغییرناپذیر.

آموزش حلقه while در پایتون؛ تکرار تا تغییر شرط

حلقه while در پایتون تا زمانی ادامه پیدا می‌کنه که شرطش درست ارزیابی بشه. برخلاف for، اینجا تکرار الزاماً به تعداد اعضای یک مجموعه وابسته نیست؛ ممکنه سه بار اجرا بشه یا صد بار. برای آموزش while در پایتون از مثال شمارنده شروع می‌کنیم:

count = 1

while count <= 5:
    print(count)
    count += 1

خروجی :

1
2
3
4
5

جریانش رو قدم‌به‌قدم ببین: اول بررسی می‌شه count <= 5 درست هست یا نه. اگر درست بود، بدنه اجرا می‌شه و count یکی افزایش پیدا می‌کنه. بعد دوباره شرط بررسی می‌شه. وقتی شمارنده به ۶ برسه، شرط نادرست می‌شه و حلقه تموم می‌شه. پس تغییر وضعیت داخل بدنه، بخش مهمی از این مثال محسوب می‌شه.

Check condition
↓
False → finish
True
↓
Run body
↓
Change state
↓
Check condition again

اون بخش «Change state» خیلی مهمه. اگر چیزی که روی Condition اثر می‌ذاره هیچ‌وقت تغییر نکنه، ممکنه Loop هیچ‌وقت تموم نشه.

حلقه while در پایتون

حلقه بی‌نهایت در پایتون چطور ساخته میشه؟

یکی از اولین Bugهایی که با while می‌بینی Infinite Loop هست. مثلاً :

count = 1

while count <= 5:
    print(count)

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

استفاده درست از while True

دیدن while True به‌خودی‌خود نشونه اشتباه نیست. بعضی وقت‌ها عمداً حلقه رو شروع می‌کنیم و خروج رو به یک تصمیم داخل بدنه می‌سپاریم. مثلاً برنامه می‌تونه تا زمانی که کاربر کلمه exit رو وارد نکرده، دستورهای ساده دریافت کنه:

while True:
    command = input("Command: ")

    if command == "exit":
        break

    print("Running:", command)

اینجا Loop از نظر Condition خودش همیشه True هست، ولی مسیر خروج مشخص و قابل فهمی داره. تفاوت این با Infinite Loop ناخواسته اینه که ما دقیقاً می‌دونیم چه Eventای Loop رو متوقف می‌کنه.

دستور break در پایتون؛ خروج به‌موقع از حلقه

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

numbers = [4, 8, 3, -2, 7, -9]

for number in numbers:
    if number < 0:
        print("Found:", number)
        break

خروجی :

Found: -2

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

دستور continue در پایتون؛ رد کردن یک دور

برعکس break، دستور continue در پایتون حلقه رو کامل متوقف نمی‌کنه. فقط ادامه کدهای همین دور رو کنار می‌ذاره و می‌ره سراغ دور بعدی. مثلاً برای اینکه عددهای زوج رو رد کنیم و فقط فردها رو چاپ کنیم:

for number in range(1, 8):
    if number % 2 == 0:
        continue

    print(number)

خروجی :

1
3
5
7

وقتی عدد زوج باشه، continue اجازه نمی‌ده print() در همون دور اجرا بشه. تفاوت رو ساده به خاطر بسپار: break یعنی «از این حلقه بیا بیرون»، اما continue یعنی «این دور رو رد کن و ادامه بده».

تفاوت break و continue در یک نگاه

دستوراثراجرای Loop بعدش
breakخروج کامل از Loop فعلیمتوقف میشه
continueرد کردن ادامه Iteration فعلیبا Iteration بعدی ادامه پیدا می‌کنه

وقتی حلقه در پایتون می‌نویسی، استفاده زیاد و بی‌قاعده از break و continue می‌تونه دنبال کردن جریان برنامه رو سخت کنه. ولی وقتی نقطه خروج یا Skip واقعاً بخشی از منطق مسئله باشه، هر دو ابزار کاملاً طبیعی و مفیدن.

دستور pass داخل حلقه چه فرقی با continue داره؟

pass و continue شبیه هم نیستن. pass هیچ کاری انجام نمیده و اجرای کد از خط بعدی ادامه پیدا می‌کنه :

for number in range(3):
    if number == 1:
        pass

    print(number)

خروجی :

0
1
2

ولی اگر pass رو با continue عوض کنی، 1 چاپ نمیشه چون ادامه همون Iteration Skip میشه. pass بیشتر وقتی به درد می‌خوره که موقتاً به یک Statement خالی نیاز داری، نه برای کنترل واقعی جریان Loop.

for else و while else در پایتون چه فرقی با if دارن؟

یکی از ویژگی‌های کمتر شناخته‌شده، for else در پایتون و while else در پایتون هست. این else به معنی «وقتی شرط if غلط شد» نیست؛ وقتی اجرا می‌شه که حلقه بدون break به پایان برسه. پس اگه وسط جست‌وجو مورد موردنظر رو پیدا کنیم و break بزنیم، else اجرا نمی‌شه. مثال بعدی رو ببین:

users = ["ali", "sara", "mina"]
target = "reza"

for user in users:
    if user == target:
        print("User found")
        break
else:
    print("User not found")

خروجی :

User not found

چون کاربر موردنظر پیدا نشد، هیچ breakای اجرا نشد و پیام «پیدا نشد» رو دیدیم. این ساختار برای جست‌وجو کاربرد داره، ولی لازم نیست به همه حلقه‌ها else اضافه کنیم. ضمناً اگر اجرای تابع با return یا خطا قطع بشه، نمی‌شه انتظار داشت بخش else بعد از حلقه اجرا بشه.

حلقه تو در تو در پایتون یا Nested Loop

وقتی یک حلقه داخل حلقه دیگه قرار بگیره، بهش Nested Loop یا حلقه تو در تو می‌گیم. مثلاً برای ترکیب هر رنگ با هر اندازه، برای هر عضو لیست اول باید کل لیست دوم رو پیمایش کنیم:

colors = ["red", "blue"]
sizes = ["S", "M", "L"]

for color in colors:
    for size in sizes:
        print(color, size)

خروجی :

red S
red M
red L
blue S
blue M
blue L

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

break در Nested Loop دقیقاً کدوم حلقه رو متوقف می‌کنه؟

این نکته خیلی مهمه. break فقط نزدیک‌ترین Loop رو می‌شکنه :

for row in range(3):
    for column in range(3):
        if column == 1:
            break

        print(row, column)

با رسیدن column به عدد ۱، فقط حلقه داخلی قطع می‌شه و حلقه بیرونی برای ردیف بعدی ادامه پیدا می‌کنه. اگر لازم داری هر دو سطح رو متوقف کنی، بهتره منطق رو داخل تابع جدا قرار بدی تا خروج از تابع واضح باشه؛ بعداً توی درس توابع دقیق‌تر سراغش می‌ریم.

آموزش enumerate در پایتون؛ شماره و مقدار با هم

وقتی هم شماره هر آیتم رو می‌خوای و هم خود آیتم رو، احتمالاً اول وسوسه می‌شی با range(len(...)) جلو بری. کد زیر کار می‌کنه، ولی در خیلی از موقعیت‌ها راه ساده‌تری داریم:

courses = ["Python", "Linux", "Security"]

for index in range(len(courses)):
    print(index, courses[index])

برای این کار enumerate در پایتون دقیقاً به درد می‌خوره. با enumerate() شماره و مقدار رو هم‌زمان دریافت می‌کنی و نیازی نیست دستی اندیس بسازی:

courses = ["Python", "Linux", "Security"]

for index, course in enumerate(courses):
    print(index, course)

اگر هم بخوای شماره‌ها از ۱ شروع بشن :

for index, course in enumerate(courses, start=1):
    print(index, course)

وقتی هدف کدت فقط نمایش آیتم‌ها همراه شماره‌شونه، enumerate() هم خواناتره و هم احتمال خطای اندیس رو کمتر می‌کنه. قرار نیست همیشه کوتاه‌ترین کد رو بنویسیم؛ دنبال کدی هستیم که راحت بشه فهمید و تغییرش داد.

آموزش zip در پایتون؛ حرکت هم‌زمان روی دو مجموعه

برای پیمایش هم‌زمان دو مجموعه مرتبط می‌تونیم سراغ zip در پایتون بریم. مثلاً وقتی لیست عنوان دوره‌ها و قیمت‌ها رو جدا داریم، می‌خوایم هر عنوان کنار قیمت متناظرش بررسی بشه:

courses = ["Python", "Linux", "Security"]
prices = [900, 700, 1200]

for course, price in zip(courses, prices):
    print(course, price)

zip() هر بار یک عضو از هر مجموعه می‌گیره. نکته مهم اینه که به‌صورت پیش‌فرض با تمام شدن کوتاه‌ترین مجموعه متوقف می‌شه؛ یعنی ممکنه بخشی از داده‌ها بدون هشدار کنار گذاشته بشن. اگر برابری طول دو مجموعه برای کارت مهمه، در نسخه‌های جدید پایتون می‌تونی از zip(courses, prices, strict=True) استفاده کنی تا اختلاف طول خطا بده.

Unpacking داخل حلقه for

پایتون اجازه می‌ده اعضای چندبخشی رو موقع پیمایش مستقیم باز کنیم؛ به این کار Unpacking می‌گن. وقتی هر آیتم خودش دو مقدار داره، می‌تونیم به‌جای دسترسی با اندیس، همون ابتدا هر بخش رو به اسم مشخصی نسبت بدیم:

courses = [
    ("Python", 342),
    ("Linux", 120),
    ("Security", 280),
]

for title, videos in courses:
    print(title, videos)

اینجا هر عضو لیست یک تاپل دوعضویه و دو اسم title و videos هم‌زمان مقدار می‌گیرن. این روش از item[0] و item[1] خواناتره، البته باید مطمئن باشیم ساختار همه آیتم‌ها با تعداد اسم‌ها جور درمیاد.

حلقه روی Dictionary چطور کار می‌کنه؟

اگر مستقیم روی Dictionary for بزنی، به‌صورت پیش‌فرض Keyها رو می‌گیری :

course = {
    "title": "Python",
    "videos": 342,
    "published": True,
}

for key in course:
    print(key)

اگر هم Key و Value رو با هم می‌خوای، items() انتخاب طبیعی‌تریه :

for key, value in course.items():
    print(key, value)

برای فقط Valueها هم values() وجود داره. موقع استفاده از حلقه در پایتون روی Dictionary اول مشخص کن واقعاً Key می‌خوای، Value می‌خوای یا هر دو؛ بعد Method مناسب رو انتخاب کن تا Intent کد واضح بمونه.

حین Loop روی یک Collection همون Collection رو تغییر نده مگر دقیقاً بدونی داری چی کار می‌کنی

یکی از اشتباه‌های مهم در کار با loops در پایتون اینه که هم‌زمان با پیمایش یک مجموعه، ساختار همون مجموعه رو تغییر بدیم. مثلاً پاک کردن کلیدهای دیکشنری حین حرکت مستقیم روی اون می‌تونه خطا بده. برای همین توی مثال بعدی روی کپی داده‌ها حرکت می‌کنیم و تغییر رو روی نسخه اصلی انجام می‌دیم.

users = {
    "ali": "active",
    "sara": "inactive",
    "mina": "active",
}

for username, status in users.copy().items():
    if status == "inactive":
        del users[username]

print(users)

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

range در پایتون

تکرار مستقیم روی آیتم بهتره یا استفاده از Index؟

اگر فقط خود Value رو لازم داری، مستقیم روی Value حرکت کن :

courses = ["Python", "Linux", "Security"]

for course in courses:
    print(course)

این مدل معمولاً بهتر از اینه :

for index in range(len(courses)):
    print(courses[index])

نسخه دوم غلط نیست؛ توی حلقه for در پایتون وقتی Index هیچ کاربردی نداره یک لایه اضافی به ذهن خواننده تحمیل می‌کنه. در حلقه در پایتون یکی از بهترین عادت‌ها اینه که ساده‌ترین شکل Iteration متناسب با مسئله رو انتخاب کنی.

وقتی فقط تعداد تکرار مهمه از underscore استفاده کنیم؟

بعضی وقت‌ها فقط می‌خوای کاری چند بار انجام بشه و خود شمارنده اهمیتی نداره. توی پایتون رسمه چنین متغیر بدون استفاده‌ای رو با _ نشون بدیم. این صرفاً یک قرارداد برای خواناتر شدن کده:

for _ in range(3):
    print("Retrying...")

خود _ همچنان یک اسم معمولیه؛ پایتون رفتار ویژه‌ای فقط به خاطر این اسم بهش نمی‌ده. ما ازش استفاده می‌کنیم تا بقیه بدونن مقدار هر دور برای منطق برنامه مهم نیست.

ترکیب شرط و حلقه در پایتون

حلقه‌ها وقتی کنار if قرار می‌گیرن، کلی مسئله کاربردی رو حل می‌کنن. مثلاً می‌تونیم روی فهرست نمره‌ها حرکت کنیم و فقط اون‌هایی رو نمایش بدیم که حداقل نمره قبولی دارن:

scores = [8, 14, 19, 7, 12]

for score in scores:
    if score >= 10:
        print(score)

در این مثال for روی نمره‌ها حرکت می‌کنه و if تصمیم می‌گیره کدوم نمره چاپ بشه. اگه این بخش برات مبهمه، قبل از ادامه درس شرط‌ها در پایتون رو مرور کن؛ چون ترکیب شرط و حلقه یکی از پایه‌های حل مسئله است.

یک مثال واقعی‌تر؛ پیدا کردن اولین دوره با ظرفیت خالی

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

courses = [
    {"title": "Python", "seats": 0},
    {"title": "Linux", "seats": 3},
    {"title": "Security", "seats": 5},
]

for course in courses:
    if course["seats"] > 0:
        print("Available:", course["title"])
        break

خروجی :

Available: Linux

این نمونه از حلقه در پایتون چند مفهوم رو با هم ترکیب می‌کنه: Iteration، Dictionary، Condition و break. نکته مهم اینه که بعد از پیدا کردن جواب دیگه ادامه دادن Loop ارزش نداره، پس break دقیقاً با Intent مسئله هماهنگه.

یک مثال واقعی با while؛ اعتبارسنجی ورودی کاربر

فرض کن کاربر باید عددی بین ۱ تا ۵ وارد کنه و تا وقتی ورودی درست نباشه برنامه دوباره سؤال بپرسه. چون نمی‌دونیم چند بار تلاش می‌کنه، while مناسب‌تره. مثال زیر برای ورودی‌های عددی نوشته شده:

while True:
    score = int(input("Score from 1 to 5: "))

    if 1 <= score <= 5:
        break

    print("Invalid score")

print("Saved:", score)

وقتی عدد داخل بازه باشه، break اجرا می‌شه و حلقه تموم می‌شه. اما اگر کاربر به‌جای عدد متن وارد کنه، تبدیل int() خطا می‌ده؛ پس این مثال هنوز اعتبارسنجی کامل ورودی نیست. در درس خطاها می‌تونیم سراغ try/except بریم تا چنین ورودی‌هایی رو هم مدیریت کنیم.

Zero-trip Loop؛ حلقه‌ای که حتی یک بار هم اجرا نمیشه

همیشه قرار نیست Loop حداقل یک بار اجرا بشه. مثلاً :

count = 10

while count < 5:
    print(count)

چون Condition از همون ابتدا False هست، بدنه حتی یک بار هم اجرا نمیشه. for هم اگر Iterable خالی باشه همین وضعیت رو داره :

courses = []

for course in courses:
    print(course)

هیچ خروجی‌ای نمی‌بینی. این نکته توی حلقه در پایتون وقتی انتظار داری متغیری ساخته بشه یا Side Effect مهمی اتفاق بیفته، خیلی مهم میشه. قبل از وابسته شدن به نتیجه Loop، حالت «صفر بار اجرا شدن» رو هم در ذهنت بررسی کن.

۷ اشتباه رایج در حلقه‌های پایتون

اشتباه اول : فراموش کردن Update در while

اگه متغیری که شرط while بهش وابسته‌ست تغییر نکنه، ممکنه برنامه وارد تکرار بی‌پایان بشه. قبل از اجرا مشخص کن کدوم خط قراره شرط رو به وضعیت توقف برسونه.

اشتباه دوم : انتظار داشتن stop داخل range

در range(start, stop) مقدار stop تولید نمیشه. پس range(1, 5) اعداد ۱ تا ۴ رو میده، نه ۱ تا ۵. این یکی از اصلی‌ترین منابع Off-by-one Error هست.

اشتباه سوم : استفاده بی‌دلیل از range(len())

اگر فقط Value لازم داری، مستقیم روی Collection حرکت کن. range(len(...)) زمانی معنی‌دارتره که واقعاً Index لازم داشته باشی؛ حتی اون موقع هم enumerate() خیلی وقت‌ها خواناتر میشه.

اشتباه چهارم : تغییر Collection حین Iteration

حذف یا اضافه کردن عضو به همون Collectionی که داری روش Iteration می‌کنی می‌تونه کد رو پیچیده و Buggy کنه. در چنین شرایطی معمولاً روی Copy حرکت کن یا نتیجه جدید بساز.

اشتباه پنجم : فکر کردن break همه Nested Loopها رو می‌بنده

break فقط داخلی‌ترین Loop فعلی رو متوقف می‌کنه. اگر چند Loop تو در تو داری، دقیقاً مشخص کن کدوم سطح باید متوقف بشه.

اشتباه ششم : اشتباه گرفتن continue با pass

continue ادامه Iteration فعلی رو Skip می‌کنه، ولی pass هیچ کاری نمی‌کنه و خط بعدی همچنان اجرا میشه. این دو جایگزین هم نیستن.

اشتباه هفتم : Loop بی‌دلیل وقتی ابزار مستقیم‌تری وجود داره

قرار نیست هر مسئله‌ای رو حتماً با حلقه حل کنیم. مثلاً اگه فقط مجموع چند عدد رو می‌خوای، معمولاً sum() واضح‌تر از نوشتن حلقه و جمع کردن دستی تک‌تک عددهاست. مهم اینه که ابزار مناسبی برای مسئله انتخاب کنی، نه اینکه تعداد حلقه‌های برنامه بیشتر بشه.

قبل از Run خروجی رو حدس بزن

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

تمرین اول

for number in range(1, 4):
    print(number)

خروجی :

1
2
3

عدد ۴ چاپ نمیشه چون Stop مربوط به range() داخل خروجی نیست.

تمرین دوم

for number in range(5):
    if number == 2:
        continue

    print(number)

خروجی :

0
1
3
4

وقتی number برابر ۲ میشه، continue باقی بدنه همون Iteration رو رد می‌کنه.

تمرین سوم

for number in range(5):
    if number == 2:
        break

    print(number)

خروجی :

0
1

اینجا break کل Loop رو متوقف می‌کنه.

تمرین چهارم

count = 3

while count > 0:
    print(count)
    count -= 1
else:
    print("Done")

خروجی :

3
2
1
Done

حلقه بدون break تموم شده، بنابراین else اجرا میشه.

تمرین پنجم

for i in range(3):
    i = 100
    print(i)

خروجی :

100
100
100

داخل هر Iteration خودت i رو به ۱۰۰ تغییر دادی و همون چاپ شده، ولی در شروع دور بعدی Python آیتم بعدی range رو دوباره به i Assign می‌کنه. تغییر i منبع Iteration رو عوض نمی‌کنه.

تمرین ششم

items = []

for item in items:
    print("A")
else:
    print("B")

خروجی :

B

چون مجموعه قابل پیمایش خالیه، بدنه for اجرا نمیشه؛ ولی چون break اتفاق نیفتاده، else اجرا میشه.

۶ تمرین حلقه در پایتون؛ خودت کد بزن

تمرین ۱ : جمع عددهای زوج

یک List از عددها بساز و با Loop فقط عددهای زوج رو جمع کن. در پایان مجموع رو چاپ کن. بعد همین مسئله رو یک بار دیگه حل کن و ببین آیا استفاده از sum() همراه با یک روش مناسب می‌تونه Intent رو واضح‌تر کنه یا نه.

تمرین ۲ : پیدا کردن اولین نمره مردودی

از داخل یک List نمره‌ها، اولین نمره کمتر از ۱۰ رو پیدا کن و بعد Loop رو متوقف کن. اگر هیچ نمره مردودی وجود نداشت، با for ... else پیام مناسب نمایش بده.

تمرین ۳ : تلاش محدود برای ورود

با while سیستمی بنویس که کاربر حداکثر سه بار فرصت وارد کردن رمز داشته باشه. اگر رمز درست بود Loop همون لحظه با break متوقف بشه و اگر سه تلاش ناموفق تموم شد پیام جداگانه نمایش بده.

تمرین ۴ : جدول ضرب کوچک

با Nested Loop جدول ضرب ۱ تا ۵ رو تولید کن. قبل از Run حساب کن بدنه داخلی در مجموع چند بار اجرا میشه و دلیلش رو توضیح بده.

تمرین ۵ : شماره‌گذاری دوره‌ها

یک List از اسم دوره‌ها بساز و با enumerate(..., start=1) هر دوره رو همراه شماره انسانی از ۱ چاپ کن. این تمرین رو یک بار هم با range(len(...)) بنویس و مقایسه کن کدوم نسخه Intent رو بهتر نشون میده.

تمرین ۶ : پاک‌سازی امن Dictionary

یک Dictionary از نام کاربر و وضعیت active/inactive بساز. کاربرهای inactive رو بدون اینکه مستقیم روی Dictionary اصلی در حال تغییرش باشی حذف کن. یک بار روی Copy حرکت کن و یک بار Dictionary جدیدی فقط از Activeها بساز.

برای هر مسئله چه مدل Loopی انتخاب کنیم؟

اگر نمی‌دونی کِی از for استفاده کنی و کِی از while، مسئله رو با زبان ساده توضیح بده. عبارت «برای هر آیتم این کار رو بکن» معمولاً به for اشاره داره؛ عبارت «تا وقتی این وضعیت ادامه داره تکرار کن» بیشتر به while. برای شمارش می‌تونی سراغ range() بری؛ برای شماره و مقدار با هم، enumerate() رو بشناس؛ و وقتی یک جواب پیدا شد، در صورت نیاز از break استفاده کن.

سؤالات متداول درباره حلقه در پایتون

حلقه در پایتون چیست؟

حلقه در پایتون ساختاری برای اجرای تکراری یک Block از کده. for معمولاً روی آیتم‌های یک Iterable حرکت می‌کنه و while تا زمانی ادامه پیدا می‌کنه که یک Condition از نظر Truth Value درست باشه.

تفاوت for و while در پایتون چیه؟

در for معمولاً منبع Iteration مشخصه و Python آیتم‌ها رو یکی‌یکی می‌گیره. در while ادامه تکرار به یک Condition وابسته‌ست. وقتی روی Collection حرکت می‌کنی for معمولاً خواناتر و وقتی تعداد تکرار از قبل معلوم نیست while معمولاً طبیعی‌تره.

range در پایتون چه کاری می‌کنه؟

تابع range() یک دنباله عددی تولید می‌کنه که برای Iteration خیلی کاربردیه. می‌تونی Start، Stop و Step رو مشخص کنی و باید یادت باشه مقدار Stop داخل دنباله قرار نمی‌گیره.

فرق break و continue چیه؟

دستور break Loop فعلی رو کامل متوقف می‌کنه، ولی continue فقط ادامه Iteration جاری رو Skip می‌کنه و اجرای Loop از دور بعدی ادامه پیدا می‌کنه.

آیا for در Python فقط برای عددهاست؟

نه. for روی Iterable حرکت می‌کنه، بنابراین می‌تونی روی String، List، Tuple، Dictionary، range() و خیلی Objectهای Iterable دیگه Loop بزنی. این یکی از مهم‌ترین تفاوت‌های مدل for در Python با تصور صرفاً شمارنده‌ایه.

چرا while من متوقف نمیشه؟

معمولاً چون Condition همیشه Truthy باقی مونده یا Stateای که باید Condition رو تغییر بده داخل Loop Update نمیشه. مقدارهای مربوط به شرط رو در هر Iteration بررسی کن و نقطه خروج رو واضح مشخص کن.

آیا حلقه for و while می‌تونن else داشته باشن؟

بله. else مربوط به Loop وقتی اجرا میشه که Loop بدون break تموم بشه. در for یعنی Iterable به انتها رسیده و در while یعنی Condition False شده. اگر break اجرا بشه، بخش else اجرا نمیشه.

برای گرفتن Index داخل for از چی استفاده کنیم؟

اگر هم Index و هم Value رو لازم داری، enumerate() معمولاً از range(len(...)) خواناتر و Pythonicتره. مثلاً for index, item in enumerate(items): هر دو مقدار رو مستقیم در اختیارت می‌ذاره.

برای مطالعه بیشتر از مستندات رسمی Python

اگه خواستی رفتار دستورها رو از منبع اصلی بررسی کنی، بخش کنترل جریان در مستندات رسمی پایتون توضیح خوبی درباره for، range() و break داره. برای جزئیات دقیق‌تر ساختارها هم مرجع دستورهای ترکیبی رو ببین. توضیح رفتار zip() و گزینه strict=True هم در مستندات رسمی موجوده. این لینک‌ها صرفاً برای مطالعه عمیق‌ترن و نیازی نیست برای شروع همه‌شون رو بخونی.

جمع‌بندی

اگر بخوای کل مبحث حلقه در پایتون رو توی دو مدل جمع کنی، برای for این مسیر رو یادت نگه دار :

Iterable
↓
Item
↓
Body
↓
Next Item
↓
Finish

و برای while این مدل رو :

Condition
↓
True → Body → Update → Check Again
False → Finish

توی این درس دیدیم که حلقه در پایتون فقط تکرار کردن چند خط نیست. با for روی آیتم‌ها حرکت کردیم، با while تکرار رو به شرط وابسته کردیم و با range() شمارش انجام دادیم. بعد هم break، continue، else حلقه، حلقه‌های تو در تو و ابزارهایی مثل enumerate() و zip() رو بررسی کردیم. مهم‌تر از همه یاد گرفتیم قبل از نوشتن حلقه مشخص کنیم چه چیزی قراره تکرار بشه و کِی باید متوقف بشه.

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

می‌خواهی عمیق‌تر جلو بروی؟

این درس فقط یک بخش از مسیر کامل است

اگر می‌خواهی مباحث را به‌ترتیب، همراه تمرین و پروژه دنبال کنی، مسیر کامل دوره را ببین.

دوره پایتون از صفر