پرهیز از خطای تورفتگی

پایتون از تورفتگی برای تعیین ارتباط یک خط یا گروهی از خط‌ها با دیگر قسمت‌های برنامه استفاده می‌کند. در مثال‌های گذشته خط‌هایی که برای هر شعبده‌باز پیامی جداگانه چاپ می‌کرد جزئی از حلقه‌ی for بودند زیرا آن‌ها را با تورفتگی نوشته بودیم. به‌کاررفتنِ تورفتگی در پایتون خواندنِ کد را بسیار ساده می‌کند و اساساً پایتون از فاصله‌ی خالی استفاده می‌کند تا شما را به نوشتن کدهای مرتّب با ساختار بصری واضح وادار کند. در برنامه‌های طولانی‌تر پایتون به قطعه‌های تورفته‌ی کد با سطح‌های تورفتگی‌های متفاوت برخواهید خورد. این سطوح تورفتگی به شما کمک می‌کند تا احساسی کلّی نسبت به سازمان و ساختار کلّی برنامه پیدا کنید.

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

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

فراموش کردن تورفتگی

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

1
2
3
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(magician)

فراخوانی print() در 1 باید تورفته باشد امّا نیست. هنگامی که پایتون به دنبال خطی تورفته باشد امّا آن را نیابد، به شما می‌گوید که کدام خط چنین مشکلی دارد.

1
2
3
4
File "magicians.py", line 3
    print(magician)
    ^
IndentationError: expected an indented block

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

فراموش کردن تورفتگی خط‌های اضافی

گاهی حلقه‌ی شما بدون هیچ خطایی اجرا می‌شود امّا نتیجه‌ی مطلوب را نمی‌گیرید. این گاهی به این دلیل رخ می‌دهد که می‌خواهید چند وظیفه را در یک حلقه انجام دهید امّا فراموش کرده‌اید که برخی از خط‌ها را تورفته کنید.

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

1
2
3
4
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
    print(f"{magician.title()}, that was a great trick!")
print(f"I can't wait to see your next trick, {magician.title()}.\n")

فراخوانی print() در 1 باید با تورفتگی همراه باشد، امّا چون پایتون حداقل یک خطِ تورفته‌ی درست بعد از حلقه‌ی for می‌بیند، خطایی اعلام نمی‌کند. در نتیجه print() اول که خط آن تورفته است برای هر کدام از نام‌ها یک بار اجرا می‌شود. دومین print() را بدون تورفتگی نوشتیم پس تنها یک بار بعد از پایان کار حلقه اجرا می‌شود. از آن‌جایی که آخرین مقداری که به magician نسبت داده شده ‘carolina’ است، او تنها کسی است که پیام ’looking forward to the next trick’ را دریافت می‌کند:

1
2
3
4
Alice, that was a great trick!
David, that was a great trick!
Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.

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

تورفتگی‌های بی‌مورد

اگر به طور تصادفی خطی را تورفته کنید بدون این که نیاز باشد پایتون در مورد آن به شما اطّلاع خواهد داد:

1
2
message = "Hello Python world!"
    print(message)

دستور print() در 1 نیازی به تورفتگی ندارد چون بخشی از حلقه نیست؛ بنابراین پایتون این خطا را گزارش می‌کند:

1
2
3
4
File "hello_world.py", line 2
    print(message)
    ^
IndentationError: unexpected indent

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

تورفتگی بی‌مورد پس از حلقه

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

برای مثال اجازه دهید ببینیم با تورفته کردن خطی که حاوی پیام تشکر به همه‌ی شعبده‌بازها بود، چه اتّفاقی می‌افتد:

1
2
3
4
5
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
    print(f"{magician.title()}, that was a great trick!")
    print(f"I can't wait to see your next trick, {magician.title()}.\n")
    print("Thank you everyone, that was a great magic show!")

چون که خط 1 تورفته است، یک بار برای هر فرد داخل لیست چاپ می‌شود، چنانکه اینجا می‌بینید:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Alice, that was a great trick!
I can't wait to see your next trick, Alice.

Thank you everyone, that was a great magic show!
David, that was a great trick!
I can't wait to see your next trick, David.

Thank you everyone, that was a great magic show!
Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.

Thank you everyone, that was a great magic show!

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

فراموش کردن دونقطه

دونقطه‌ی پایان عبارت for به پایتون می‌گوید که خط بعدی را به عنوان آغاز حلقه در نظر بگیرد.

1
2
3
magicians = ['alice', 'david', 'carolina']
for magician in magicians
print(magician)

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