دوره جامع آموزش پایتون

هر آنچه برای تبدیل شدن به یک برنامه‌نویس حرفه‌ای پایتون از سطح صفر نیاز دارید، در این دوره گردآوری شده است.

دسترسی کاملا رایگان

این دوره آموزشی به صورت کامل و برای همیشه رایگان است.

معرفی زبان پایتون

یک زبان شیءگرا، مفسری و سطح بالای برنامه‌نویسی است.

خالق و تاریخچه

توسط "خیدو فان روسوم" در سال ۱۹۹۱ برای اولین بار منتشر شد.

وب‌سایت رسمی

مرجع اصلی مستندات و دانلود: python.org

چندسکویی و جهانی

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

کاربردهای کلیدی

از هوش مصنوعی و علم داده تا توسعه وب، اتوماسیون و اسکریپت‌نویسی.

این دوره برای چه کسانی مناسب است؟


این دوره به گونه‌ای طراحی شده که همه افراد، از مبتدی تا حرفه‌ای، بتوانند از آن بهره‌مند شوند.

مخاطبین دوره
  • افراد کاملا مبتدی و بدون دانش برنامه‌نویسی
  • دانشجویان رشته‌های کامپیوتر و مهندسی
  • علاقه‌مندان به ورود به حوزه هوش مصنوعی و علم داده
  • توسعه‌دهندگان وب (Back-End)
  • افرادی که به دنبال اتوماسیون کارهای روزمره هستند
پیش‌نیازها و محدودیت‌ها
  • پیش‌نیاز علمی: این دوره از صفر مطلق شروع می‌شود و به هیچ دانش برنامه‌نویسی قبلی نیاز ندارد.
  • پیش‌نیاز سیستمی: تنها یک کامپیوتر (ویندوز، مک یا لینوکس) و دسترسی به اینترنت کافی است.
  • محدودیت سنی: این دوره برای نوجوانان (۱۴ سال به بالا) تا بزرگسالان کاملا مناسب است و هیچ محدودیتی ندارد.

سرفصل‌های دوره جامع پایتون

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

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

پایتون یک زبان برنامه‌نویسی سطح بالا، شیءگرا و مفسری است. "مفسری بودن" به این معناست که کدهای شما به صورت خط به خط توسط یک برنامه به نام مفسر (Interpreter) خوانده و اجرا می‌شوند. این فرآیند برخلاف زبان‌های کامپایلری است که کل کد ابتدا باید به زبان ماشین تبدیل شود.

روند اجرای یک کد پایتون به شکل زیر است:

اجرای نهایی کد
مفسر (Interpreter)

کامپایلر: کد شما را به یک زبان میانی به نام بایت‌کد تبدیل می‌کند.

بایت‌کد (Bytecode)

ماشین مجازی (PVM): بایت‌کد را اجرا کرده و از ماژول‌ها و کتابخانه‌ها استفاده می‌کند.

کد منبع (Source Code)
نکته کلیدی: به لطف این فرآیند، کدی که شما می‌نویسید روی هر سیستم‌عاملی (ویندوز، مک، لینوکس) که پایتون روی آن نصب باشد، بدون هیچ تغییری اجرا می‌شود.

غول‌های تکنولوژی که از پایتون استفاده می‌کنند

  • گوگل: توسعه ابزارها، تست، تحلیل داده و هوش مصنوعی.
  • اینستاگرام: ستون فقرات سرورهای بک‌اند و پردازش تصویر.
  • اسپاتیفای: تحلیل داده و الگوریتم‌های پیشنهاد موسیقی.
  • نتفلیکس: تحلیل داده‌های بزرگ و اتوماسیون فرآیندها.
  • دراپ‌باکس: توسعه سرور و کلاینت دسکتاپ.
  • اوبر (Uber): تحلیل داده، الگوریتم‌های مسیریابی و قیمت‌گذاری.
  • ناسا (NASA): تحلیل داده‌های فضایی و پردازش تصویر.
  • فیسبوک (متا): مدیریت زیرساخت و هوش مصنوعی.
  • آمازون: اتوماسیون و تحلیل داده در سرویس‌های AWS.
  • ردیت (Reddit): توسعه بخش‌های اصلی وب‌سایت.
  • کورا (Quora): توسعه وب و مدیریت داده.
  • یاهو: تحلیل داده و پردازش تصویر.
  • ویکی‌پدیا: اسکریپت‌نویسی و ویرایش خودکار مقالات.
  • بیت‌تورنت: توسعه کلاینت‌ها و سرورها.

سیستم نسخه‌بندی پایتون (Semantic Versioning)

نسخه‌های نرم‌افزارها، از جمله پایتون، معمولا از یک استاندارد سه‌بخشی پیروی می‌کنند: X.Y.Z. هر عدد معنای خاصی دارد:

X
Major (نسخه اصلی)

با تغییرات بسیار بزرگ و معماری که ممکن است کدهای قدیمی را از کار بیندازد، این عدد افزایش می‌یابد. (مثلا جهش از پایتون 2 به 3)

Y
Minor (نسخه فرعی)

با اضافه شدن ویژگی‌های جدید که سازگاری را بر هم نمی‌زنند، این عدد افزایش می‌یابد. (مثلا اضافه شدن f-string در 3.6)

Z
Patch (وصله)

با رفع اشکالات (باگ‌ها) و بدون افزودن ویژگی جدید، این عدد افزایش می‌یابد.

این روش در بسیاری از نرم‌افزارهای دیگر نیز رایج است، مانند: اندروید (14.0.0)، iOS (17.5.1) و هزاران کتابخانه برنامه‌نویسی دیگر.

تاریخچه نسخه‌های کلیدی پایتون

نسخه تاریخ انتشار پایان پشتیبانی ویژگی‌های کلیدی افزوده شده
0.9.0 فوریه 1991 - اولین انتشار با ماژول‌ها، توابع و ساختار اصلی.
2.0 اکتبر 2000 2010 List comprehensions، مدیریت حافظه خودکار (Garbage Collection).
2.7 جولای 2010 ژانویه 2020 آخرین نسخه پایتون 2، بهینه‌سازی‌های فراوان.
3.0 دسامبر 2008 فوریه 2014 تغییرات بزرگ و شکستن سازگاری با پایتون 2، تابع شدن print.
3.5 سپتامبر 2015 سپتامبر 2020 اضافه شدن سینتکس async و await برای برنامه‌نویسی غیرهمزمان.
3.6 دسامبر 2016 دسامبر 2021 معرفی f-strings برای فرمت‌بندی آسان رشته‌ها.
3.8 اکتبر 2019 اکتبر 2024 معرفی عملگر Walrus (:=).
3.12 اکتبر 2023 در حال پشتیبانی بهبودهای چشمگیر در عملکرد و پیام‌های خطای دقیق‌تر.

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

  • هوش مصنوعی و یادگیری ماشین
  • علم داده (Data Science)
  • تحلیل داده‌های بزرگ (Big Data)
  • توسعه وب (سمت سرور)
  • اینترنت اشیاء (IoT)
  • اتوماسیون و اسکریپت‌نویسی
  • رباتیک و سیستم‌های کنترلی
  • توسعه بازی
  • محاسبات علمی و مهندسی
  • امنیت و تست نفوذ (هک)
  • پردازش تصویر و بینایی ماشین
  • امور مالی و تحلیل بازار (FinTech)
  • طراحی رابط‌های کاربری گرافیکی (GUI)
  • تست نرم‌افزار
  • و انجام کارهای روزمره!

دریافت فایل نصبی پایتون

برای نصب پایتون، ابتدا باید فایل نصبی آن را دانلود کنیم. در حالت عادی، بهترین و امن‌ترین منبع، وب‌سایت رسمی پایتون به آدرس python.org است.

سایت رسمی پایتون تصویری از صفحه دانلود سایت رسمی پایتون
لینک جایگزین: با توجه به شرایط فعلی اینترنت (فروردین ۱۴۰۵)، اگر در دسترسی به سایت رسمی پایتون مشکل دارید، می‌توانید از وب‌سایت‌های معتبر داخلی مانند Soft98 برای دانلود آخرین نسخه استفاده کنید.
انتخاب نسخه مناسب:

پایتون برای سیستم‌عامل‌های مختلفی عرضه می‌شود. شما باید نسخه مناسب با سیستم‌عامل و معماری آن (۳۲ یا ۶۴ بیتی) را دانلود کنید.

چگونه نوع سیستم‌عامل را تشخیص دهیم؟ روی This PC (یا My Computer) کلیک راست کرده و Properties را انتخاب کنید. در قسمت System type، می‌توانید ۳۲ یا ۶۴ بیتی بودن سیستم خود را مشاهده کنید.

مراحل نصب گام‌به‌گام

پس از دانلود فایل نصبی (مثلا Python.3.14.3.x64.exe)، آن را اجرا کنید. برای نصب صحیح، مراحل زیر را با دقت دنبال کنید:

مهم‌ترین مرحله!

در اولین پنجره نصب، حتماً و حتماً تیک گزینه "Add python.exe to PATH" را در پایین صفحه فعال کنید. فراموش کردن این مرحله باعث می‌شود نتوانید به سادگی از پایتون در محیط CMD یا PowerShell استفاده کنید.

  1. انتخاب نصب سفارشی
    پس از فعال کردن تیک `Add to PATH`، روی گزینه Customize installation کلیک کنید.
  2. پنجره Optional Features
    در این مرحله، مطمئن شوید تمام تیک‌ها فعال هستند و روی Next کلیک کنید.
  3. پنجره Advanced Options
    در این پنجره، دو تیک مهم را فعال کنید:
    • Install Python 3.14 for all users: پایتون را برای تمام کاربران سیستم نصب می‌کند.
    • Precompile standard library: سرعت اجرای اولیه برنامه‌ها را افزایش می‌دهد.
  4. شروع نصب
    روی دکمه Install کلیک کرده و منتظر بمانید تا فرآیند نصب به پایان برسد.
  5. پایان موفقیت‌آمیز نصب
    پس از مشاهده پیام Setup was successful، روی دکمه Close کلیک کنید. تبریک! پایتون با موفقیت نصب شد.

تست نصب و اجرای اولین کد

برای اطمینان از نصب صحیح پایتون، منوی استارت را باز کرده، cmd را تایپ کنید و Command Prompt را اجرا نمایید. سپس دستور زیر را وارد کرده و Enter را بزنید:

python

اگر با خروجی مشابه زیر مواجه شدید، یعنی پایتون به درستی نصب و به PATH سیستم اضافه شده است:

PS C:\Users\MHS> python
Python 3.14.3 (tags/v3.14.3:b61e9d3, Mar 20 2026, 14:08:24) [MSC v.1942 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

آشنایی با IDLE و نوشتن اولین برنامه

پایتون به همراه خود یک ویرایشگر ساده و محیط یادگیری یکپارچه به نام IDLE (مخفف Integrated Development and Learning Environment) نصب می‌کند که برای شروع عالی است.

  1. در منوی استارت، IDLE را جستجو و برنامه IDLE (Python 3.14 64-bit) را اجرا کنید.
  2. از منوی بالا، روی File و سپس New File کلیک کنید تا یک پنجره ویرایشگر جدید باز شود.
  3. کد زیر را در پنجره جدید تایپ کنید:
print("Hello MHS")
  1. از منوی Run، گزینه Run Module را انتخاب کنید یا کلید F5 را فشار دهید.
  2. پنجره‌ای برای ذخیره فایل باز می‌شود. فایل را با نام دلخواه (مثلا `hello.py`) در مسیر مورد نظر خود ذخیره کنید.
  3. بلافاصله پس از ذخیره، کد شما اجرا شده و خروجی آن را در پنجره اصلی IDLE مشاهده خواهید کرد:
Hello MHS
امتحان کنید: عبارت "MHS" (مخفف محمد حسین سلیمانی) را به نام خودتان تغییر دهید و برنامه را مجدداً اجرا کنید تا خروجی جدید را ببینید!

متغیرها چه هستند؟

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

برای قرار دادن یک مقدار درون متغیر (که به آن **تخصیص یا "Assign"** می‌گویند) از علامت مساوی = استفاده می‌کنیم.

قوانین نام‌گذاری متغیرها در پایتون

برای نام‌گذاری متغیرها باید از قوانینی پیروی کنیم. در ادامه، حالت‌های صحیح و ناصحیح را مشاهده می‌کنید:

✅ حالت‌های مجاز برای تعریف متغیر:
myvar = "mhs"
my_var = "mhs"
_my_var = "mhs"
myVar = "mhs"
MYVAR = "mhs"
myvar2 = "mhs"
❌ حالت‌های غیرمجاز برای تعریف متغیر:
2myvar = "mhs"   # شروع با عدد مجاز نیست
my-var = "mhs"   # استفاده از خط تیره (-) مجاز نیست
my var = "mhs"   # استفاده از فاصله (space) مجاز نیست
نکته کلیدی: اگر فقط "نبایدها" را یاد بگیرید (شروع با عدد، استفاده از خط تیره و فاصله)، به صورت خودکار تمام "بایدها" را یاد گرفته‌اید!

استانداردهای نام‌گذاری (دست خط برنامه‌نویس)

برای اینکه کد شما خوانا و حرفه‌ای باشد، بهتر است از یک استاندارد نوشتاری یکسان پیروی کنید. سه روش رایج عبارتند از:

  • Pascal Case (روش پاسکال)

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

    MyVariableName = "MHS"
  • Camel Case (روش شتری)

    حرف اول کلمه اول کوچک و حرف اول کلمات بعدی بزرگ است.

    myVariableName = "MHS"
  • Snake Case (روش ماری)

    تمام حروف کوچک هستند و کلمات با آندرلاین _ از هم جدا می‌شوند.

    my_variable_name = "MHS"

توصیه مدرس: برای شروع، پیشنهاد می‌شود از روش Pascal Case استفاده کنید چون خوانایی بالایی دارد. "دست خط" کدنویسی شما نشان‌دهنده حرفه‌ای بودن و توجه شما به استانداردهاست و در آینده هنگام ارسال رزومه برای کارفرما، تأثیر بسیار مثبتی خواهد داشت.

انواع داده اصلی در پایتون

هر متغیر در پایتون یک نوع داده مشخص دارد. در جدول زیر برخی از مهم‌ترین انواع داده را می‌بینید:

مثال کد نوع داده (Data Type)
x = "Hello World" str (رشته)
x = 20 int (عدد صحیح)
x = 20.5 float (عدد اعشاری)
x = True bool (بولین: صحیح/غلط)
x = None NoneType (تهی)
x = range(6) range (محدوده)
x = ["mhs", 30, True] list (لیست)
x = ("mhs", 30, True) tuple (تاپل)
x = {"mhs", 30, True} set (مجموعه)
x = {"name": "mhs", "age": 30} dict (دیکشنری)

کالکشن‌ها (Collections) در پایتون

چهار نوع داده اصلی برای نگهداری مجموعه‌ای از داده‌ها وجود دارد که هرکدام ویژگی‌های خاص خود را دارند:

نوع داده مرتب (Ordered) قابل تغییر (Mutable) عضو تکراری کاربرد اصلی
List مجموعه‌های عمومی و انعطاف‌پذیر
Tuple داده‌های فقط-خواندنی (Read-only)
Set حذف اعضای تکراری و تست عضویت سریع
Dict (برای کلیدها) نگهداری داده به صورت زوج کلید-مقدار

کار با رشته‌ها (Strings)

الحاق رشته‌ها (Concatenation)

با استفاده از عملگر + یا f-string ها می‌توان رشته‌ها را به هم چسباند:

# Example 1: Using the + operator
a = "Hello"
b = "World"
c = a + " " + b
print(c)

# Example 2: Using f-strings (Modern way)
c_modern = f"{a} {b}" 
print(c_modern)
Hello World
Hello World

برش رشته‌ها (Slicing)

می‌توان بخشی از یک رشته را با مشخص کردن اندیس شروع و پایان جدا کرد:

name = "Mohammad Hossein Soleymani"
print(name[3:12])   # از اندیس 3 تا قبل از 12
print(name[:9])     # از ابتدای رشته تا قبل از 9
print(name[6:])     # از اندیس 6 تا انتهای رشته
print(name[-12:-3]) # برش از انتها
ammad Hos
Mohammad 
ad Hossein Soleymani
n Soleyma

متدهای کاربردی رشته

متدها، توابعی هستند که روی یک متغیر اجرا می‌شوند و عملیات خاصی انجام می‌دهند:

name = "Mohammad Hossein, Soleymani"
print(name.upper())         # تبدیل به حروف بزرگ
print(name.lower())         # تبدیل به حروف کوچک
print(name.strip())         # حذف فاصله‌های خالی از ابتدا و انتها
print(name.replace("H", "G")) # جایگزینی یک کاراکتر با کاراکتر دیگر
print(name.split(","))      # تقسیم رشته بر اساس یک جداکننده
'MOHAMMAD HOSSEIN, SOLEYMANI'
'mohammad hossein, soleymani'
'Mohammad Hossein, Soleymani'
'MoGammad Gossein, Soleymani'
['Mohammad Hossein', ' Soleymani']

کاراکترهای گریز (Escape Characters)

کاراکترهایی که با بک‌اسلش \ شروع می‌شوند و معنای خاصی در رشته‌ها دارند:

کد نتیجه مثال خروجی
\' Single Quote print('It\'s a nice day!') It's a nice day!
\\ Backslash print('Path: C:\\Users') Path: C:\Users
\n New Line print('Line1\nLine2') Line1
Line2
\t Tab print('Col1\tCol2') Col1    Col2
\b Backspace print('Hello\bW') HellW

کار با لیست‌ها (Lists)

لیست‌ها می‌توانند شامل انواع داده‌های مختلف باشند.

# Example 1: List with Various Data Types
my_list = [42, 3.14, 'Hello', True, None]
print("List items:", my_list)

# Example 2: Using type() and len()
print("Type of the list:", type(my_list))
print("Length of the list:", len(my_list))
List items: [42, 3.14, 'Hello', True, None]
Type of the list: <class 'list'>
Length of the list: 5

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


۱ گام اول: تعریف متغیرها

ابتدا اطلاعات زیر را در متغیرهایی با نام‌های مناسب و خوانا ذخیره می‌کنیم.

FirstName = "Mohammad Hossein"
LastName = "Soleymani"
Age = 32
Height = 177.43  # قد به سانتی‌متر
BodyHealth = True
نکته نام‌گذاری: استفاده از نام Height به جای H باعث می‌شود کد شما بسیار خواناتر و قابل فهم‌تر باشد. به این کار "کد خود-مستند" (Self-documenting code) می‌گویند.

۲ گام دوم: ترکیب متغیرها برای ساخت یک خروجی جذاب

با استفاده از f-string، یک متن کامل و حرفه‌ای برای نمایش اطلاعات ایجاد می‌کنیم.

# روش حرفه‌ای و مدرن با f-string
FullName = f"{FirstName} {LastName}"

# ساخت یک گزارش کامل با f-string و کاراکتر خط جدید (\n)
ProfileReport = f"User Profile:\nName: {FullName}\nAge: {Age} years old\nHeight: {Height} cm"

print(ProfileReport)
print(f"Is health status good? {BodyHealth}")
User Profile: Name: Mohammad Hossein Soleymani Age: 32 years old Height: 177.43 cm Is health status good? True

۳ گام سوم: ذخیره تمام داده‌ها در یک لیست

تمام متغیرها را درون یک لیست به نام Person قرار می‌دهیم و به اعضای آن از طریق اندیس (که از 0 شروع می‌شود) دسترسی پیدا می‌کنیم.

Person = [FullName, Age, Height, BodyHealth]

# چاپ کل لیست
print("Complete list:")
print(Person)

# چاپ نام شخص (عنصر اول با اندیس 0)
print("\nJust the name:")
print(Person[0])
Complete list: ['Mohammad Hossein Soleymani', 32, 177.43, True] Just the name: Mohammad Hossein Soleymani
یادآوری مهم: وقتی نام یک متغیر (مانند Person[0]) را صدا می‌زنید، هرگز آن را داخل کوتیشن (' ' یا " ") قرار ندهید، چون در این صورت به یک رشته متنی ساده تبدیل می‌شود.

۴ گام چهارم: بررسی نوع داده‌های درون لیست

با استفاده از تابع type()، نوع داده هر کدام از اعضای لیست را بررسی و چاپ می‌کنیم.

print("\nData types in the Person list:")
print(f"Type of FullName: {type(Person[0])}")
print(f"Type of Age: {type(Person[1])}")
print(f"Type of Height: {type(Person[2])}")
print(f"Type of BodyHealth: {type(Person[3])}")
Data types in the Person list: Type of FullName: <class 'str'> Type of Age: <class 'int'> Type of Height: <class 'float'> Type of BodyHealth: <class 'bool'>

از تمرین کردن غافل نشوید! هر چقدر بیشتر مفاهیم را در قالب تمرین‌های کوچک و بزرگ به کار بگیرید، درک شما عمیق‌تر و مهارتتان بیشتر خواهد شد.

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

۱. مدیریت اعضای لیست: فراتر از append

یاد گرفتیم که با `append()` یک آیتم به انتهای لیست اضافه کنیم. اما ابزارهای بسیار بیشتری برای مدیریت اعضا وجود دارد.

افزودن اعضا

  • insert(index, value): یک آیتم را در اندیس مشخصی اضافه می‌کند.
  • extend(iterable): اعضای یک لیست (یا هر شیء تکرارپذیر دیگر) را به انتهای لیست فعلی اضافه می‌کند.
fruits = ['apple', 'banana', 'cherry']
# اضافه کردن در اندیس 1
fruits.insert(1, 'orange')
print(f"After insert: {fruits}")

# اضافه کردن یک لیست دیگر به انتهای آن
more_fruits = ['mango', 'pineapple']
fruits.extend(more_fruits)
print(f"After extend: {fruits}")
After insert: ['apple', 'orange', 'banana', 'cherry'] After extend: ['apple', 'orange', 'banana', 'cherry', 'mango', 'pineapple']

حذف اعضا

  • remove(value): اولین آیتم با مقدار مشخص شده را حذف می‌کند.
  • pop(index): آیتم موجود در اندیس مشخص را حذف **و آن را برمی‌گرداند**. اگر اندیس ندهید، آیتم آخر را حذف می‌کند.
  • clear(): تمام اعضای لیست را حذف کرده و آن را خالی می‌کند.
items = ['book', 'pen', 'pencil', 'pen']
items.remove('pen') # فقط اولین 'pen' حذف می‌شود
print(f"After remove: {items}")

removed_item = items.pop(1) # حذف 'pencil' در اندیس 1
print(f"After pop: {items}")
print(f"Removed item: {removed_item}")

items.clear()
print(f"After clear: {items}")
After remove: ['book', 'pencil', 'pen'] After pop: ['book', 'pen'] Removed item: pencil After clear: []

۲. برش‌دهی (Slicing): استخراج زیرلیست‌ها

برش‌دهی یک تکنیک فوق‌العاده قدرتمند برای انتخاب بخشی از یک لیست است. فرمت کلی آن list[start:stop:step] است.

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# از اندیس 2 تا قبل از 5
print(f"numbers[2:5] -> {numbers[2:5]}")

# از ابتدا تا قبل از اندیس 4
print(f"numbers[:4] -> {numbers[:4]}")

# از اندیس 6 تا انتها
print(f"numbers[6:] -> {numbers[6:]}")

# کل لیست با گام‌های 2 تایی
print(f"numbers[::2] -> {numbers[::2]}")

# معکوس کردن لیست
print(f"numbers[::-1] -> {numbers[::-1]}")
numbers[2:5] -> [2, 3, 4] numbers[:4] -> [0, 1, 2, 3] numbers[6:] -> [6, 7, 8, 9] numbers[::2] -> [0, 2, 4, 6, 8] numbers[::-1] -> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

۳. مرتب‌سازی و کپی‌کردن

دو عملیات بسیار رایج که درک تفاوت‌های ظریف آن‌ها اهمیت زیادی دارد.

مرتب‌سازی: `sort()` در مقابل `sorted()`

نکته کلیدی: متد .sort() خود لیست را **درجا (in-place)** تغییر می‌دهد و چیزی برنمی‌گرداند. اما تابع `sorted()` یک **لیست مرتب‌شده‌ی جدید** برمی‌گرداند و لیست اصلی را دست‌نخورده باقی می‌گذارد.
grades = [88, 95, 72, 100, 85]
sorted_grades = sorted(grades)
print(f"Original list: {grades}")
print(f"New sorted list: {sorted_grades}")

grades.sort(reverse=True) # مرتب‌سازی نزولی خود لیست
print(f"Original list after .sort(): {grades}")
Original list: [88, 95, 72, 100, 85] New sorted list: [72, 85, 88, 95, 100] Original list after .sort(): [100, 95, 88, 85, 72]

کپی کردن: ارجاع در مقابل کپی واقعی

این یکی از مفاهیم حیاتی در پایتون است. وقتی می‌نویسید list_b = list_a شما یک کپی جدید نمی‌سازید، بلکه فقط یک نام دیگر به همان لیست در حافظه می‌دهید.

هشدار: تغییر در لیست جدید، لیست اصلی را نیز تغییر خواهد داد، چون هر دو به یک شیء اشاره دارند!
original = [1, 2, 3]
reference_copy = original # این یک کپی نیست!
true_copy = original.copy() # روش صحیح کپی کردن

reference_copy.append(4)
print(f"Original list: {original}") # لیست اصلی هم تغییر کرد!
print(f"Reference copy: {reference_copy}")
print(f"True copy: {true_copy}") # این کپی دست نخورده باقی مانده
Original list: [1, 2, 3, 4] Reference copy: [1, 2, 3, 4] True copy: [1, 2, 3]

مقایسه جامع: List, Tuple, Set & Dictionary

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

ویژگی لیست (List) تاپل (Tuple) مجموعه (Set) دیکشنری (Dictionary)
نحوه ساخت [1, "a", True] (1, "a", True) {1, "a", True} {"key": "value"}
تغییرپذیری (Mutable) بله خیر (Immutable) بله بله
ترتیب (Ordered) بله بله خیر بله (از پایتون 3.7+)
اعضای تکراری مجاز است مجاز است مجاز نیست کلیدهای تکراری مجاز نیست
نحوه دسترسی اندیس عددی (li[0]) اندیس عددی (tup[0]) دسترسی مستقیم ندارد بر اساس کلید (d["key"])
کاربرد اصلی مجموعه داده‌های عمومی و قابل تغییر داده‌های ثابت و غیرقابل تغییر حذف تکراری‌ها و عملیات ریاضی ذخیره داده به صورت زوج‌های کلید-مقدار
تبریک می‌گوییم! شما اکنون دانش عمیقی از چهار ساختار داده بنیادین در پایتون دارید. انتخاب هوشمندانه بین این چهار مورد، شما را به یک برنامه‌نویس کارآمدتر و حرفه‌ای‌تر تبدیل می‌کند.

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

۱. تاپل (Tuple): لیستِ تغییرناپذیر

تاپل‌ها بسیار شبیه به لیست‌ها هستند، با یک تفاوت کلیدی و بسیار مهم: **تاپل‌ها تغییرناپذیر (Immutable) هستند.** یعنی پس از ساخته شدن، نمی‌توانید هیچ‌کدام از اعضای آن را تغییر دهید، حذف کنید یا عضو جدیدی به آن اضافه کنید.

ویژگی‌های کلیدی تاپل:
  • با استفاده از پرانتز () ساخته می‌شوند.
  • تغییرناپذیر (Immutable) هستند.
  • ترتیب اعضای آن حفظ می‌شود (Ordered).
  • می‌توانند حاوی اعضای تکراری باشند.

چگونه یک تاپل بسازیم؟

# یک تاپل شامل انواع داده مختلف
my_tuple = ("apple", 3.14, 100, True)
print(my_tuple)

# دسترسی به اعضا درست مانند لیست‌ها با اندیس
print(f"عضو اول: {my_tuple[0]}")
print(f"عضو آخر: {my_tuple[-1]}")

# تلاش برای تغییر یک عضو که با خطا مواجه می‌شود
# اگر این خط را از کامنت خارج کنید، برنامه خطا می‌دهد
# my_tuple[0] = "banana" 
('apple', 3.14, 100, True) عضو اول: apple عضو آخر: True # TypeError: 'tuple' object does not support item assignment

چرا از تاپل استفاده کنیم؟

شاید بپرسید وقتی لیست وجود دارد، چرا به یک ساختار محدودتر نیاز داریم؟

  • امنیت داده: وقتی می‌خواهید مطمئن باشید داده‌های شما در طول برنامه به هیچ وجه تغییر نمی‌کنند (مثلاً مختصات جغرافیایی یک نقطه یا روزهای هفته).
  • کمی بهینه‌تر: پایتون می‌تواند تاپل‌ها را کمی سریع‌تر از لیست‌ها پردازش کند، چون ساختار ساده‌تری دارند.

۲. مجموعه (Set): کیسه‌ای از اعضای یکتا

مجموعه‌ها یا "ست"ها، برای نگهداری اعضای **غیرتکراری** و **بدون ترتیب** به کار می‌روند. آن‌ها را مانند یک کیسه در نظر بگیرید که فقط نمونه‌های منحصر به فردی از اشیاء را در خود نگه می‌دارد و ترتیب قرارگیری آن‌ها برایش مهم نیست.

ویژگی‌های کلیدی مجموعه (Set):
  • با استفاده از آکولاد {} یا تابع set() ساخته می‌شوند.
  • اعضای آن **حتماً یکتا (Unique)** هستند (تکراری‌ها خودکار حذف می‌شوند).
  • ترتیب مشخصی ندارند (Unordered).
  • تغییرپذیر (Mutable) هستند (می‌توان عضو اضافه یا کم کرد).

چگونه یک مجموعه بسازیم؟

# ساخت یک مجموعه با اعضای تکراری
numbers_list = [1, 2, 3, 2, 4, 3, 5, 1]
unique_numbers_set = set(numbers_list)
print(f"لیست اولیه: {numbers_list}")
print(f"مجموعه ساخته شده (بدون تکرار): {unique_numbers_set}")

# افزودن و حذف کردن اعضا
unique_numbers_set.add(6)   # افزودن عدد 6
print(f"بعد از افزودن 6: {unique_numbers_set}")

unique_numbers_set.remove(1) # حذف کردن عدد 1
print(f"بعد از حذف 1: {unique_numbers_set}")
لیست اولیه: [1, 2, 3, 2, 4, 3, 5, 1] مجموعه ساخته شده (بدون تکرار): {1, 2, 3, 4, 5} بعد از افزودن 6: {1, 2, 3, 4, 5, 6} بعد از حذف 1: {2, 3, 4, 5, 6}
نکته مهم: برای ساخت یک مجموعه خالی، **حتماً** از set() استفاده کنید. استفاده از {} یک دیکشنری خالی ایجاد می‌کند که در درس‌های آینده با آن آشنا خواهیم شد.

عملیات ریاضی روی مجموعه‌ها

قدرت واقعی مجموعه‌ها در انجام عملیات ریاضی مانند اجتماع، اشتراک و تفاضل است.

set_A = {1, 2, 3, 4}
set_B = {3, 4, 5, 6}

# Union (اجتماع: تمام اعضای هر دو مجموعه)
print(f"اجتماع A | B: {set_A | set_B}")

# Intersection (اشتراک: اعضای مشترک)
print(f"اشتراک A & B: {set_A & set_B}")

# Difference (تفاضل: اعضایی که در A هستند ولی در B نیستند)
print(f"تفاضل A - B: {set_A - set_B}")
اجتماع A | B: {1, 2, 3, 4, 5, 6} اشتراک A & B: {3, 4} تفاضل A - B: {1, 2}

جمع‌بندی و مقایسه

ویژگی لیست (List) تاپل (Tuple) مجموعه (Set)
نحوه ساخت [1, 2, 3] (1, 2, 3) {1, 2, 3}
تغییرپذیری بله (Mutable) خیر (Immutable) بله (Mutable)
ترتیب دارد (Ordered) دارد (Ordered) ندارد (Unordered)
اعضای تکراری مجاز است مجاز است مجاز نیست (حذف می‌شود)
کاربرد اصلی مجموعه داده‌های عمومی که تغییر می‌کنند داده‌های ثابت و غیرقابل تغییر حذف تکراری‌ها و عملیات ریاضی
انتخاب ساختار داده‌ی مناسب (لیست، تاپل یا مجموعه) یکی از مهارت‌های کلیدی در برنامه‌نویسی است که به بهینه‌تر و خواناتر شدن کد شما کمک شایانی می‌کند.

تا به حال با ساختارهای داده‌ای سر و کار داشتیم که داده‌ها را به صورت یک دنباله پشت سر هم ذخیره می‌کردند (مانند لیست و تاپل). دیکشنری‌ها یک رویکرد متفاوت و بسیار قدرتمند را معرفی می‌کنند: ذخیره داده‌ها به صورت زوج‌های **کلید-مقدار (Key-Value)**. این ساختار شبیه به یک دیکشنری واقعی است که در آن هر کلمه (کلید) یک تعریف (مقدار) مشخص دارد.

۱. آناتومی یک دیکشنری: کلید و مقدار چیست؟

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

  • کلید (Key) یک شناسه منحصر به فرد برای دسترسی به داده است. کلیدها باید **تغییرناپذیر (Immutable)** باشند (مانند رشته‌ها، اعداد یا تاپل‌ها) و در یک دیکشنری **نباید تکراری** باشند.
  • مقدار (Value) داده‌ای است که به یک کلید نسبت داده می‌شود. مقدارها می‌توانند از **هر نوع داده‌ای** باشند: عدد، رشته، لیست، تاپل یا حتی یک دیکشنری دیگر.

دیکشنری‌ها با استفاده از آکولاد {} ساخته می‌شوند و هر زوج کلید-مقدار با یک کالن : از هم جدا می‌شود.


۲. مثال عملی: ساخت دیکشنری خودرو

بیایید اطلاعات یک خودروی فورد موستانگ را در قالب یک دیکشنری ذخیره کنیم.

Car = {
    "brand": "Ford",
    "model": "Mustang",
    "year": 1964,
    "colors": ["قرمز", "مشکی", "سفید"],
    "is_automatic": True
}

# دسترسی به مقادیر با استفاده از کلید
print(f"برند خودرو: {Car['brand']}")
print(f"سال ساخت: {Car['year']}")
print(f"رنگ‌های موجود: {Car['colors']}")

# دسترسی به یکی از رنگ‌ها (عضو دوم لیست)
print(f"دومین رنگ در لیست: {Car['colors'][1]}")

# افزودن یک زوج کلید-مقدار جدید
Car["engine"] = "V8"
print(f"دیکشنری بعد از افزودن موتور: {Car}")

# تغییر مقدار یک کلید موجود
Car["year"] = 1965
print(f"سال ساخت آپدیت شده: {Car['year']}")
برند خودرو: Ford سال ساخت: 1964 رنگ‌های موجود: ['قرمز', 'مشکی', 'سفید'] دومین رنگ در لیست: مشکی دیکشنری بعد از افزودن موتور: {'brand': 'Ford', 'model': 'Mustang', 'year': 1964, 'colors': ['قرمز', 'مشکی', 'سفید'], 'is_automatic': True, 'engine': 'V8'} سال ساخت آپدیت شده: 1965

۳. متدهای پرکاربرد دیکشنری

پایتون متدهای داخلی زیادی برای کار با دیکشنری‌ها فراهم کرده است که در ادامه مهم‌ترین آن‌ها را با مثال بررسی می‌کنیم.

متد (Method) توضیحات و مثال
clear() تمام اعضا را از دیکشنری حذف می‌کند.
d = {'a': 1}; d.clear() # d becomes {}
copy() یک کپی سطحی (shallow copy) از دیکشنری برمی‌گرداند.
d1 = {'a': 1}; d2 = d1.copy()
fromkeys(keys, value) یک دیکشنری جدید با کلیدهای مشخص شده و یک مقدار ثابت برای همه آن‌ها ایجاد می‌کند.
keys = ['a', 'b']; d = dict.fromkeys(keys, 0) # {'a': 0, 'b': 0}
get(key, default) مقدار مربوط به کلید را برمی‌گرداند. اگر کلید وجود نداشته باشد، به جای خطا، مقدار `default` (که به طور پیش‌فرض `None` است) را برمی‌گرداند.
d = {'a': 1}; print(d.get('a')); print(d.get('b', 'Not Found'))
items() یک لیست از تاپل‌های (کلید, مقدار) را برمی‌گرداند.
d = {'a': 1, 'b': 2}; print(d.items()) # dict_items([('a', 1), ('b', 2)])
keys() یک لیست از تمام کلیدهای دیکشنری برمی‌گرداند.
d = {'a': 1, 'b': 2}; print(d.keys()) # dict_keys(['a', 'b'])
pop(key) عضو با کلید مشخص شده را حذف کرده و مقدار آن را برمی‌گرداند.
d = {'a': 1, 'b': 2}; val = d.pop('a'); print(val) # 1
popitem() آخرین زوج (کلید, مقدار) اضافه شده را حذف کرده و آن را برمی‌گرداند.
d = {'a': 1, 'b': 2}; item = d.popitem(); print(item) # ('b', 2)
setdefault(key, value) اگر کلید وجود داشته باشد، مقدار آن را برمی‌گرداند. در غیر این صورت، کلید را با مقدار مشخص شده اضافه کرده و سپس آن مقدار را برمی‌گرداند.
d = {'a': 1}; d.setdefault('b', 2); print(d) # {'a': 1, 'b': 2}
update(other_dict) دیکشنری را با زوج‌های کلید-مقدار از یک دیکشنری دیگر آپدیت می‌کند (اگر کلید مشترک باشد، مقدار آن به‌روز می‌شود).
d1 = {'a': 1}; d2 = {'b': 2}; d1.update(d2); print(d1)
values() یک لیست از تمام مقادیر دیکشنری برمی‌گرداند.
d = {'a': 1, 'b': 2}; print(d.values()) # dict_values([1, 2])
دیکشنری‌ها به دلیل سرعت بالای دسترسی به داده‌ها از طریق کلید، یکی از بهینه‌ترین و پرکاربردترین ساختارهای داده در پایتون برای مدل‌سازی اشیاء دنیای واقعی (مانند کاربر، محصول یا خودرو) هستند.

تاکنون با لیست‌ها، تاپل‌ها و دیکشنری‌ها به صورت مجزا آشنا شدیم. قدرت واقعی پایتون زمانی آشکار می‌شود که این ساختارها را مانند عروسک‌های ماتریوشکا (Matryoshka) در دل یکدیگر قرار دهیم. به این کار، ساخت **ساختارهای تودرتو (Nested Structures)** می‌گویند که برای مدل‌سازی داده‌های پیچیده و سلسله‌مراتبی ضروری است.

۱. لیست‌های تودرتو (Nested Lists): ساخت ماتریس

ساده‌ترین نوع ساختار تودرتو، لیستی است که اعضای آن خودشان لیست هستند. این ساختار برای نمایش داده‌های جدولی یا ماتریسی (مانند صفحه شطرنج، جدول اکسل یا یک ماتریس ریاضی) ایده‌آل است.

# یک ماتریس ۳x۳
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# دسترسی به یک ردیف کامل (ردیف دوم با ایندکس ۱)
print(f"ردیف دوم ماتریس: {matrix[1]}")

# دسترسی به یک عنصر خاص (عنصر سوم از ردیف دوم)
# ایندکس اول برای ردیف (سطر) و ایندکس دوم برای ستون است
element = matrix[1][2]
print(f"عنصر در سطر ۱، ستون ۲: {element}")

# تغییر مقدار یک عنصر
matrix[0][0] = 99
print(f"ماتریس پس از تغییر: {matrix}")
ردیف دوم ماتریس: [4, 5, 6] عنصر در سطر ۱، ستون ۲: 6 ماتریس پس از تغییر: [[99, 2, 3], [4, 5, 6], [7, 8, 9]]
نکته کلیدی: برای دسترسی به عناصر در لیست‌های تودرتو، از ایندکس‌گذاری چندگانه [row][col] استفاده می‌کنیم. ایندکس اول (بیرونی) لیست اصلی را انتخاب می‌کند و ایندکس دوم (درونی) عنصر داخل آن لیست را.

۲. لیست دیکشنری‌ها (List of Dictionaries): پایگاه داده کوچک شما

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

users = [
    {
        "id": 1,
        "name": "علی رضایی",
        "email": "ali.rezaei@example.com"
    },
    {
        "id": 2,
        "name": "مریم احمدی",
        "email": "maryam.ahmadi@example.com"
    }
]

# دسترسی به کاربر اول (یک دیکشنری کامل)
first_user = users[0]
print(f"کاربر اول: {first_user}")

# دسترسی به نام کاربر دوم
second_user_name = users[1]["name"]
print(f"نام کاربر دوم: {second_user_name}")

# پیمایش و چاپ اطلاعات کاربران
print("\nلیست ایمیل کاربران:")
for user in users:
    print(f"- {user['email']}")
کاربر اول: {'id': 1, 'name': 'علی رضایی', 'email': 'ali.rezaei@example.com'} نام کاربر دوم: مریم احمدی لیست ایمیل کاربران: - ali.rezaei@example.com - maryam.ahmadi@example.com

۳. دیکشنری‌های پیچیده: مدل‌سازی یک شیء کامل

گاهی یک دیکشنری به تنهایی می‌تواند حاوی لیست‌ها یا دیکشنری‌های دیگر به عنوان مقدار (value) باشد. این ساختار برای مدل‌سازی یک شیء واحد با جزئیات پیچیده و چندلایه عالی است.

student_profile = {
    "student_id": "S40510",
    "full_name": "سارا کریمی",
    "major": "مهندسی نرم‌افزار",
    "courses": ["مبانی برنامه‌نویسی", "ساختمان داده", "طراحی الگوریتم"],
    "contact": {
        "email": "sara.k@university.ac.ir",
        "phone": "09120001122"
    }
}

# دسترسی به لیست درس‌ها
courses_list = student_profile["courses"]
print(f"لیست دروس: {courses_list}")

# دسترسی به دومین درس
second_course = student_profile["courses"][1]
print(f"دومین درس: {second_course}")

# دسترسی به ایمیل دانشجو (دسترسی به مقدار در دیکشنری تودرتو)
email = student_profile["contact"]["email"]
print(f"ایمیل: {email}")

# اضافه کردن یک درس جدید به لیست
student_profile["courses"].append("هوش مصنوعی")
print(f"لیست دروس آپدیت شده: {student_profile['courses']}")
لیست دروس: ['مبانی برنامه‌نویسی', 'ساختمان داده', 'طراحی الگوریتم'] دومین درس: ساختمان داده ایمیل: sara.k@university.ac.ir لیست دروس آپدیت شده: ['مبانی برنامه‌نویسی', 'ساختمان داده', 'طراحی الگوریتم', 'هوش مصنوعی']
جمع‌بندی: ساختارهای تودرتو به شما اجازه می‌دهند تا روابط سلسله‌مراتبی و پیچیده بین داده‌ها را به شکلی طبیعی و خوانا در کد خود پیاده‌سازی کنید. تسلط بر دسترسی، تغییر و پیمایش این ساختارها یک مهارت کلیدی برای هر توسعه‌دهنده پایتون است.

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


صورت مسئله (Scenario)

شما باید یک ساختار داده واحد در پایتون به نام course_data ایجاد کنید که اطلاعات زیر را در مورد یک دوره آموزشی ذخیره کند:

  • اطلاعات اصلی دوره: کد دوره ("CS101") و تعداد واحد (3). این اطلاعات ثابت و غیرقابل تغییر هستند.
  • اطلاعات استاد: نام ("دکتر اکبری") و ایمیل ("akbari@university.edu").
  • لیست دانشجویان: شامل دو دانشجو با شماره دانشجویی و نام آن‌ها.
  • کلمات کلیدی (تگ‌ها): مجموعه‌ای از تگ‌های مرتبط با دوره مانند "پایتون"، "مقدماتی" و "برنامه‌نویسی". تگ‌ها نباید تکراری باشند.

وظایف شما (Tasks)

بر اساس صورت مسئله، کد پایتون را بنویسید که مراحل زیر را انجام دهد:

  1. گام اول: ساخت ساختار داده
    • یک دیکشنری اصلی به نام course_data بسازید.
    • برای اطلاعات اصلی دوره (کد و واحد) از یک تاپل استفاده کنید.
    • برای اطلاعات استاد از یک دیکشنری تودرتو استفاده کنید.
    • برای لیست دانشجویان از یک لیست تودرتو که هر عضو آن یک دیکشنری است، استفاده کنید.
    • برای تگ‌ها از یک مجموعه (Set) استفاده کنید.
  2. گام دوم: دسترسی به داده‌ها
    • کد دوره را از ساختار داده استخراج و چاپ کنید.
    • ایمیل استاد را چاپ کنید.
    • نام دانشجوی دوم را چاپ کنید.
  3. گام سوم: اصلاح و افزودن داده
    • یک دانشجوی جدید با شماره دانشجویی "S9903" و نام "نگار محمودی" به لیست دانشجویان اضافه کنید.
    • تگ "علوم کامپیوتر" را به مجموعه تگ‌ها اضافه کنید.
    • ایمیل استاد را به "h.akbari@university.edu" تغییر دهید.
  4. گام چهارم (چالش): پیمایش و گزارش‌گیری
    • با استفاده از یک حلقه، لیستی از تمام شماره‌های دانشجویی (`student_id`) ثبت‌نام شده در کلاس را استخراج کرده و چاپ کنید.

کد کامل و خروجی نهایی (Solution)

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

# گام اول: ساخت ساختار داده اولیه
course_data = {
    "course_info": ("CS101", 3),  # استفاده از تاپل برای داده‌های ثابت
    "instructor": {
        "name": "دکتر اکبری",
        "email": "akbari@university.edu"
    },
    "students": [
        {"student_id": "S9901", "name": "رضا حسینی"},
        {"student_id": "S9902", "name": "فاطمه کریمی"}
    ],
    "tags": {"پایتون", "مقدماتی", "برنامه‌نویسی"} # استفاده از مجموعه برای تگ‌های یکتا
}

print("--- ساختار اولیه ---")
print(course_data)

# گام دوم: دسترسی به داده‌ها
print("\n--- دسترسی به داده‌ها ---")
course_code = course_data["course_info"][0]
instructor_email = course_data["instructor"]["email"]
second_student_name = course_data["students"][1]["name"]

print(f"کد دوره: {course_code}")
print(f"ایمیل استاد: {instructor_email}")
print(f"نام دانشجوی دوم: {second_student_name}")

# گام سوم: اصلاح و افزودن داده
print("\n--- اصلاح و افزودن داده ---")
# افزودن دانشجوی جدید
new_student = {"student_id": "S9903", "name": "نگار محمودی"}
course_data["students"].append(new_student)

# افزودن تگ جدید
course_data["tags"].add("علوم کامپیوتر")

# تغییر ایمیل استاد
course_data["instructor"]["email"] = "h.akbari@university.edu"

print("ساختار داده پس از تغییرات:")
print(course_data)

# گام چهارم (چالش): پیمایش و گزارش‌گیری
print("\n--- چالش: لیست شماره‌های دانشجویی ---")
student_ids = []
for student in course_data["students"]:
    student_ids.append(student["student_id"])

print(f"شماره‌های دانشجویی ثبت‌نام شده: {student_ids}")
--- ساختار اولیه --- {'course_info': ('CS101', 3), 'instructor': {'name': 'دکتر اکبری', 'email': 'akbari@university.edu'}, 'students': [{'student_id': 'S9901', 'name': 'رضا حسینی'}, {'student_id': 'S9902', 'name': 'فاطمه کریمی'}], 'tags': {'برنامه‌نویسی', 'پایتون', 'مقدماتی'}} --- دسترسی به داده‌ها --- کد دوره: CS101 ایمیل استاد: akbari@university.edu نام دانشجوی دوم: فاطمه کریمی --- اصلاح و افزودن داده --- ساختار داده پس از تغییرات: {'course_info': ('CS101', 3), 'instructor': {'name': 'دکتر اکبری', 'email': 'h.akbari@university.edu'}, 'students': [{'student_id': 'S9901', 'name': 'رضا حسینی'}, {'student_id': 'S9902', 'name': 'فاطمه کریمی'}, {'student_id': 'S9903', 'name': 'نگار محمودی'}], 'tags': {'علوم کامپیوتر', 'پایتون', 'برنامه‌نویسی', 'مقدماتی'}} --- چالش: لیست شماره‌های دانشجویی --- شماره‌های دانشجویی ثبت‌نام شده: ['S9901', 'S9902', 'S9903']
نکات کلیدی این تمرین: این چالش شما را مجبور کرد تا از دیکشنری به عنوان ساختار اصلی، از تاپل برای داده‌های غیرقابل تغییر، از مجموعه برای آیتم‌های یکتا، از لیست برای گردآوری رکوردها و از ساختارهای تودرتو برای مدل‌سازی روابط بین داده‌ها استفاده کنید. تبریک! شما یک گام بزرگ در مسیر تسلط بر ساختارهای داده پایتون برداشته‌اید.

عملگرها (Operators) نمادهای ویژه‌ای هستند که برای انجام عملیات روی مقادیر (که به آنها عملوند یا Operand می‌گوییم) به کار می‌روند. به زبان ساده، عملگرها به پایتون می‌گویند که چه کاری با داده‌های شما انجام دهد. در این درس، با شش دسته اصلی از عملگرها در پایتون آشنا می‌شویم.


۱. عملگرهای محاسباتی (Arithmetic Operators)

این عملگرها برای انجام عملیات ریاضی استاندارد استفاده می‌شوند.

عملگر نام مثال توضیح
+ جمع (Addition) x + y دو مقدار را با هم جمع می‌کند.
- تفریق (Subtraction) x - y مقدار دوم را از اولی کم می‌کند.
* ضرب (Multiplication) x * y دو مقدار را در هم ضرب می‌کند.
/ تقسیم (Division) x / y تقسیم واقعی که حاصل آن همیشه یک عدد اعشاری (float) است.
% باقی‌مانده (Modulus) x % y باقی‌مانده تقسیم اولی بر دومی را برمی‌گرداند.
** توان (Exponentiation) x ** y مقدار اولی را به توان دومی می‌رساند.
// تقسیم صحیح (Floor Division) x // y خارج قسمت صحیح تقسیم را برمی‌گرداند (بخش اعشاری را حذف می‌کند).
a = 13
b = 4

print(f"a + b  = {a + b}")   # جمع
print(f"a - b  = {a - b}")   # تفریق
print(f"a * b  = {a * b}")   # ضرب
print(f"a / b  = {a / b}")   # تقسیم اعشاری
print(f"a % b  = {a % b}")   # باقی‌مانده
print(f"a ** b = {a ** b}")  # توان
print(f"a // b = {a // b}")  # تقسیم صحیح
a + b = 17 a - b = 9 a * b = 52 a / b = 3.25 a % b = 1 a ** b = 28561 a // b = 3

۲. عملگرهای انتسابی (Assignment Operators)

این عملگرها برای تخصیص (مقداردهی) یک مقدار به یک متغیر استفاده می‌شوند. علاوه بر عملگر ساده $=$, عملگرهای ترکیبی نیز وجود دارند که یک عملیات محاسباتی را با انتساب ترکیب می‌کنند.

x = 10
print(f"مقدار اولیه x: {x}")

x += 3  # معادل است با: x = x + 3
print(f"بعد از += 3: {x}")

x -= 4  # معادل است با: x = x - 4
print(f"بعد از -= 4: {x}")

x *= 2  # معادل است با: x = x * 2
print(f"بعد از *= 2: {x}")

x /= 3  # معادل است با: x = x / 3
print(f"بعد از /= 3: {x}")
مقدار اولیه x: 10 بعد از += 3: 13 بعد از -= 4: 9 بعد از *= 2: 18 بعد از /= 3: 6.0

۳. عملگرهای مقایسه‌ای (Comparison Operators)

این عملگرها دو مقدار را با هم مقایسه کرده و نتیجه را به صورت یک مقدار بولی (True یا False) برمی‌گردانند. این عملگرها در ساختارهای کنترلی مانند if بسیار پرکاربرد هستند.

x = 10
y = 12

print(f"{x} == {y} is {x == y}")  # برابر
print(f"{x} != {y} is {x != y}")  # نابرابر
print(f"{x} > {y} is {x > y}")    # بزرگتر
print(f"{x} < {y} is {x < y}")    # کوچکتر
print(f"{x} >= 10 is {x >= 10}") # بزرگتر یا مساوی
print(f"{y} <= 10 is {y <= 10}") # کوچکتر یا مساوی
10 == 12 is False 10 != 12 is True 10 > 12 is False 10 < 12 is True 10>= 10 is True 12 <= 10 is False

۴. عملگرهای منطقی (Logical Operators)

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

x = 5
y = 10

# and: هر دو شرط باید درست باشند
print(f"({x} > 3) and ({y} < 15) is {(x > 3) and (y < 15)}") 

# or: حداقل یکی از شرط‌ها باید درست باشد
print(f"({x} > 10) or ({y} < 15) is {(x > 10) or (y < 15)}")

# not: نتیجه عبارت بولی را معکوس می‌کند
print(f"not({x} > 3) is {not(x > 3)}")
(5 > 3) and (10 < 15) is True (5> 10) or (10 < 15) is True not(5> 3) is False

۵. عملگرهای هویتی (Identity Operators)

این عملگرها برای مقایسه هویت دو شیء به کار می‌روند، یعنی بررسی می‌کنند که آیا دو متغیر به یک شیء یکسان در حافظه اشاره دارند یا خیر. این با عملگر == که محتوای دو شیء را مقایسه می‌کند، متفاوت است.

list_a = ["apple", "banana"]
list_b = ["apple", "banana"]
list_c = list_a

# is: آیا دو متغیر به یک شیء یکسان اشاره دارند؟
print(f"list_a is list_b: {list_a is list_b}")  # False, چون دو شیء مجزا در حافظه هستند
print(f"list_a is list_c: {list_a is list_c}")  # True, چون c فقط نام دیگری برای list_a است

# is not: آیا دو متغیر به اشیاء متفاوتی اشاره دارند؟
print(f"list_a is not list_b: {list_a is not list_b}")

# مقایسه محتوا با ==
print(f"list_a == list_b: {list_a == list_b}")  # True, چون محتوای آنها یکسان است
list_a is list_b: False list_a is list_c: True list_a is not list_b: True list_a == list_b: True
نکته مهم: عملگر == محتوای دو متغیر را مقایسه می‌کند، در حالی که عملگر is بررسی می‌کند آیا دو متغیر به یک مکان در حافظه اشاره می‌کنند یا خیر.

۶. عملگرهای عضویت (Membership Operators)

این عملگرها برای بررسی اینکه آیا یک مقدار خاص، عضوی از یک دنباله (مانند لیست، تاپل، رشته یا دیکشنری) است یا خیر، به کار می‌روند.

my_list = ["apple", "banana", "cherry"]
my_dict = {"name": "Ali", "age": 30}

# in: آیا مقدار در دنباله وجود دارد؟
print(f"'banana' in my_list is {'banana' in my_list}")
print(f"'orange' in my_list is {'orange' in my_list}")
print(f"'name' in my_dict is {'name' in my_dict}") # در دیکشنری‌ها، عضویت در کلیدها بررسی می‌شود

# not in: آیا مقدار در دنباله وجود ندارد؟
print(f"'orange' not in my_list is {'orange' not in my_list}")
'banana' in my_list is True 'orange' in my_list is False 'name' in my_dict is True 'orange' not in my_list is True
جمع‌بندی: عملگرها ابزارهای قدرتمندی هستند که به شما امکان می‌دهند داده‌ها را دستکاری، مقایسه و ارزیابی کنید. تسلط بر این عملگرها برای نوشتن کدهای پیچیده‌تر و کارآمدتر ضروری است.

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


ساختار if (اگر...)

ساده‌ترین ساختار شرطی، دستور if است. این دستور یک شرط را بررسی می‌کند و اگر آن شرط برقرار باشد (یعنی نتیجه آن `True` باشد)، قطعه کدی که در زیر آن قرار دارد را اجرا می‌کند. اگر شرط برقرار نباشد، آن قطعه کد نادیده گرفته شده و برنامه به مسیر خود ادامه می‌دهد.

نحو (Syntax)

ساختار کلی دستور if به این صورت است. توجه به تورفتگی (Indentation) بعد از علامت : بسیار حیاتی است. این تورفتگی به پایتون می‌فهماند که کدام خطوط کد متعلق به بلوک if هستند.


if condition:
    # کدی که در صورت درست بودن شرط اجرا می‌شود
    # این کد باید تورفتگی داشته باشد
مثال: بررسی سن برای رای دادن

در این مثال، برنامه بررسی می‌کند که آیا سن فرد برای رای دادن مجاز است یا خیر.

age = 20

# بررسی می‌کنیم که آیا سن بزرگتر یا مساوی 18 است
if age >= 18:
    print("شما مجاز به رای دادن هستید.")

print("اجرای برنامه ادامه پیدا می‌کند...")
شما مجاز به رای دادن هستید. اجرای برنامه ادامه پیدا می‌کند...

اگر مقدار `age` را به عددی کمتر از 18 (مثلاً 15) تغییر دهید، پیام اول چاپ نخواهد شد و فقط خط آخر نمایش داده می‌شود.

اضافه کردن else (در غیر این صورت...)

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

بلوک else فقط زمانی اجرا می‌شود که شرط if برقرار نباشد.

مثال: تکمیل سناریوی رای دادن

حالا سناریوی قبل را کامل می‌کنیم تا به کسانی که مجاز نیستند نیز پیام مناسب نمایش داده شود.

age = 15

if age >= 18:
    print("شما مجاز به رای دادن هستید.")
else:
    print("متاسفانه شما هنوز به سن قانونی برای رای دادن نرسیده‌اید.")

print("بررسی صلاحیت به پایان رسید.")
متاسفانه شما هنوز به سن قانونی برای رای دادن نرسیده‌اید. بررسی صلاحیت به پایان رسید.
یک دو راهی در کد: ساختار if...else مانند یک دو راهی عمل می‌کند. برنامه شرط را بررسی کرده و بر اساس نتیجه، یا به مسیر if می‌رود یا به مسیر else. هیچ‌گاه هر دو مسیر با هم اجرا نمی‌شوند.

مثال عملی: تشخیص زوج یا فرد بودن عدد

یکی از کاربردهای کلاسیک if...else، تشخیص زوج یا فرد بودن یک عدد با استفاده از عملگر باقی‌مانده (%) است. اگر باقی‌مانده تقسیم یک عدد بر ۲ برابر با صفر باشد، آن عدد زوج است.

number = 17

# بررسی باقی‌مانده تقسیم بر 2
if number % 2 == 0:
    print(f"عدد {number} زوج است.")
else:
    print(f"عدد {number} فرد است.")
عدد 17 فرد است.
جمع‌بندی: شما با if و else اولین گام برای هوشمند کردن برنامه‌هایتان را برداشتید. این ساختارها به شما اجازه می‌دهند تا کدهایی بنویسید که به ورودی‌ها و شرایط مختلف، واکنش‌های متناسب نشان دهند. در درس بعدی، یاد خواهیم گرفت که چگونه با استفاده از elif، چند شرط مختلف را بررسی کنیم.

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


تابع input() چگونه کار می‌کند؟

وقتی پایتون به تابع input() می‌رسد، اجرای برنامه را متوقف کرده و منتظر می‌ماند تا کاربر متنی را در ترمینال تایپ کرده و کلید Enter را فشار دهد. سپس، متنی که کاربر وارد کرده است به صورت یک رشته (string) برگردانده می‌شود و ما می‌توانیم آن را در یک متغیر ذخیره کنیم.

ارائه پیام راهنما (Prompt)

برای اینکه کاربر بداند چه اطلاعاتی باید وارد کند، می‌توانیم یک پیام راهنما را به عنوان آرگومان به تابع input() بدهیم. این پیام قبل از دریافت ورودی به کاربر نمایش داده می‌شود.

# دریافت نام کاربر با استفاده از یک پیام راهنما
user_name = input("لطفاً نام خود را وارد کنید: ")

print(f"سلام، {user_name}! به دوره پایتون خوش آمدی.")

خروجی در زمان اجرا (کاربر 'علی' را وارد می‌کند):

لطفاً نام خود را وارد کنید: علی
سلام، علی! به دوره پایتون خوش آمدی.

نکته حیاتی: خروجی `input()` همیشه یک رشته (string) است!

این مهم‌ترین نکته در مورد تابع input() است. فرقی نمی‌کند کاربر عدد، متن یا هر چیز دیگری وارد کند، پایتون همیشه آن را به عنوان یک رشته (str) در نظر می‌گیرد. این موضوع می‌تواند منجر به خطاهای غیرمنتظره شود اگر به آن توجه نکنیم.

مثال: تلاش برای انجام محاسبات روی ورودی

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

age_str = input("سن خود را وارد کنید: ")

# تلاش برای جمع کردن یک رشته با یک عدد
result = age_str + 5 
print(result)

خروجی در زمان اجرا (کاربر '25' را وارد می‌کند):

سن خود را وارد کنید: 25
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
TypeError: can only concatenate str (not "int") to str
خطای TypeError به وضوح می‌گوید که شما نمی‌توانید یک رشته (خروجی `input`) را با یک عدد (int) "جمع" کنید.

راه حل: تبدیل نوع داده (Type Casting)

برای حل مشکل بالا، باید رشته دریافت شده از input() را به نوع داده عددی مورد نظر (مانند int برای عدد صحیح یا float برای عدد اعشاری) تبدیل کنیم. این کار با استفاده از توابعی به همین نام‌ها انجام می‌شود.

# دریافت سن به صورت رشته
age_str = input("سن خود را وارد کنید: ")

# تبدیل رشته به عدد صحیح (integer)
age_num = int(age_str)

# حالا محاسبات به درستی انجام می‌شود
future_age = age_num + 5
print(f"شما 5 سال دیگر {future_age} ساله خواهید شد.")

خروجی در زمان اجرا (کاربر '25' را وارد می‌کند):

سن خود را وارد کنید: 25
شما 5 سال دیگر 30 ساله خواهید شد.
روش خلاصه‌تر

می‌توانیم تبدیل نوع را مستقیماً در همان خطی که ورودی را دریافت می‌کنیم، انجام دهیم:

# دریافت و تبدیل نوع در یک خط
height = float(input("قد خود را به متر وارد کنید (مثال: 1.75): "))
print(f"قد شما {height} متر است.")

مثال کامل: یک محاسبه‌گر ساده سن

بیایید تمام مفاهیم را در یک برنامه کوچک ترکیب کنیم که سال تولد کاربر را گرفته و سن تقریبی او را محاسبه می‌کند.

# 1. دریافت نام کاربر (رشته)
name = input("نام شما چیست؟ ")

# 2. دریافت سال تولد و تبدیل به عدد
birth_year = int(input("در چه سالی (میلادی) به دنیا آمده‌اید؟ "))

# 3. سال فعلی (برای سادگی فرض می‌کنیم 2026 است)
current_year = 2026

# 4. محاسبه سن
age = current_year - birth_year

# 5. نمایش نتیجه نهایی
print(f"سلام {name}! شما تقریباً {age} سال سن دارید.")

یک نمونه از اجرای برنامه:

نام شما چیست؟ سارا
در چه سالی (میلادی) به دنیا آمده‌اید؟ 1995
سلام سارا! شما تقریباً 31 سال سن دارید.
جمع‌بندی: تابع input() دروازه ورود به دنیای برنامه‌های تعاملی است. با استفاده از آن می‌توانید اطلاعات را از کاربر بگیرید، اما همیشه به یاد داشته باشید که خروجی آن یک رشته است و برای محاسبات، باید آن را به نوع داده مناسب تبدیل کنید.

در درس قبل با ساختار if...else آشنا شدیم که برای تصمیم‌گیری بین دو حالت عالی است. اما در دنیای واقعی، اغلب با سناریوهایی روبرو می‌شویم که بیش از دو انتخاب ممکن وجود دارد. اینجا جایی است که elif (مخفف else if) وارد میدان می‌شود و به ما اجازه می‌دهد چندین شرط را به صورت زنجیره‌ای بررسی کنیم.


ساختار if...elif...else

این ساختار به شما اجازه می‌دهد تا یک شرط را با if بررسی کنید، سپس چندین شرط دیگر را با elif و در نهایت، اگر هیچ‌کدام از شرط‌ها برقرار نبودند، یک کار نهایی را با else انجام دهید.

نحو (Syntax)

پایتون شرط‌ها را به ترتیب از بالا به پایین بررسی می‌کند. به محض اینکه اولین شرط درست (True) را پیدا کند، بلوک کد مربوط به آن را اجرا کرده و از بقیه زنجیره شرط (elifها و else) صرف‌نظر می‌کند.


if condition1:
    # کدی که در صورت درست بودن condition1 اجرا می‌شود
elif condition2:
    # اگر condition1 نادرست بود، این شرط بررسی می‌شود
    # و در صورت درستی، این بلوک کد اجرا می‌شود
elif condition3:
    # و به همین ترتیب...
else:
    # اگر هیچ‌کدام از شرط‌های بالا درست نبودند، این بلوک اجرا می‌شود
مثال عملی: سیستم نمره‌دهی

فرض کنید می‌خواهیم برنامه‌ای بنویسیم که نمره یک دانشجو را گرفته و رتبه کیفی (A, B, C) او را مشخص کند.

score = 85

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "نیاز به تلاش بیشتر"

print(f"نمره شما {score} است و رتبه شما '{grade}' می‌باشد.")
نمره شما 85 است و رتبه شما 'B' می‌باشد.

در مثال بالا، چون `score` (85) بزرگتر از 90 نیست، شرط اول رد می‌شود. سپس پایتون شرط دوم (`score >= 80`) را بررسی می‌کند که درست است. بنابراین، مقدار `grade` برابر "B" قرار داده شده و برنامه از بقیه شرط‌ها (`elif score >= 70` و `else`) عبور می‌کند و به دستور `print` می‌رسد.

ویژگی‌های کلیدی و قوانین ساختار شرطی

بر اساس درخواست دقیق شما، بیایید قوانین این ساختار را مرور کنیم:

  • شروع همیشه با if است: یک زنجیره شرطی همیشه و فقط با یک دستور if آغاز می‌شود.
  • تعداد دلخواه elif: شما می‌توانید هر تعداد elif که نیاز دارید (از صفر تا بی‌نهایت) در زنجیره خود داشته باشید تا تمام حالات ممکن را پوشش دهید.
  • دستور else اختیاری در انتها: می‌توانید یک دستور else در انتهای زنجیره داشته باشید تا به عنوان یک حالت "پیش‌فرض" یا "catch-all" عمل کند. این دستور کاملاً اختیاری است.
  • اجرای فقط یک بلوک: مهم‌ترین نکته این است که در کل زنجیره if/elif/else، حداکثر یک بلوک کد اجرا خواهد شد؛ یعنی اولین بلوکی که شرط آن برقرار باشد.
ترتیب شرط‌ها مهم است! همیشه سعی کنید شرط‌های خاص‌تر و محدودتر را بالاتر از شرط‌های عمومی‌تر قرار دهید. اگر در مثال نمره‌دهی، شرط score >= 70 را قبل از score >= 90 قرار می‌دادیم، نمره‌ای مانند 95 به اشتباه رتبه "C" دریافت می‌کرد، زیرا شرط اول (بزرگتر از 70) را برآورده می‌کرد و پایتون بقیه را نادیده می‌گرفت.

سناریوهای مختلف استفاده

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

1. فقط if

برای انجام یک کار فقط در صورت برقراری یک شرط خاص و نادیده گرفتن آن در غیر این صورت.

temperature = 32
if temperature > 30:
    print("هوا گرم است، یک لیوان آب خنک بنوشید.")
# اگر شرط برقرار نباشد، هیچ اتفاقی نمی‌افتد
2. ساختار if...else

برای انتخاب بین دو مسیر انحصاری.

is_logged_in = False
if is_logged_in:
    print("به داشبورد خود خوش آمدید.")
else:
    print("لطفاً ابتدا وارد حساب کاربری خود شوید.")
3. ساختاری که با elif تمام می‌شود (بدون else)

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

command = "start"

if command == "start":
    print("سرویس در حال اجرا شدن است...")
elif command == "stop":
    print("سرویس متوقف شد.")
elif command == "restart":
    print("سرویس در حال راه‌اندازی مجدد است...")
# اگر command مقداری غیر از اینها باشد، هیچ پیامی چاپ نمی‌شود
جمع‌بندی: دستور elif ابزاری قدرتمند برای مدیریت تصمیم‌های چند گزینه‌ای است. با ترکیب هوشمندانه if، elif و else، می‌توانید منطق‌های پیچیده‌ای را در برنامه‌های خود پیاده‌سازی کنید و کدی بنویسید که به طیف وسیعی از شرایط به درستی واکنش نشان دهد.

وقت آن است که دانش خود را به کار بگیریم! در این تمرین، ما تمام مفاهیم کلیدی اخیر یعنی دریافت ورودی از کاربر (`input`)، ذخیره داده در لیست‌ها (`list`) و تصمیم‌گیری با (`if/elif/else`) را با هم ترکیب می‌کنیم تا یک سیستم ثبت نام و ورود ساده بسازیم.


صورت مسئله

برنامه‌ای بنویسید که مراحل زیر را انجام دهد:

  1. مرحله ثبت نام:
    • نام و نام خانوادگی، سن، نام کاربری و رمز عبور را از کاربر دریافت کند.
    • تمام این اطلاعات را در یک لیست (list) ذخیره کند.
  2. مرحله ورود:
    • پس از ثبت نام، از کاربر بخواهد تا با وارد کردن نام کاربری و رمز عبور، وارد سیستم شود.
    • ورودی‌های کاربر را با اطلاعات ذخیره شده در لیست مقایسه کند.
  3. نتیجه:
    • اگر اطلاعات مطابقت داشت، پیام "خوش آمدید" نمایش دهد.
    • اگر اطلاعات اشتباه بود، پیام خطای مناسب نمایش دهد.
    • اگر کاربر نام کاربری یا رمز عبور را خالی رها کرد، پیام مناسب دیگری نمایش دهد.

کد کامل راه‌حل

در ادامه، کد کامل پایتون برای پیاده‌سازی این سیستم آورده شده است. آن را با دقت بررسی کنید.

# یک لیست خالی برای ذخیره اطلاعات کاربر
user_data = []

print("--- به سیستم ثبت نام خوش آمدید ---")

# --- مرحله 1: ثبت نام ---
full_name = input("نام و نام خانوادگی خود را وارد کنید: ")
age = input("سن خود را وارد کنید: ")
username_reg = input("یک نام کاربری برای خود انتخاب کنید: ")
password_reg = input("یک رمز عبور قوی انتخاب کنید: ")

# افزودن اطلاعات به لیست
user_data.append(full_name)
user_data.append(age)
user_data.append(username_reg)
user_data.append(password_reg)

print("\nثبت نام با موفقیت انجام شد! اکنون وارد شوید.")
print("------------------------------------------")

# --- مرحله 2: ورود ---
print("\n--- لطفاً برای ورود اطلاعات خود را وارد کنید ---")
username_login = input("نام کاربری: ")
password_login = input("رمز عبور: ")

# --- مرحله 3: بررسی و اعتبارسنجی ---

# ابتدا بررسی می‌کنیم که ورودی‌ها خالی نباشند
if username_login == "" or password_login == "":
    print("\nخطا: نام کاربری و یا رمز عبور نمی‌تواند خالی باشد.")

# سپس اطلاعات را با داده‌های ذخیره شده در لیست مقایسه می‌کنیم
# نام کاربری در خانه شماره 2 و رمز عبور در خانه شماره 3 لیست است
elif username_login == user_data[2] and password_login == user_data[3]:
    print(f"\nورود موفق! سلام {user_data[0]}، خوش آمدید!")
    
# اگر هیچ‌کدام از شرایط بالا برقرار نبود
else:
    print("\nخطا: نام کاربری یا رمز عبور اشتباه است.")

یک نمونه از اجرای برنامه:

--- به سیستم ثبت نام خوش آمدید ---
نام و نام خانوادگی خود را وارد کنید: علی رضایی
سن خود را وارد کنید: 28
یک نام کاربری برای خود انتخاب کنید: ali_re
یک رمز عبور قوی انتخاب کنید: Python123

ثبت نام با موفقیت انجام شد! اکنون وارد شوید.
------------------------------------------

--- لطفاً برای ورود اطلاعات خود را وارد کنید ---
نام کاربری: ali_re
رمز عبور: Python123

ورود موفق! سلام علی رضایی، خوش آمدید!

تجزیه و تحلیل کد

نکته کلیدی: دسترسی به اعضای لیست با اندیس (Index)
همانطور که در کد پیشنهادی شما اشاره شده بود، مقایسه باید با مقادیر داخل لیست انجام شود، نه خود لیست. به یاد دارید که در لیست‌ها، شمارش از صفر شروع می‌شود. پس:
  • user_data[0] برابر است با نام کامل ('علی رضایی')
  • user_data[1] برابر است با سن ('28')
  • user_data[2] برابر است با نام کاربری ('ali_re')
  • user_data[3] برابر است با رمز عبور ('Python123')
بنابراین شرط اصلی ما برای بررسی صحت ورود elif username_login == user_data[2] and password_login == user_data[3]: است. این مهم‌ترین بخش این تمرین است.
ترتیب منطقی شرط‌ها

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

  1. if: اولین و مهم‌ترین بررسی، خالی نبودن فیلدهاست. اگر کاربر چیزی وارد نکند، برنامه نباید اصلاً به سراغ مقایسه برود.
  2. elif: تنها در صورتی که شرط اول (خالی بودن) برقرار نباشد، این شرط اجرا می‌شود و مقایسه اصلی را انجام می‌دهد.
  3. else: اگر ورودی‌ها خالی نبودند (شرط if رد شده) و با اطلاعات ما نیز مطابقت نداشتند (شرط elif رد شده)، پس حتماً اطلاعات اشتباه وارد شده‌اند.

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


حلقه for چیست و چه ساختاری دارد؟

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

نحو (Syntax)
for item in sequence:
    # کدی که برای هر item تکرار می‌شود
    # این کد باید تورفتگی (indentation) داشته باشد
  • item: یک متغیر موقتی است که در هر دور از حلقه، مقدار یکی از اعضای دنباله را به خود می‌گیرد.
  • in: یک کلمه کلیدی در پایتون است.
  • sequence: مجموعه‌ای که می‌خواهیم روی اعضای آن پیمایش کنیم.
مثال: پیمایش روی یک لیست
fruits = ["Apple", "Banana", "Cherry"]

for fruit in fruits:
    print(f"I like {fruit}")

print("Loop finished!")
I like Apple
I like Banana
I like Cherry
Loop finished!

حلقه for روی چه چیزهایی کار می‌کند؟ (Iterables)

حلقه for می‌تواند روی هر شیء "قابل پیمایش" (Iterable) کار کند. بیایید چند نمونه رایج را ببینیم:

۱. پیمایش روی رشته (String)

حلقه for کاراکتر به کاراکتر روی رشته حرکت می‌کند.

for char in "Python":
    print(char)
P
y
t
h
o
n
۲. پیمایش روی دیکشنری (Dictionary)

به صورت پیش‌فرض، حلقه for روی کلیدهای دیکشنری پیمایش می‌کند. اما می‌توانیم مقادیر یا جفت‌های کلید-مقدار را نیز پیمایش کنیم.

student = {
    "name": "Sara",
    "course": "Computer Science",
    "id": 987
}

# پیمایش روی کلیدها (پیش‌فرض)
print("--- Keys ---")
for key in student:
    print(key)

# پیمایش روی مقادیر با .values()
print("\n--- Values ---")
for value in student.values():
    print(value)
    
# پیمایش روی جفت‌های کلید-مقدار با .items()
print("\n--- Items ---")
for key, value in student.items():
    print(f"{key}: {value}")
--- Keys ---
name
course
id

--- Values ---
Sara
Computer Science
987

--- Items ---
name: Sara
course: Computer Science
id: 987

ایجاد دنباله‌های عددی با تابع range()

اگر بخواهیم یک قطعه کد را به تعداد مشخصی تکرار کنیم، تابع range() بهترین ابزار ماست. این تابع یک دنباله از اعداد تولید می‌کند.

  • range(stop): اعدادی از 0 تا یکی کمتر از stop تولید می‌کند.
  • range(start, stop): اعدادی از start تا یکی کمتر از stop.
  • range(start, stop, step): اعدادی از start تا stop با گام‌های step.
# چاپ اعداد 0 تا 4
for i in range(5):
    print(i, end=" ") # end=" " باعث می‌شود خروجی در یک خط باشد

print("\n--------------------")

# چاپ اعداد 2 تا 6
for i in range(2, 7):
    print(i, end=" ")
    
print("\n--------------------")

# چاپ اعداد زوج از 0 تا 10
for i in range(0, 11, 2):
    print(i, end=" ")
0 1 2 3 4
--------------------
2 3 4 5 6
--------------------
0 2 4 6 8 10

کنترل پیشرفته جریان حلقه

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

دستور break (شکستن حلقه)

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

numbers = [1, 5, 8, -3, 10, 12]

# پیدا کردن اولین عدد منفی و توقف
for num in numbers:
    if num < 0:
        print("اولین عدد منفی پیدا شد:", num)
        break # از حلقه خارج شو
    print("بررسی عدد:", num)
بررسی عدد: 1
بررسی عدد: 5
بررسی عدد: 8
اولین عدد منفی پیدا شد: -3
دستور continue (پرش به تکرار بعدی)

دستور continue باعث می‌شود ادامه کدهای داخل حلقه در آن دور خاص نادیده گرفته شده و حلقه مستقیماً به سراغ آیتم بعدی برود.

# چاپ فقط اعداد فرد
for i in range(1, 11):
    if i % 2 == 0: # اگر عدد زوج بود
        continue   # این دور را رد کن و به سراغ عدد بعدی برو
    print(i)
1
3
5
7
9
بلوک else در حلقه for

یک قابلیت جالب در پایتون این است که حلقه `for` می‌تواند یک بلوک `else` داشته باشد. این بلوک تنها در صورتی اجرا می‌شود که حلقه به صورت کامل و طبیعی (بدون برخورد به `break`) به پایان برسد.

my_list = [1, 3, 5, 7]
search_item = 8

for item in my_list:
    if item == search_item:
        print(f"آیتم {search_item} پیدا شد.")
        break
else:
    # این کد فقط زمانی اجرا می‌شود که حلقه تمام شود و break رخ ندهد
    print(f"آیتم {search_item} در لیست وجود ندارد.")
آیتم 8 در لیست وجود ندارد.
جمع‌بندی: حلقه for قلب تپنده بسیاری از برنامه‌هاست. با تسلط بر آن، می‌توانید داده‌ها را پردازش کنید، دنباله‌ها را بسازید و منطق‌های پیچیده‌ای را به شکلی تمیز و کارآمد پیاده‌سازی کنید.

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

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


ساختار حلقه `while`

ساختار حلقه while بسیار ساده است. این حلقه یک "شرط" (condition) را در ابتدای هر دور بررسی می‌کند. اگر شرط True باشد، بلوک کد داخل حلقه اجرا می‌شود. این فرآیند آنقدر تکرار می‌شود تا زمانی که شرط False شود.

نحو (Syntax)
while condition:
    # کدهایی که تا زمان درست بودن شرط، تکرار می‌شوند
    # این بلوک باید تورفتگی (indentation) داشته باشد
    # بسیار مهم: باید کاری انجام دهید که شرط در نهایت False شود!
مثال: یک شمارنده ساده

بیایید عددی را از 1 تا 5 چاپ کنیم. این کار را با `for` هم می‌توان انجام داد، اما مثال خوبی برای درک ساختار `while` است.

counter = 1  # 1. مقدار اولیه را تعریف می‌کنیم

while counter <= 5:  # 2. شرط را بررسی می‌کنیم
    print(counter)
    counter = counter + 1  # 3. مقدار را به‌روزرسانی می‌کنیم تا به پایان نزدیک شویم

print("حلقه به پایان رسید.")
1
2
3
4
5
حلقه به پایان رسید.

همانطور که می‌بینید، سه بخش کلیدی وجود دارد: مقداردهی اولیه، شرط و به‌روزرسانی. اگر مرحله سوم (به‌روزرسانی) را فراموش کنیم، چه اتفاقی می‌افتد؟

خطرناک‌ترین تله: حلقه‌های بی‌نهایت (Infinite Loops)

هشدار جدی! اگر شرط حلقه while هرگز False نشود، برنامه شما در یک "حلقه بی‌نهایت" گیر می‌کند و تا ابد به اجرای کد داخل حلقه ادامه می‌دهد. این اتفاق معمولاً به دلیل فراموش کردن به‌روزرسانی متغیر شرط رخ می‌دهد.
نمونه یک حلقه بی‌نهایت (این کد را با احتیاط اجرا کنید)
i = 1
while i > 0:
    print("این پیام تا ابد چاپ می‌شود...")
    # چون مقدار i هرگز تغییر نمی‌کند، شرط i > 0 همیشه True است

اگر برنامه‌تان در یک حلقه بی‌نهایت گیر کرد، در اکثر ترمینال‌ها و محیط‌های برنامه‌نویسی می‌توانید با فشردن کلیدهای Ctrl + C اجرای آن را متوقف کنید.

چه زمانی از `while` به جای `for` استفاده کنیم؟

قانون کلی این است:

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

این یکی از رایج‌ترین کاربردهای حلقه `while` است. ما می‌خواهیم از کاربر ورودی بگیریم و تا زمانی که ورودی معتبری وارد نکرده، این کار را تکرار کنیم.

user_input = ""

while user_input.lower() != "yes":
    user_input = input("آیا با قوانین موافق هستید؟ (yes/no): ")

print("عالی! شما موافقت کردید.")

یک نمونه از اجرای برنامه:

آیا با قوانین موافق هستید؟ (yes/no): maybe
آیا با قوانین موافق هستید؟ (yes/no): no
آیا با قوانین موافق هستید؟ (yes/no): OK
آیا با قوانین موافق هستید؟ (yes/no): yes
عالی! شما موافقت کردید.
کاربرد ۲: حلقه‌های بازی (Game Loops)

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

کنترل پیشرفته جریان در حلقه `while`

درست مانند حلقه `for`، در حلقه `while` نیز می‌توانیم از دستورات `break`, `continue` و بلوک `else` استفاده کنیم.

`break`, `continue`, و `else` در `while`

منطق این دستورات دقیقاً مشابه حلقه `for` است:

  • break: حلقه را فوراً متوقف می‌کند.
  • continue: دور فعلی حلقه را متوقف کرده و به ابتدای حلقه (بررسی شرط) می‌پرد.
  • else: تنها زمانی اجرا می‌شود که حلقه به صورت طبیعی (با False شدن شرط) به پایان برسد و نه با دستور break.
# مثالی ترکیبی
# برنامه یک عدد از کاربر می‌گیرد. اگر منفی بود، آن را نادیده می‌گیرد
# اگر صفر بود، متوقف می‌شود. در غیر این صورت، آن را چاپ می‌کند

print("اعداد مثبت را وارد کنید (برای خروج 0 را وارد کنید):")
while True: # یک حلقه که به ظاهر بی‌نهایت است
    num_str = input("> ")
    if not num_str.isdigit():
        print("لطفا فقط عدد وارد کنید.")
        continue # به ابتدای حلقه برگرد

    num = int(num_str)
    
    if num == 0:
        break # حلقه را بشکن
    
    print(f"شما عدد {num} را وارد کردید.")

print("برنامه تمام شد.")

در درس قبل دیدیم که یک حلقه بی‌نهایت تصادفی چقدر می‌تواند خطرناک باشد. اما اگر به شما بگویم یکی از پرکاربردترین الگوها در برنامه‌نویسی، ساخت یک "حلقه بی‌نهایت عمدی" و کنترل آن است، چه؟ به الگوی while True خوش آمدید!

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


ایده اصلی: "تا ابد اجرا شو، مگر اینکه من بگویم تمام کن"

ساختار while True به خودی خود یک حلقه بی‌نهایت ایجاد می‌کند، زیرا شرط True هرگز False نمی‌شود. کلید کنترل این حلقه، استفاده از دستور break در یک شرط داخلی (if) است.

الگوی عمومی
while True:
    # 1. کدی که می‌خواهید به طور مداوم تکرار شود
    #    (مثلاً دریافت ورودی، پردازش داده، به‌روزرسانی صفحه)
    
    # 2. بررسی یک شرط برای خروج
    if condition_for_exit:
        break  # 3. شکستن حلقه و خروج

    # 4. کدهای دیگری که ممکن است در هر دور اجرا شوند

فکر کنید یک ربات دارید که می‌خواهید تا زمانی که به او دستور "خاموش" نداده‌اید، به کارش ادامه دهد. شرط حلقه while True می‌گوید "همیشه روشن باش" و دستور if user_command == "خاموش": break مکانیزم توقف آن است.

مثال کاربردی: ساخت یک منوی ساده

فرض کنید می‌خواهیم برنامه‌ای بنویسیم که یک منو را به کاربر نمایش می‌دهد و منتظر دستور او می‌ماند. این برنامه باید تا زمانی که کاربر صراحتاً گزینه "خروج" را انتخاب نکرده، به کار خود ادامه دهد.

while True:
    print("\n--- منوی اصلی ---")
    print("1. نمایش پیام خوشامدگویی")
    print("2. محاسبه توان دو یک عدد")
    print("q. خروج از برنامه")

    choice = input("لطفا گزینه خود را انتخاب کنید: ")

    if choice == '1':
        print("\n*** سلام! به برنامه ما خوش آمدید. ***")
    elif choice == '2':
        num_str = input("عددی را برای محاسبه توان دو وارد کنید: ")
        if num_str.isdigit():
            num = int(num_str)
            print(f"\n*** توان دوی عدد {num} برابر است با {num ** 2} ***")
        else:
            print("\n*** ورودی نامعتبر! لطفاً فقط عدد وارد کنید. ***")
    elif choice.lower() == 'q':
        print("\n*** از برنامه خارج شدید. خدانگهدار! ***")
        break  # نقطه خروج اصلی از حلقه
    else:
        print("\n*** گزینه نامعتبر! لطفا دوباره تلاش کنید. ***")

یک نمونه از اجرای برنامه:

--- منوی اصلی ---
1. نمایش پیام خوشامدگویی
2. محاسبه توان دو یک عدد
q. خروج از برنامه
لطفا گزینه خود را انتخاب کنید: 1

*** سلام! به برنامه ما خوش آمدید. ***

--- منوی اصلی ---
1. نمایش پیام خوشامدگویی
2. محاسبه توان دو یک عدد
q. خروج از برنامه
لطفا گزینه خود را انتخاب کنید: 2
عددی را برای محاسبه توان دو وارد کنید: 5

*** توان دوی عدد 5 برابر است با 25 ***

--- منوی اصلی ---
1. نمایش پیام خوشامدگویی
2. محاسبه توان دو یک عدد
q. خروج از برنامه
لطفا گزینه خود را انتخاب کنید: q

*** از برنامه خارج شدید. خدانگهدار! ***

چرا این الگو اینقدر مفید است؟

شاید بپرسید چرا از یک متغیر "پرچم" (flag) استفاده نکنیم؟ مثلاً:

روش سنتی (با متغیر پرچم)
running = True
while running:
    choice = input("> ")
    if choice == 'q':
        running = False
    else:
        # process other choices
        pass
روش مدرن (با `while True`)
while True:
    choice = input("> ")
    if choice == 'q':
        break
    else:
        # process other choices
        pass

هر دو روش کار می‌کنند، اما الگوی while True چندین مزیت دارد:

  • خوانایی و صراحت (Clarity): نیت شما کاملاً روشن است. while True یعنی "این حلقه قرار است برای همیشه اجرا شود" و if ... break دقیقاً نشان می‌دهد که نقطه خروج کجاست.
  • انعطاف‌پذیری (Flexibility): شما می‌توانید چندین شرط break در نقاط مختلف حلقه داشته باشید. این کار مدیریت شرط‌های خروج پیچیده را بسیار آسان‌تر می‌کند.
  • کد کمتر (Less Boilerplate): شما نیازی به تعریف و مدیریت یک متغیر پرچم اضافی (مانند `running`) ندارید. کد شما کوتاه‌تر و مستقیم‌تر می‌شود.

نکات کلیدی

  • الگوی while True برای ساخت حلقه‌هایی است که تا زمان وقوع یک رویداد خاص باید ادامه یابند.
  • این الگو همیشه به یک یا چند دستور break برای خروج نیاز دارد، در غیر این صورت به یک حلقه بی‌نهایت واقعی تبدیل می‌شود.
  • این روش اغلب خواناتر و انعطاف‌پذیرتر از استفاده از متغیرهای پرچم برای کنترل حلقه است.
  • این الگو را در منوهای خط فرمان، حلقه‌های اصلی بازی، و برنامه‌هایی که منتظر ورودی دائمی هستند (مانند سرورها) به وفور خواهید دید.

تا به حال دستوراتی را یاد گرفته‌ایم که کاری انجام می‌دهند: چاپ می‌کنند، محاسبه می‌کنند، یا جریان برنامه را کنترل می‌کنند. اما پایتون دستوری دارد که کارش دقیقاً "هیچ کاری نکردن" است: دستور pass.

شاید عجیب به نظر برسد، اما `pass` یک ابزار بسیار مفید برای برنامه‌نویسان است. این دستور به ما اجازه می‌دهد تا ساختار کد خود را (مانند توابع، کلاس‌ها یا شرط‌ها) مشخص کنیم بدون اینکه هنوز منطق داخلی آن‌ها را پیاده‌سازی کرده باشیم. `pass` مانند یک یادداشت "بعداً اینجا را کامل می‌کنم" برای مفسر پایتون عمل می‌کند.


چرا به `pass` نیاز داریم؟ قانون تورفتگی (Indentation)

در پایتون، بلوک‌های کد (بدنه حلقه‌ها، شرط‌ها، توابع و کلاس‌ها) با تورفتگی مشخص می‌شوند. مفسر پایتون انتظار دارد که بعد از یک دستورِ شروع‌کننده بلوک (مانند if یا def که به : ختم می‌شود)، حداقل یک خط کد تورفته وجود داشته باشد. اگر این بلوک خالی باشد، با خطا مواجه خواهید شد.

کد اشتباه (ایجاد خطا)
age = 20
if age > 18:
    # می‌خواهم بعداً برای این شرط کد بنویسم، فعلاً خالی می‌گذارم
else:
    print("شما مجاز نیستید.")

# این کد خطا می‌دهد: IndentationError: expected an indented block

اینجاست که `pass` به کمک ما می‌آید. `pass` یک دستور معتبر پایتون است که هیچ عملیاتی انجام نمی‌دهد و صرفاً جای یک بلوک کد را پر می‌کند تا از خطای سینتکسی جلوگیری شود.

کد صحیح (با استفاده از `pass`)
age = 20
if age > 18:
    pass # عالی! حالا کد بدون خطا اجرا می‌شود. بعداً برمی‌گردم و منطق را اضافه می‌کنم.
else:
    print("شما مجاز نیستید.")

کاربردهای رایج دستور `pass`

استفاده از `pass` یک تکنیک استاندارد در طول فرآیند توسعه نرم‌افزار است.

۱. طراحی ساختار توابع و کلاس‌ها

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

# تعریف ساختار کلی برنامه بدون پیاده‌سازی جزئیات

def connect_to_database(db_name):
    # TODO: کد اتصال به دیتابیس را اینجا بنویس
    pass

def get_user_data(user_id):
    # TODO: منطق دریافت اطلاعات کاربر
    pass

class UserProfile:
    # TODO: تعریف مشخصات و متدهای کاربر
    pass

استفاده از کامنت‌هایی مانند # TODO در کنار pass یک روش بسیار خوب برای یادآوری کارهایی است که باید در آینده انجام دهید.

۲. جای خالی در شرط‌ها و حلقه‌ها

گاهی در یک زنجیره if/elif/else، می‌دانید که باید یک شرط خاص را در نظر بگیرید، اما هنوز نمی‌خواهید برای آن کدی بنویسید.

user_role = "guest"

if user_role == "admin":
    # کدهای مربوط به ادمین
    print("دسترسی کامل")
elif user_role == "editor":
    # هنوز برای نقش ویرایشگر تصمیمی نگرفته‌ایم
    pass
else:
    print("دسترسی محدود")
۳. نادیده گرفتن استثناها (با احتیاط!)

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

هشدار: استفاده از pass در بلوک except می‌تواند خطرناک باشد، زیرا باعث می‌شود خطاها به صورت خاموش نادیده گرفته شوند. این کار را فقط زمانی انجام دهید که ۱۰۰٪ مطمئن هستید نادیده گرفتن آن خطا مشکلی ایجاد نمی‌کند.
try:
    # تلاش برای حذف فایلی که ممکن است وجود نداشته باشد
    import os
    os.remove("temporary_file.tmp")
except FileNotFoundError:
    # اگر فایل وجود نداشت، اشکالی ندارد. هیچ کاری نکن.
    pass

تفاوت کلیدی: `pass` در مقابل `continue`

یک اشتباه رایج، یکی دانستن `pass` و `continue` است. این دو دستور کاملاً متفاوت هستند:

  • pass: یک پرکننده جای خالی است. هیچ کاری نمی‌کند و مفسر به سادگی به خط بعدی کد در همان بلوک می‌رود.
  • continue: فقط در حلقه‌ها (`for` و `while`) معنا دارد. اجرای دور فعلی حلقه را متوقف کرده و به ابتدای حلقه می‌پرد تا دور بعدی را شروع کند.
مثال مقایسه‌ای

بیایید اعداد ۰ تا ۴ را پیمایش کنیم و ببینیم این دو دستور چه تأثیری روی عدد ۲ دارند.

استفاده از `pass`

for i in range(5):
    if i == 2:
        pass # هیچ اتفاقی نمی‌افتد
    print(i)
0
1
2
3
4

همانطور که می‌بینید، `pass` هیچ تأثیری در روند اجرای حلقه نداشت و عدد ۲ نیز چاپ شد.

استفاده از `continue`

for i in range(5):
    if i == 2:
        continue # به ابتدای حلقه بپر
    print(i)
0
1
3
4

در اینجا، وقتی `i` برابر ۲ بود، `continue` باعث شد دستور `print(i)` در آن دور نادیده گرفته شود و حلقه مستقیماً به سراغ دور بعدی (با `i = 3`) برود.

وقت آن است که تمام چیزهایی که یاد گرفته‌ایم را در یک پروژه سرگرم‌کننده و تعاملی به کار ببریم! در این تمرین، یک بازی کلاسیک "حدس اعداد" می‌سازیم.

این برنامه یک عدد تصادفی در ذهن خود انتخاب می‌کند و شما باید با راهنمایی‌های "بالاتر" و "پایین‌تر" آن عدد را پیدا کنید. این تمرین به طور کامل از مفاهیم while True، input()، تبدیل نوع (Type Casting)، if/elif/else و break استفاده می‌کند.


نگاهی به آینده: استفاده از کتابخانه‌ها

برای اینکه کامپیوتر بتواند یک عدد "تصادفی" انتخاب کند، ما به ابزاری نیاز داریم که در خود پایتون تعبیه شده است. این ابزار در یک "کتابخانه" (Library) به نام random قرار دارد.

ما با نوشتن import random در ابتدای برنامه، به پایتون می‌گوییم که می‌خواهیم از ابزارهای این کتابخانه استفاده کنیم. سپس با دستور random.randint(a, b) از آن می‌خواهیم که یک عدد صحیح تصادفی بین a و b (شامل خودشان) به ما بدهد.

نگران نباشید! در درس بعدی به طور مفصل درباره کتابخانه‌ها، نحوه وارد کردن (import) و نصب آن‌ها صحبت خواهیم کرد. فعلاً فقط این دو خط را به عنوان یک "جادو" برای تولید عدد تصادفی بپذیرید.

کد کامل بازی

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

# 1. وارد کردن کتابخانه مورد نیاز برای تولید عدد تصادفی
import random

# 2. تولید یک عدد صحیح تصادفی بین 1 و 100 و ذخیره آن
# این کار فقط یک بار و قبل از شروع حلقه انجام می‌شود
secret_number = random.randint(1, 100)
guess_count = 0

print("به بازی حدس اعداد خوش آمدید!")
print("من یک عدد بین 1 تا 100 انتخاب کرده‌ام. آیا می‌توانی آن را حدس بزنی؟")

# 3. استفاده از الگوی while True برای تکرار بازی تا زمان حدس درست
while True:
    # 4. دریافت حدس از کاربر به صورت رشته
    user_guess_str = input("\nحدس خود را وارد کنید: ")
    guess_count += 1 # افزایش شمارنده حدس‌ها

    # 5. اعتبارسنجی ورودی: آیا ورودی کاربر فقط از اعداد تشکیل شده؟
    if user_guess_str.isdigit():
        # اگر بله، آن را به عدد صحیح تبدیل کن
        user_guess_num = int(user_guess_str)
    else:
        # اگر نه، یک پیام خطا بده و با continue به ابتدای حلقه برگرد
        print("خطا: لطفاً فقط عدد وارد کنید!")
        continue

    # 6. مقایسه حدس کاربر با عدد مخفی
    if user_guess_num < secret_number:
        print("عدد من بزرگتر است! دوباره تلاش کن.")
    elif user_guess_num > secret_number:
        print("عدد من کوچکتر است! دوباره تلاش کن.")
    else:
        # اگر حدس درست بود
        print(f"🎉 تبریک می‌گویم! شما برنده شدید! عدد مخفی {secret_number} بود.")
        print(f"شما در {guess_count} تلاش موفق شدید.")
        break # 7. شکستن حلقه و پایان بازی

یک نمونه از اجرای بازی:

به بازی حدس اعداد خوش آمدید!
من یک عدد بین 1 تا 100 انتخاب کرده‌ام. آیا می‌توانی آن را حدس بزنی؟

حدس خود را وارد کنید: 50
عدد من بزرگتر است! دوباره تلاش کن.

حدس خود را وارد کنید: 75
عدد من کوچکتر است! دوباره تلاش کن.

حدس خود را وارد کنید: 60
عدد من بزرگتر است! دوباره تلاش کن.

حدس خود را وارد کنید: 68
عدد من کوچکتر است! دوباره تلاش کن.

حدس خود را وارد کنید: 65
🎉 تبریک می‌گویم! شما برنده شدید! عدد مخفی 65 بود.
شما در 5 تلاش موفق شدید.

تجزیه و تحلیل کد

  • مرحله ۱ و ۲: با import random ابزار لازم را فراهم کرده و با random.randint(1, 100) یک عدد مخفی فقط برای یک بار قبل از شروع بازی ایجاد می‌کنیم. متغیر `guess_count` هم برای شمردن تعداد تلاش‌ها صفر می‌شود.
  • مرحله ۳: حلقه while True بازی را در یک وضعیت تکرار مداوم قرار می‌دهد. این حلقه تنها زمانی متوقف می‌شود که دستور break اجرا شود.
  • مرحله ۵: این بخش بسیار حیاتی است. چون input() همیشه رشته برمی‌گرداند، ما نمی‌توانیم مستقیماً آن را با عدد مقایسه کنیم. ابتدا با متد .isdigit() چک می‌کنیم که آیا رشته ورودی شامل کاراکترهای عددی است یا خیر. اگر نبود، با continue از ادامه اجرای کد در این دور صرف‌نظر کرده و به ابتدای حلقه می‌پریم تا دوباره از کاربر ورودی بگیریم.
  • مرحله ۶: این قلب تپنده منطق بازی است. با یک زنجیره if/elif/else، حدس کاربر را با عدد مخفی مقایسه کرده و پیام مناسب را چاپ می‌کنیم.
  • مرحله ۷: اگر کاربر عدد را درست حدس بزند (بلوک else)، پیام موفقیت‌آمیز چاپ شده و دستور break حلقه while را می‌شکند و برنامه به پایان می‌رسد.
چالش برای شما

سعی کنید برنامه را طوری تغییر دهید که:

  1. تعداد تلاش‌های کاربر محدود باشد (مثلاً ۱۰ تلاش). اگر کاربر در این تعداد تلاش موفق نشد، بازنده شود و عدد مخفی به او نمایش داده شود. (راهنمایی: می‌توانید از یک حلقه for با range(10) استفاده کنید یا شمارنده حدس را درون حلقه while چک کنید).
  2. بعد از پایان بازی (چه برد و چه باخت)، از کاربر بپرسد که آیا می‌خواهد دوباره بازی کند یا خیر.

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

هدف بازی ساده است: کامپیوتر یک کلمه را به صورت تصادفی از یک لیست انتخاب می‌کند و شما باید با حدس زدن حروف، آن کلمه را پیدا کنید. شما تعداد محدودی "جان" برای حدس‌های اشتباه دارید.


مفاهیم اصلی در این بازی

  • انتخاب کلمه تصادفی: ما یک لیست از کلمات تعریف می‌کنیم و با استفاده از تابع random.choice() یک کلمه را به صورت تصادفی از آن انتخاب می‌کنیم.
  • نمایش وضعیت کلمه: برای نمایش کلمه به کاربر، از یک لیست از خط تیره (_) استفاده می‌کنیم. هر بار که کاربر یک حرف درست را حدس بزند، خط تیره مربوطه با آن حرف جایگزین می‌شود.
  • مدیریت جان‌ها (Lives): یک متغیر برای تعداد تلاش‌های باقی‌مانده در نظر می‌گیریم. با هر حدس اشتباه، یکی از جان‌ها کم می‌شود.
  • حلقه اصلی بازی: بازی تا زمانی که کاربر کلمه را پیدا کند یا جان‌هایش تمام شود، در یک حلقه while ادامه می‌یابد.

کد کامل بازی

این کد شامل تمام منطق‌های مورد نیاز برای اجرای بازی است. آن را با دقت بررسی کنید.

import random

# 1. تنظیمات اولیه بازی
words = ["پایتون", "برنامه", "کامپیوتر", "توسعه", "کتابخانه", "متغیر"]
secret_word = random.choice(words)
lives = 6
guessed_letters = []
# ایجاد لیستی از خط تیره به طول کلمه مخفی
display_word = ["_" for _ in secret_word]

print("به بازی حدس کلمه خوش آمدید!")
print("کلمه مورد نظر", len(secret_word), "حرف دارد.")

# 2. حلقه اصلی بازی
while lives > 0 and "_" in display_word:
    # 3. نمایش وضعیت فعلی به کاربر
    print("\n" + " ".join(display_word))
    print(f"حروف حدس زده شده: {', '.join(guessed_letters)}")
    print(f"شما {lives} جان دیگر دارید.")

    # 4. دریافت حدس از کاربر و اعتبارسنجی
    guess = input("یک حرف حدس بزنید: ").lower()

    if len(guess) != 1 or not guess.isalpha():
        print("ورودی نامعتبر است. لطفاً فقط یک حرف وارد کنید.")
        continue

    if guess in guessed_letters:
        print("شما این حرف را قبلاً حدس زده‌اید. یک حرف دیگر را امتحان کنید.")
        continue
    
    # اضافه کردن حرف جدید به لیست حروف حدس زده شده
    guessed_letters.append(guess)

    # 5. بررسی وجود حرف در کلمه مخفی
    if guess in secret_word:
        print(f"آفرین! حرف '{guess}' در کلمه وجود دارد.")
        # جایگزینی خط تیره با حرف صحیح در تمام موقعیت‌ها
        for index, letter in enumerate(secret_word):
            if letter == guess:
                display_word[index] = guess
    else:
        print(f"متأسفم، حرف '{guess}' در کلمه نیست.")
        lives -= 1 # کاهش یک جان

# 6. پایان بازی: بررسی شرط برد یا باخت
if "_" not in display_word:
    print(f"\n🎉 تبریک! شما برنده شدید. کلمه صحیح '{secret_word}' بود.")
else:
    print(f"\n💥 بازی تمام شد! شما باختید. کلمه صحیح '{secret_word}' بود.")

یک نمونه از اجرای بازی:

به بازی حدس کلمه خوش آمدید!
کلمه مورد نظر 6 حرف دارد.

_ _ _ _ _ _
حروف حدس زده شده: 
شما 6 جان دیگر دارید.
یک حرف حدس بزنید: ب

آفرین! حرف 'ب' در کلمه وجود دارد.

_ _ _ ب _ _
حروف حدس زده شده: ب
شما 6 جان دیگر دارید.
یک حرف حدس بزنید: ر
متأسفم، حرف 'ر' در کلمه نیست.

_ _ _ ب _ _
حروف حدس زده شده: ب, ر
شما 5 جان دیگر دارید.
یک حرف حدس بزنید: ه
متأسفم، حرف 'ه' در کلمه نیست.
...
🎉 تبریک! شما برنده شدید. کلمه صحیح 'کتابخانه' بود.

تجزیه و تحلیل کد

  • display_word = ["_" for _ in secret_word]: این یک تکنیک پایتونی به نام "List Comprehension" است. این کد یک لیست می‌سازد که به ازای هر حرف در secret_word، یک آیتم "_" در آن قرار می‌دهد. راه ساده‌تر آن استفاده از یک حلقه for است، اما این روش خلاصه‌تر است.
  • " ".join(display_word): این متد، تمام اعضای لیست display_word را به هم می‌چسباند و بین آن‌ها یک فاصله (" ") قرار می‌دهد. این کار برای نمایش زیباتر وضعیت کلمه به کاربر است.
  • while lives > 0 and "_" in display_word: شرط حلقه دو بخشی است. بازی تا زمانی ادامه پیدا می‌کند که کاربر هم جان داشته باشد (lives > 0) و هم هنوز جای خالی ("_") در کلمه نمایشی وجود داشته باشد.
  • enumerate(secret_word): این تابع قدرتمند در پایتون، هنگام پیمایش یک لیست یا رشته، هم اندیس (شماره موقعیت) و هم مقدار هر آیتم را به ما می‌دهد. ما از این ویژگی برای پیدا کردن تمام موقعیت‌های حرف حدس زده شده و به‌روزرسانی display_word استفاده می‌کنیم.
  • بررسی برد/باخت: پس از اتمام حلقه، با بررسی اینکه آیا هنوز "_" در display_word وجود دارد یا نه، می‌توانیم بفهمیم که کاربر برده (هیچ "_" باقی نمانده) یا باخته است.
چالش برای شما

برای بهتر کردن این بازی، می‌توانید موارد زیر را امتحان کنید:

  1. لیست کلمات را بسیار بزرگ‌تر کنید.
  2. یک طرح گرافیکی ساده با کاراکترها (ASCII art) برای نمایش آدمک دار زده شده (Hangman) اضافه کنید که با هر حدس اشتباه، یک بخش از آن کشیده شود.
  3. سطح سختی به بازی اضافه کنید (مثلاً کلمات طولانی‌تر و جان‌های کمتر برای سطح سخت).

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

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


کتابخانه (Library) چیست و PyPI کجاست؟

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

  • کتابخانه (Library) یا پکیج (Package): مجموعه‌ای از کدها، توابع و ابزارهای از پیش نوشته شده است که برای حل یک مشکل خاص طراحی شده‌اند. به جای اینکه ما چرخ را از اول اختراع کنیم، از این ابزارهای آماده استفاده می‌کنیم. برای مثال، کتابخانه‌ای برای کار با داده‌های علمی، کتابخانه‌ای برای ساخت وب‌سایت، کتابخانه‌ای برای تحلیل تصویر و... وجود دارد.

  • PyPI - Python Package Index: این همان "فروشگاه ابزارآلات" ماست! PyPI مخزن رسمی و مرکزی تمام کتابخانه‌های عمومی پایتون است. این یک وب‌سایت عظیم است که به عنوان مرجع اصلی برای پیدا کردن، دانلود و نصب پکیج‌های پایتون عمل می‌کند.
تصویری از وب‌سایت PyPI
وب‌سایت رسمی PyPI، مرجع اصلی پکیج‌های پایتون. می‌توانید آن را در pypi.org مشاهده کنید.

ابزار نصب: `pip`

پایتون یک ابزار مدیریت پکیج داخلی به نام `pip` (مخفف Pip Installs Packages) دارد که معمولاً همراه با خود پایتون نصب می‌شود. `pip` مسئول اتصال به PyPI، دانلود پکیج مورد نظر شما و نصب صحیح آن بر روی سیستم شماست.

ساختار استاندارد دستور نصب بسیار ساده است:

pip install نام-پکیج

برای مثال، برای نصب کتابخانه محبوب numpy که برای محاسبات عددی استفاده می‌شود، دستور زیر را در ترمینال یا Command Prompt اجرا می‌کنیم:

pip install numpy

چالش دسترسی و راه‌حل: استفاده از میرور (Mirror)

گاهی اوقات، دسترسی مستقیم به سرورهای اصلی PyPI که در خارج از کشور قرار دارند، ممکن است با کندی یا قطعی همراه باشد. اینجاست که "میرورها" یا "آینه‌ها" به کمک ما می‌آیند.

یک میرور، یک کپی کامل یا بخشی از داده‌های PyPI است که بر روی یک سرور داخلی (در ایران) میزبانی می‌شود. استفاده از میرور دو مزیت بزرگ دارد:

  1. سرعت بسیار بالاتر: چون سرور در داخل کشور است، دانلود پکیج‌ها با سرعت بسیار بیشتری انجام می‌شود.
  2. پایداری و قابلیت اطمینان: این سرورها تحت تأثیر محدودیت‌های اینترنتی بین‌المللی قرار نمی‌گیرند و دسترسی پایدارتری را فراهم می‌کنند.

راهنمای ویدیویی

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

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

نحوه نصب با استفاده از میرور

برای اینکه به `pip` بگوییم به جای سرور اصلی PyPI از یک میرور استفاده کند، از یک فلگ (flag) به نام -i یا --index-url در دستور خود استفاده می‌کنیم. در ادامه چند نمونه از میرورهای ایرانی معتبر و دستور نصب با آن‌ها آمده است:

میرور Runflare
pip install -i https://mirror-pypi.runflare.com/simple نام-پکیج
میرور ابرآروان (Chabokan سابق)
pip install --index-url https://mirror2.chabokan.net/pypi/simple/ نام-پکیج
میرور لیارا
pip install -i https://package-mirror.liara.ir/repository/pypi/simple نام-پکیج

مثال‌های عملی

فرض کنید می‌خواهیم چند کتابخانه معروف را با استفاده از میرور Runflare نصب کنیم:

  • نصب `numpy` (محاسبات علمی):
    pip install -i https://mirror-pypi.runflare.com/simple numpy
  • نصب `pandas` (تحلیل داده):
    pip install -i https://mirror-pypi.runflare.com/simple pandas
  • نصب `flask` (ساخت وب اپلیکیشن‌های ساده):
    pip install -i https://mirror-pypi.runflare.com/simple flask
  • نصب `django` (ساخت وب اپلیکیشن‌های پیشرفته):
    pip install -i https://mirror-pypi.runflare.com/simple django
پس از نصب موفقیت‌آمیز، می‌توانید به سادگی با دستور import در فایل پایتون خود، از کتابخانه نصب شده استفاده کنید. مثلا: import numpy.

توابع، ستون فقرات برنامه‌نویسی ساختاریافته هستند. آن‌ها مانند دستورالعمل‌های یک‌بار نوشته‌شده‌ای هستند که می‌توانیم هر زمان که به آن‌ها نیاز داشتیم، با نامشان آن‌ها را "صدا بزنیم" یا "فراخوانی کنیم".

استفاده از توابع به ما کمک می‌کند تا از اصل مهم DRY (Don't Repeat Yourself) یا "خودت را تکرار نکن" پیروی کنیم. به جای نوشتن یک قطعه کد تکراری در چندین جای برنامه، آن را یک بار درون یک تابع می‌نویسیم و بارها از آن استفاده می‌کنیم.


۱. تعریف و فراخوانی یک تابع ساده

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

# تعریف یک تابع ساده
def greet():
    print("سلام! به دوره پایتون خوش آمدید.")
    print("امیدوارم از یادگیری لذت ببرید.")

# فراخوانی یا اجرای تابع
print("برنامه شروع شد...")
greet()
print("تابع اجرا شد.")
greet() # می‌توانیم تابع را هر چند بار که بخواهیم فراخوانی کنیم
print("برنامه تمام شد.")
برنامه شروع شد...
سلام! به دوره پایتون خوش آمدید.
امیدوارم از یادگیری لذت ببرید.
تابع اجرا شد.
سلام! به دوره پایتون خوش آمدید.
امیدوارم از یادگیری لذت ببرید.
برنامه تمام شد.

۲. ارسال اطلاعات به تابع (آرگومان‌ها)

اغلب اوقات، می‌خواهیم اطلاعاتی را به تابع ارسال کنیم تا بر اساس آن اطلاعات، کاری را انجام دهد. به این اطلاعات ورودی آرگومان (Argument) گفته می‌شود. متغیرهایی که در تعریف تابع این اطلاعات را دریافت می‌کنند، پارامتر (Parameter) نام دارند.

تابع با یک آرگومان
# 'name' یک پارامتر است
def greet_user(name):
    print(f"سلام، {name} عزیز!")

# "علی" و "مریم" آرگومان‌هایی هستند که به تابع ارسال می‌شوند
greet_user("علی")
greet_user("مریم")
سلام، علی عزیز!
سلام، مریم عزیز!
تابع با چند آرگومان

می‌توانید هر تعداد آرگومان که نیاز دارید به تابع ارسال کنید. ترتیب ارسال آرگومان‌ها مهم است.

# این تابع سه پارامتر دارد: نام، نام خانوادگی و سن
def show_user_info(first_name, last_name, age):
    print(f"نام: {first_name}")
    print(f"نام خانوادگی: {last_name}")
    print(f"سن: {age}")

show_user_info("محمدحسین", "سلیمانی", 30)
نام: محمدحسین
نام خانوادگی: سلیمانی
سن: 30

۳. مقدار پیش‌فرض برای پارامترها

می‌توانیم برای پارامترهای یک تابع، یک مقدار پیش‌فرض تعیین کنیم. اگر هنگام فراخوانی تابع، آرگومانی برای آن پارامتر ارسال نشود، از مقدار پیش‌فرض استفاده خواهد شد. این کار باعث می‌شود برخی آرگومان‌ها "اختیاری" شوند.

def describe_pet(pet_name, animal_type="سگ"):
    print(f"من یک حیوان خانگی به نام {pet_name} دارم.")
    print(f"او یک {animal_type} است.")

# فراخوانی بدون ارسال آرگومان برای animal_type (از مقدار پیش‌فرض استفاده می‌شود)
describe_pet("رکسی")
print("-" * 20)
# فراخوانی با ارسال آرگومان برای animal_type (مقدار پیش‌فرض نادیده گرفته می‌شود)
describe_pet("برفی", "گربه")
من یک حیوان خانگی به نام رکسی دارم.
او یک سگ است.
--------------------
من یک حیوان خانگی به نام برفی دارم.
او یک گربه است.

۴. ارسال لیست و دریافت خروجی (Return)

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

# این تابع یک لیست از اعداد را می‌گیرد و بزرگترین عدد را بازمی‌گرداند
def find_max(numbers):
    if not numbers: # اگر لیست خالی باشد
        return None # مقدار None را بازمی‌گرداند

    maximum = numbers[0]
    for number in numbers:
        if number > maximum:
            maximum = number
    return maximum

my_scores = [88, 95, 72, 100, 91]
# فراخوانی تابع و ذخیره مقدار بازگشتی در یک متغیر
highest_score = find_max(my_scores) 

print(f"لیست نمرات من: {my_scores}")
print(f"بالاترین نمره من {highest_score} است.")

# یک مثال دیگر
empty_list = []
result = find_max(empty_list)
print(f"بزرگترین عدد در لیست خالی: {result}")
لیست نمرات من: [88, 95, 72, 100, 91]
بالاترین نمره من 100 است.
بزرگترین عدد در لیست خالی: None
نکته مهم: به محض اینکه پایتون به دستور return می‌رسد، اجرای تابع متوقف شده و مقدار مشخص شده بازگردانده می‌شود. هر کدی که پس از دستور return در تابع نوشته شود، اجرا نخواهد شد.

۵. دستور `pass` در توابع

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

def calculate_taxes():
    # TODO: منطق محاسبه مالیات بعدا در اینجا نوشته خواهد شد
    pass

def send_email_report():
    # منطق ارسال ایمیل هنوز پیاده‌سازی نشده است
    pass

# برنامه بدون خطا اجرا می‌شود
print("در حال طراحی ساختار برنامه...")
calculate_taxes() # این تابع هیچ کاری انجام نمی‌دهد
send_email_report() # این تابع هم همینطور
print("طراحی اولیه کامل شد.")

۶. مستندسازی توابع (Docstrings)

یک تمرین حرفه‌ای و بسیار خوب این است که برای توابع خود مستندات بنویسید. این کار با استفاده از یک رشته چندخطی (محصور در سه علامت نقل قول """) درست بعد از خط تعریف تابع انجام می‌شود. این رشته به عنوان Docstring شناخته می‌شود و توضیح می‌دهد که تابع چه کاری انجام می‌دهد.

def square(number):
    """
    این تابع یک عدد را به عنوان ورودی دریافت کرده
    و توان دوم (مجذور) آن را بازمی‌گرداند.
    """
    return number * number

# می‌توانیم با استفاده از تابع help() این مستندات را مشاهده کنیم
help(square)
Help on function square in module __main__:

square(number)
    این تابع یک عدد را به عنوان ورودی دریافت کرده
    و توان دوم (مجذور) آن را بازمی‌گرداند.

تا به حال تمام کدهایمان را در یک فایل می‌نوشتیم. اما وقتی برنامه‌ها بزرگ و پیچیده می‌شوند، نگهداری همه چیز در یک فایل بسیار دشوار خواهد بود. ماژول‌ها راه‌حل پایتون برای این مشکل هستند.

یک ماژول در ساده‌ترین حالت، یک فایل پایتون (با پسوند .py) است که شامل تعاریف و دستورات پایتون (توابع، متغیرها، کلاس‌ها و...) می‌باشد. ما می‌توانیم این فایل را در فایل دیگری "وارد" (import) کرده و از محتویات آن استفاده کنیم. این کار به سازماندهی و استفاده مجدد از کد کمک شایانی می‌کند.

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


۱. چگونه یک ماژول بسازیم؟

ساختن یک ماژول بسیار ساده است. کافیست کدهای پایتون خود را در یک فایل با پسوند .py ذخیره کنید. بیایید یک ماژول به نام mymodule.py بسازیم که شامل یک تابع و یک دیکشنری است.

مرحله ۱: یک فایل جدید ایجاد کرده و کد زیر را در آن ذخیره کنید. نام فایل را mymodule.py بگذارید.

# file: mymodule.py

def greeting(name):
    print(f"سلام، {name}!")

person1 = {
    "name": "سارا",
    "age": 28,
    "country": "ایران"
}
تبریک! شما اولین ماژول خود را ساختید. این فایل به تنهایی قابل اجراست، اما قدرت اصلی آن زمانی مشخص می‌شود که در فایل دیگری از آن استفاده کنیم.

۲. چگونه از یک ماژول استفاده کنیم؟

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

مرحله ۲: یک فایل جدید دیگر در همان پوشه (directory) که فایل mymodule.py را ذخیره کردید، ایجاد کنید. نام این فایل را main.py بگذارید و کد زیر را در آن بنویسید.

نکته مهم: برای اینکه پایتون بتواند ماژول شما را پیدا کند، هر دو فایل (mymodule.py و main.py) باید در یک پوشه قرار داشته باشند.
# file: main.py

# وارد کردن ماژول mymodule
import mymodule

# استفاده از تابع و دیکشنری داخل ماژول
# توجه کنید که باید نام ماژول را قبل از نام تابع/متغیر بیاوریم
mymodule.greeting("رضا")

age = mymodule.person1["age"]
print(f"سن شخص مورد نظر {age} سال است.")

مرحله ۳: فایل main.py را اجرا کنید. خروجی به شکل زیر خواهد بود:

سلام، رضا!
سن شخص مورد نظر 28 سال است.

همانطور که می‌بینید، با استفاده از نقطه (dot notation)، توانستیم به تابع greeting و دیکشنری person1 که در ماژول mymodule تعریف شده بودند، دسترسی پیدا کنیم.

۳. تغییر نام ماژول با کلمه کلیدی `as`

شما می‌توانید با استفاده از کلمه کلیدی as، یک نام مستعار یا کوتاه‌تر برای ماژول وارد شده انتخاب کنید. این کار به خصوص برای ماژول‌هایی با نام‌های طولانی بسیار مفید است.

# file: main.py (نسخه جدید)

import mymodule as mx

mx.greeting("شیوا")

country = mx.person1["country"]
print(f"کشور: {country}")
سلام، شیوا!
کشور: ایران

۴. ماژول‌های داخلی پایتون (Built-in)

پایتون دارای تعداد زیادی ماژول داخلی است که توابع و ابزارهای بسیار مفیدی را ارائه می‌دهند و شما نیازی به ساختن آن‌ها ندارید. قبلاً با ماژول random در بازی حدس اعداد آشنا شدیم. ماژول‌های پرکاربرد دیگری مانند math برای عملیات ریاضی پیشرفته و datetime برای کار با تاریخ و زمان وجود دارند.

import math

# استفاده از عدد پی (pi) از ماژول math
x = math.pi
print(f"مقدار عدد پی: {x}")

# استفاده از تابع sqrt (جذر) از ماژول math
y = math.sqrt(64)
print(f"جذر عدد 64 برابر است با: {y}")
مقدار عدد پی: 3.141592653589793
جذر عدد 64 برابر است با: 8.0

۵. وارد کردن بخش‌های خاصی از یک ماژول

گاهی اوقات شما به تمام محتویات یک ماژول نیاز ندارید و فقط می‌خواهید یک تابع یا متغیر خاص را از آن وارد کنید. برای این کار می‌توانید از دستور from ... import استفاده کنید.

مزیت این روش این است که هنگام استفاده از آن تابع یا متغیر، دیگر نیازی به نوشتن نام ماژول ندارید.

# فقط دیکشنری person1 را از ماژول mymodule وارد کن
from mymodule import person1

# دیگر نیازی به نوشتن mymodule.person1 نیست
print(f"نام شخص: {person1['name']}")

# اگر تلاش کنیم از تابع greeting استفاده کنیم، خطا دریافت خواهیم کرد
# چون آن را وارد نکرده‌ایم
# greeting("تست")  # <-- این خط باعث NameError می‌شود
نام شخص: سارا
احتیاط: اگرچه این روش کد را کوتاه‌تر می‌کند، اما اگر از ماژول‌های مختلف، توابعی با نام یکسان وارد کنید، ممکن است باعث سردرگمی و بازنویسی (overwrite) ناخواسته توابع شود.

همچنین می‌توانید هنگام وارد کردن یک بخش خاص، با استفاده از as برای آن نام مستعار انتخاب کنید:

from mymodule import greeting as greet_user

greet_user("دنیا")
سلام، دنیا!

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

پایتون برای مدیریت این "استثناها" یا خطاهای زمان اجرا (Runtime Errors)، یک مکانیزم قدرتمند به نام بلوک try...except ارائه می‌دهد. این ساختار به ما اجازه می‌دهد کدی را که "مشکوک" به ایجاد خطا است، امتحان (Try) کنیم و در صورت بروز خطا، آن را به شیوه‌ای زیبا مدیریت (Except) کنیم، بدون اینکه برنامه از کار بیفتد.


۱. مشکلی که با آن روبرو هستیم

فرض کنید برنامه‌ای نوشته‌ایم که سن کاربر را دریافت کرده و سال تولد او را محاسبه می‌کند. کد زیر را در نظر بگیرید:

age = int(input("لطفا سن خود را وارد کنید: "))
birth_year = 2026 - age
print(f"شما متولد سال {birth_year} هستید.")

این کد تا زمانی که کاربر یک عدد صحیح وارد کند، به درستی کار می‌کند. اما چه اتفاقی می‌افتد اگر کاربر به اشتباه یک رشته (متن) وارد کند؟

لطفا سن خود را وارد کنید: بیست
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'بیست'
همانطور که می‌بینید، برنامه با یک خطای ValueError متوقف می‌شود. اینجاست که try...except به کمک ما می‌آید.

۲. ساختار پایه `try...except`

ساختار اصلی به این صورت است:

  • بلوک `try`: کدی که ممکن است خطا ایجاد کند در این قسمت قرار می‌گیرد.
  • بلوک `except`: اگر در بلوک `try` خطایی رخ دهد، اجرای آن بلوک متوقف شده و کد داخل بلوک `except` اجرا می‌شود. اگر خطایی رخ ندهد، این بلوک نادیده گرفته می‌شود.

حالا بیایید مثال قبلی را بازنویسی کنیم:

try:
    age = int(input("لطفا سن خود را وارد کنید: "))
    birth_year = 2026 - age
    print(f"شما متولد سال {birth_year} هستید.")
except:
    print("خطا! لطفا فقط عدد صحیح وارد کنید.")

print("اجرای برنامه ادامه پیدا کرد...")

اگر این کد را اجرا کرده و یک متن وارد کنید، خروجی به شکل زیر خواهد بود:

لطفا سن خود را وارد کنید: بیست
خطا! لطفا فقط عدد صحیح وارد کنید.
اجرای برنامه ادامه پیدا کرد...
موفق شدیم! برنامه دیگر crash نکرد. به جای آن، یک پیام خطای مناسب به کاربر نمایش داد و به اجرای خود ادامه داد.

۳. مدیریت خطاهای خاص

استفاده از except به تنهایی (که به آن bare except می‌گویند) تمام خطاها را مدیریت می‌کند. این کار معمولاً ایده خوبی نیست، زیرا ممکن است خطاهایی را که انتظارشان را ندارید (و نشانه باگ در برنامه شما هستند) نیز مخفی کند.

روش بهتر این است که نوع خطایی را که انتظار دارید، مشخص کنید. مثلاً در مثال بالا، ما منتظر خطای ValueError هستیم.

try:
    numerator = int(input("عدد صورت کسر را وارد کنید: "))
    denominator = int(input("عدد مخرج کسر را وارد کنید: "))
    result = numerator / denominator
    print(f"نتیجه تقسیم: {result}")
except ValueError:
    print("خطا: لطفا فقط عدد وارد کنید.")
except ZeroDivisionError:
    print("خطا: تقسیم بر صفر ممکن نیست!")

در این کد:

  • اگر کاربر متن وارد کند، بلوک except ValueError اجرا می‌شود.
  • اگر کاربر برای مخرج عدد صفر را وارد کند، بلوک except ZeroDivisionError اجرا می‌شود.

شما همچنین می‌توانید چند نوع خطا را در یک بلوک `except` با استفاده از یک تاپل مدیریت کنید:

try:
    # ... کد مشابه بالا ...
except (ValueError, ZeroDivisionError):
    print("ورودی نامعتبر است یا تقسیم بر صفر انجام شده است.")

۴. بلوک `else` (اجرا در صورت عدم وجود خطا)

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

try:
    age = int(input("لطفا سن خود را وارد کنید: "))
except ValueError:
    print("خطا! لطفا فقط عدد صحیح وارد کنید.")
else:
    # این بخش فقط در صورت موفقیت بلوک try اجرا می‌شود
    birth_year = 2026 - age
    print(f"شما متولد سال {birth_year} هستید.")
چرا این کار مفید است؟ این کار باعث می‌شود کدی که به موفقیت‌آمیز بودن بلوک `try` وابسته است را جدا کنیم و به طور تصادفی خطاهای آن را با همان بلوک `except` مدیریت نکنیم.

۵. بلوک `finally` (اجرا در هر صورت)

بلوک finally یک ویژگی بسیار مهم دارد: کد داخل آن همیشه اجرا می‌شود، چه در بلوک try خطایی رخ بدهد و چه ندهد.

این بلوک معمولاً برای "عملیات پاکسازی" (cleanup actions) مانند بستن یک فایل یا قطع اتصال از یک پایگاه داده استفاده می‌شود، کارهایی که باید تحت هر شرایطی انجام شوند.

try:
    numerator = int(input("عدد صورت کسر را وارد کنید: "))
    denominator = int(input("عدد مخرج کسر را وارد کنید: "))
    result = numerator / denominator
    print(f"نتیجه تقسیم: {result}")
except ZeroDivisionError:
    print("خطا: تقسیم بر صفر ممکن نیست!")
finally:
    # این کد همیشه اجرا می‌شود
    print("محاسبه پایان یافت.")

اگر عدد `0` را برای مخرج وارد کنید، خروجی به این شکل خواهد بود:

عدد صورت کسر را وارد کنید: 10
عدد مخرج کسر را وارد کنید: 0
خطا: تقسیم بر صفر ممکن نیست!
محاسبه پایان یافت.

و اگر ورودی معتبر باشد:

عدد صورت کسر را وارد کنید: 10
عدد مخرج کسر را وارد کنید: 2
نتیجه تقسیم: 5.0
محاسبه پایان یافت.

همانطور که می‌بینید، بلوک `finally` در هر دو حالت اجرا شد.

اکنون زمان آن است که تمام مفاهیمی که در درس‌های اخیر یاد گرفته‌ایم را در یک پروژه کاربردی و سرگرم‌کننده به کار بگیریم. در این تمرین، یک ماشین حساب خط فرمان (Command-Line) می‌سازیم که علاوه بر چهار عمل اصلی، قابلیت‌های مهندسی بیشتری را با استفاده از ماژول داخلی پایتون یعنی math ارائه می‌دهد.

مفاهیمی که در این تمرین به کار می‌روند:
  • حلقه بی‌نهایت هوشمند با while True و break
  • دریافت ورودی از کاربر با input()
  • دستورات شرطی if/elif/else برای انتخاب عملیات
  • وارد کردن و استفاده از ماژول‌ها (import math)
  • مدیریت جامع خطاها با try...except برای جلوگیری از crash برنامه
  • تبدیل نوع داده (Type Casting) به float

۱. منطق و مراحل اجرای برنامه

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

  1. یک حلقه بی‌نهایت ایجاد کند تا کاربر بتواند چندین محاسبه را بدون اجرای مجدد برنامه انجام دهد.
  2. در ابتدای هر حلقه، یک منو از عملیات‌های موجود را به کاربر نمایش دهد.
  3. انتخاب کاربر را دریافت کند.
  4. اگر کاربر گزینه خروج را انتخاب کرد، حلقه را بشکند و برنامه را تمام کند.
  5. در غیر این صورت، یک یا دو عدد (بسته به نوع عملیات) از کاربر دریافت کند.
  6. محاسبه مورد نظر را انجام داده و نتیجه را نمایش دهد.
  7. در تمام مراحل دریافت ورودی عددی، باید از try...except استفاده کنیم تا اگر کاربر ورودی نامعتبر (مثلاً متن) وارد کرد، برنامه متوقف نشود و پیام مناسب نمایش داده شود.
  8. خطای خاص "تقسیم بر صفر" را نیز باید مدیریت کنیم.

۲. کد کامل برنامه

کد زیر یک پیاده‌سازی کامل از ماشین حساب مورد نظر است. آن را با دقت تحلیل کنید.

import math # مرحله ۱: وارد کردن کتابخانه ریاضی

print("ماشین حساب مهندسی خوش آمدید!")

while True:
    # مرحله ۲: نمایش منو در هر بار اجرای حلقه
    print("\nعملیات مورد نظر خود را انتخاب کنید:")
    print("1. جمع (+)")
    print("2. تفریق (-)")
    print("3. ضرب (*)")
    print("4. تقسیم (/)")
    print("5. توان (x^y)")
    print("6. جذر (√x)")
    print("7. سینوس (sin(x))")
    print("8. کسینوس (cos(x))")
    print("9. تانژانت (tan(x))")
    print("10. فاکتوریل (x!)")
    print("0. خروج")

    # مرحله ۳: دریافت انتخاب کاربر
    choice = input("کد عملیات را وارد کنید (0-10): ")

    # مرحله ۴: بررسی شرط خروج
    if choice == '0':
        print("خداحافظ!")
        break # شکستن حلقه و پایان برنامه

    # مرحله ۵: دریافت اعداد با مدیریت خطای کامل
    try:
        # عملیاتی که به دو عدد نیاز دارند
        if choice in ('1', '2', '3', '4', '5'):
            num1 = float(input("عدد اول را وارد کنید: "))
            num2 = float(input("عدد دوم را وارد کنید: "))
        # عملیاتی که به یک عدد نیاز دارند
        elif choice in ('6', '7', '8', '9', '10'):
            num1 = float(input("عدد را وارد کنید: "))
        else:
            print("خطا: انتخاب شما نامعتبر است. لطفا یک عدد بین 0 تا 10 وارد کنید.")
            continue # پرش به ابتدای حلقه

        # مرحله ۶: انجام محاسبات
        if choice == '1':
            result = num1 + num2
            print(f"نتیجه: {num1} + {num2} = {result}")
        elif choice == '2':
            result = num1 - num2
            print(f"نتیجه: {num1} - {num2} = {result}")
        elif choice == '3':
            result = num1 * num2
            print(f"نتیجه: {num1} * {num2} = {result}")
        elif choice == '4':
            # مدیریت خطای تقسیم بر صفر
            if num2 == 0:
                print("خطا: تقسیم بر صفر ممکن نیست!")
            else:
                result = num1 / num2
                print(f"نتیجه: {num1} / {num2} = {result}")
        elif choice == '5':
            result = math.pow(num1, num2)
            print(f"نتیجه: {num1} به توان {num2} = {result}")
        elif choice == '6':
            result = math.sqrt(num1)
            print(f"نتیجه: جذر {num1} = {result}")
        elif choice == '7':
            # توجه: توابع مثلثاتی در پایتون ورودی را بر حسب رادیان می‌گیرند
            result = math.sin(num1)
            print(f"نتیجه: سینوس {num1} (رادیان) = {result}")
        elif choice == '8':
            result = math.cos(num1)
            print(f"نتیجه: کسینوس {num1} (رادیان) = {result}")
        elif choice == '9':
            result = math.tan(num1)
            print(f"نتیجه: تانژانت {num1} (رادیان) = {result}")
        elif choice == '10':
            # فاکتوریل فقط برای اعداد صحیح مثبت تعریف شده است
            if num1 < 0 or num1 != int(num1):
                 print("خطا: فاکتوریل فقط برای اعداد صحیح غیرمنفی تعریف شده است.")
            else:
                result = math.factorial(int(num1))
                print(f"نتیجه: فاکتوریل {int(num1)} = {result}")

    except ValueError:
        print("خطا: ورودی نامعتبر! لطفاً فقط عدد وارد کنید.")
    except Exception as e:
        # گرفتن هر خطای پیش‌بینی نشده دیگر
        print(f"یک خطای غیرمنتظره رخ داد: {e}")

۳. تجزیه و تحلیل کد

  • `import math`: در همان ابتدا، کتابخانه `math` را برای دسترسی به توابع ریاضی پیشرفته مانند pow, sqrt, sin و... وارد کرده‌ایم.
  • بلوک اصلی `try...except`: تقریباً تمام منطق دریافت ورودی و محاسبه درون یک بلوک `try` بزرگ قرار گرفته است. این کار تضمین می‌کند که اگر کاربر در هر مرحله از وارد کردن اعداد، یک مقدار غیرعددی (مثلاً "hello") تایپ کند، برنامه به جای crash کردن، به بلوک except ValueError رفته و پیام خطای مناسب را نمایش می‌دهد.
  • `continue`: دقت کنید که اگر کاربر یک انتخاب نامعتبر برای عملیات داشته باشد (مثلاً عدد 12)، یک پیام خطا چاپ شده و سپس دستور continue اجرا می‌شود. این دستور باعث می‌شود اجرای فعلی حلقه متوقف شده و برنامه مستقیماً به ابتدای حلقه while برگردد تا منو دوباره نمایش داده شود.
  • مدیریت خاص تقسیم بر صفر: برای عملیات تقسیم، قبل از انجام محاسبه، با یک `if` ساده چک می‌کنیم که آیا مخرج (num2) صفر است یا نه. این یک نمونه از مدیریت خطای منطقی قبل از بروز استثنا است.
  • ورودی رادیان: یک نکته مهم در مورد توابع مثلثاتی (sin, cos, tan) در اکثر زبان‌های برنامه‌نویسی این است که زاویه را بر حسب رادیان دریافت می‌کنند، نه درجه. ما این نکته را در پیام خروجی به کاربر یادآوری کرده‌ایم.

چالش برای شما

سعی کنید این ماشین حساب را بهبود دهید:

  1. تبدیل درجه به رادیان: یک گزینه به منو اضافه کنید که ابتدا عدد را از کاربر بر حسب درجه بگیرد، آن را با استفاده از تابع math.radians() به رادیان تبدیل کند و سپس نتیجه سینوس یا کسینوس را محاسبه کند.
  2. افزودن لگاریتم: قابلیت محاسبه لگاریتم (math.log() یا math.log10()) را به ماشین حساب اضافه کنید.
  3. رابط کاربری بهتر: سعی کنید بعد از هر محاسبه، صفحه ترمینال را پاک کنید تا خروجی تمیزتر باشد. (نکته: برای این کار باید از ماژول os استفاده کنید: import os و سپس os.system('cls') برای ویندوز یا os.system('clear') برای لینوکس/مک).

در دنیای برنامه‌نویسی، کار با تاریخ و زمان یک نیاز اساسی است. از ثبت زمان یک تراکنش بانکی گرفته تا نمایش تاریخ انتشار یک مقاله، همه جا به ابزاری برای مدیریت زمان نیاز داریم. پایتون با ماژول داخلی قدرتمند datetime این کار را برای ما بسیار ساده می‌کند.

در این درس چه چیزهایی یاد می‌گیریم؟
  • وارد کردن ماژول datetime و دریافت تاریخ و زمان فعلی.
  • آشنایی با شیء datetime و اجزای آن (سال، ماه، روز، ساعت و...).
  • فرمت‌دهی تاریخ: تبدیل شیء تاریخ به یک رشته دلخواه با تابع strftime().
  • خواندن تاریخ: تبدیل یک رشته متنی به شیء تاریخ با تابع strptime().
  • انجام محاسبات روی تاریخ (مثلاً پیدا کردن تاریخ ۵۰ روز آینده).

۱. دریافت تاریخ و زمان فعلی

اولین قدم، وارد کردن ماژول و دریافت زمان کنونی سیستم است. این کار با استفاده از متد now() انجام می‌شود.

import datetime

# دریافت تاریخ و زمان فعلی به صورت یک شیء دیت‌تایم
now = datetime.datetime.now()

print(now)
# خروجی نمونه: 2026-03-23 10:30:55.123456

# شما می‌توانید به اجزای مختلف این شیء دسترسی داشته باشید
print(f"سال: {now.year}")
print(f"ماه: {now.month}")
print(f"روز: {now.day}")
print(f"ساعت: {now.hour}")
print(f"دقیقه: {now.minute}")
print(f"ثانیه: {now.second}")

خروجی datetime.datetime.now() یک "شیء" است، نه یک رشته متنی. به همین دلیل ما می‌توانیم به راحتی به اجزای آن مانند .year یا .hour دسترسی پیدا کنیم. اما اگر بخواهیم این تاریخ را به فرمتی زیبا و خوانا برای کاربر نمایش دهیم، باید آن را فرمت‌دهی کنیم.

۲. فرمت‌دهی تاریخ به رشته با ()strftime

متد strftime() (مخفف String Format Time) یک شیء تاریخ را می‌گیرد و آن را بر اساس "کدهای فرمت" که شما مشخص می‌کنید، به یک رشته متنی زیبا تبدیل می‌کند. این کدها با علامت % شروع می‌شوند.

import datetime

now = datetime.datetime.now()

# مثال ۱: فرمت رایج YYYY-MM-DD
formatted_date_1 = now.strftime("%Y-%m-%d")
print(f"فرمت ۱: {formatted_date_1}")
# خروجی نمونه: 2026-03-23

# مثال ۲: فرمت کامل با نام روز و ماه
formatted_date_2 = now.strftime("امروز %A، %d %B سال %Y")
print(f"فرمت ۲: {formatted_date_2}")
# خروجی نمونه: امروز Monday، 23 March سال 2026
# نکته: نام روزها و ماه‌ها به صورت پیش‌فرض انگلیسی هستند.

# مثال ۳: فرمت زمان ۱۲ ساعته با AM/PM
formatted_date_3 = now.strftime("%I:%M:%S %p")
print(f"فرمت ۳: {formatted_date_3}")
# خروجی نمونه: 10:30:55 AM

مرجع کامل کدهای فرمت ()strftime

جدول زیر لیست کاملی از کدهای فرمت را که می‌توانید در strftime() استفاده کنید، نشان می‌دهد.

کد (Directive) توضیحات مثال
%Y سال به صورت کامل (۴ رقمی) 2026
%y سال به صورت کوتاه (۲ رقمی) 26
%B نام کامل ماه March
%b نام کوتاه ماه (۳ حرفی) Mar
%m ماه به صورت عدد (01-12) 03
%A نام کامل روز هفته Monday
%a نام کوتاه روز هفته (۳ حرفی) Mon
%d روز ماه به صورت عدد (01-31) 23
%H ساعت در فرمت ۲۴ ساعته (00-23) 10
%I ساعت در فرمت ۱۲ ساعته (01-12) 10
%p نمایش AM یا PM AM
%M دقیقه (00-59) 30
%S ثانیه (00-59) 55
%f میکروثانیه (000000-999999) 123456
%w روز هفته به صورت عدد (یکشنبه=0، شنبه=6) 1
%j روز چندم سال (001-366) 082
%U هفته چندم سال (با شروع از یکشنبه) 12
%W هفته چندم سال (با شروع از دوشنبه) 12
%c نمایش محلی تاریخ و زمان Mon Mar 23 10:30:55 2026
%x نمایش محلی تاریخ 03/23/26
%X نمایش محلی زمان 10:30:55
%% برای نمایش خود کاراکتر % %

۳. خواندن تاریخ از رشته با ()strptime

گاهی اوقات شما یک تاریخ را به صورت متن (مثلاً از ورودی کاربر یا یک فایل) دریافت می‌کنید و می‌خواهید آن را به یک شیء datetime تبدیل کنید تا بتوانید روی آن محاسبات انجام دهید. تابع strptime() (مخفف String Parse Time) این کار را انجام می‌دهد.

نکته بسیار مهم: فرمتی که به strptime() می‌دهید باید دقیقاً با ساختار رشته ورودی شما مطابقت داشته باشد، در غیر این صورت با خطای ValueError مواجه خواهید شد.
import datetime

date_string = "30-07-2025 15:45"

# فرمت باید دقیقاً با رشته بالا مطابقت کند
date_format = "%d-%m-%Y %H:%M"

try:
    # تبدیل رشته به شیء datetime
    date_object = datetime.datetime.strptime(date_string, date_format)
    print(f"شیء تاریخ ایجاد شده: {date_object}")
    print(f"سال آن: {date_object.year}")
except ValueError:
    print("خطا: فرمت رشته با فرمت مشخص شده مطابقت ندارد.")

۴. محاسبات زمانی با timedelta

یکی از قابلیت‌های فوق‌العاده ماژول datetime، انجام محاسبات روی تاریخ‌ها است. برای این کار از شیء timedelta استفاده می‌کنیم که یک "بازه زمانی" را نشان می‌دهد.

import datetime

now = datetime.datetime.now()
print(f"زمان حال: {now.strftime('%Y-%m-%d')}")

# محاسبه تاریخ ۵۰ روز آینده
future_date = now + datetime.timedelta(days=50)
print(f"تاریخ ۵۰ روز بعد: {future_date.strftime('%Y-%m-%d')}")

# محاسبه تاریخ ۱۰ هفته و ۲ روز قبل
past_date = now - datetime.timedelta(weeks=10, days=2)
print(f"تاریخ ۱۰ هفته و ۲ روز قبل: {past_date.strftime('%Y-%m-%d')}")

# محاسبه تفاوت بین دو تاریخ
birthday = datetime.datetime(2027, 1, 1)
time_to_birthday = birthday - now
print(f"زمان باقی‌مانده تا آن تاریخ: {time_to_birthday}")
print(f"فقط {time_to_birthday.days} روز دیگر باقی مانده است!")

تا به امروز، تمام داده‌های ما در متغیرها ذخیره می‌شدند که با پایان اجرای برنامه از بین می‌رفتند. فایل‌ها حافظه دائمی برنامه‌های ما هستند. آن‌ها به ما اجازه می‌دهند داده‌ها را روی دیسک ذخیره کنیم، تا بعداً بتوانیم دوباره آن‌ها را بخوانیم و استفاده کنیم. به این فرآیند خواندن و نوشتن، عملیات I/O (Input/Output) گفته می‌شود.

در این درس چه چیزهایی یاد می‌گیریم؟
  • تابع کلیدی open() و مُدهای مختلف دسترسی به فایل (خواندن، نوشتن، اضافه کردن).
  • بهترین و امن‌ترین روش برای کار با فایل‌ها با استفاده از دستور with.
  • خواندن محتوای یک فایل خط به خط.
  • نوشتن اطلاعات جدید در یک فایل (و ایجاد فایل در صورت عدم وجود).
  • اضافه کردن اطلاعات به انتهای یک فایل موجود.

۱. تابع ()open و مُدهای دسترسی به فایل

برای کار با هر فایلی، ابتدا باید آن را با استفاده از تابع داخلی open() باز کنیم. این تابع حداقل دو آرگومان مهم می‌گیرد:

  1. مسیر فایل (File Path): آدرس و نام فایلی که می‌خواهید با آن کار کنید.
  2. مُد دسترسی (Access Mode): یک رشته که مشخص می‌کند می‌خواهید با فایل چه کاری انجام دهید (بخوانید، بنویسید و...).

مهم‌ترین مُدهای دسترسی در جدول زیر آمده‌اند:

مُد نام توضیحات
'r' Read (خواندن) فایل را فقط برای خواندن باز می‌کند. این مُد پیش‌فرض است. اگر فایل وجود نداشته باشد، خطا می‌دهد.
'w' Write (نوشتن) فایل را برای نوشتن باز می‌کند. هشدار: اگر فایل از قبل وجود داشته باشد، تمام محتوای آن را پاک می‌کند! اگر وجود نداشته باشد، آن را ایجاد می‌کند.
'a' Append (اضافه کردن) فایل را برای اضافه کردن اطلاعات به انتهای آن باز می‌کند. محتوای قبلی دست‌نخورده باقی می‌ماند. اگر فایل وجود نداشته باشد، آن را ایجاد می‌کند.
'x' Create (ایجاد) یک فایل جدید ایجاد می‌کند و آن را برای نوشتن باز می‌کند. اگر فایل از قبل وجود داشته باشد، خطا می‌دهد.

مدهای دیگری مانند 'b' (برای فایل‌های باینری مثل عکس) و '+' (برای به‌روزرسانی، یعنی خواندن و نوشتن همزمان) نیز وجود دارند که در مباحث پیشرفته‌تر به آن‌ها پرداخته می‌شود.

۲. بهترین روش کار با فایل‌ها: دستور with

بعد از اتمام کار با یک فایل، باید آن را ببندیم تا منابع سیستم آزاد شوند. اگر فراموش کنیم فایل را ببندیم، ممکن است باعث بروز مشکلاتی در برنامه شود. پایتون یک راه بسیار زیبا و امن برای مدیریت این موضوع ارائه می‌دهد: بلوک with.

چرا از `with` استفاده کنیم؟

دستور with open(...) as file: تضمین می‌کند که فایل به طور خودکار بسته خواهد شد، حتی اگر در حین کار با آن خطایی رخ دهد. این روش مدرن، تمیز و استاندارد برای کار با فایل‌ها در پایتون است. همیشه از این روش استفاده کنید.

# ساختار کلی استفاده از with
with open("my_file.txt", "w") as file:
    # تمام کدهایی که با فایل کار می‌کنند
    # در این بلوک با تورفتگی نوشته می‌شوند.
    file.write("سلام دنیا!")
# در این نقطه، پایتون به طور خودکار فایل را بسته است.

۳. خواندن محتوای فایل (Reading)

فرض کنید فایلی به نام greetings.txt در کنار فایل پایتون خود داریم که محتوای زیر را دارد:

Hello Python
Welcome to file handling
Enjoy learning!

بهترین راه برای خواندن این فایل، استفاده از یک حلقه for است:

try:
    with open("greetings.txt", "r") as file:
        print("محتوای فایل:")
        for line in file:
            # هر خط از فایل در متغیر line قرار می‌گیرد
            # .strip() کاراکترهای اضافی مثل خط جدید (\n) را از ابتدا و انتهای رشته حذف می‌کند
            print(line.strip()) 
except FileNotFoundError:
    print("خطا: فایل مورد نظر پیدا نشد.")
توابع دیگر برای خواندن:
  • file.read(): تمام محتوای فایل را به صورت یک رشته واحد می‌خواند. برای فایل‌های کوچک مناسب است.
  • file.readlines(): تمام خطوط فایل را می‌خواند و آن‌ها را به صورت یک لیست از رشته‌ها برمی‌گرداند.

۴. نوشتن در فایل (Writing)

برای نوشتن در فایل از متد .write() استفاده می‌کنیم. به یاد داشته باشید که این متد به طور خودکار به خط بعدی نمی‌رود و شما باید در صورت نیاز، کاراکتر خط جدید (\n) را خودتان اضافه کنید.

هشدار جدی! استفاده از مُد 'w' تمام محتوای قبلی فایل را برای همیشه پاک می‌کند. اگر فایل وجود نداشته باشد، یک فایل جدید با آن نام ایجاد می‌شود.
shopping_list = ["شیر", "نان", "تخم مرغ", "کره"]

with open("shopping.txt", "w") as file:
    file.write("لیست خرید من:\n") # \n برای رفتن به خط بعد
    for item in shopping_list:
        file.write(f"- {item}\n")

print("فایل shopping.txt با موفقیت ایجاد و نوشته شد.")

پس از اجرای این کد، فایلی به نام shopping.txt با محتوای لیست خرید ایجاد خواهد شد.

۵. اضافه کردن به انتهای فایل (Appending)

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

# فرض کنید فایل shopping.txt از قبل وجود دارد

with open("shopping.txt", "a") as file:
    file.write("- عسل\n")
    file.write("- پنیر\n")

print("موارد جدید به لیست خرید اضافه شد.")

اگر فایل shopping.txt را باز کنید، خواهید دید که "عسل" و "پنیر" به انتهای لیست قبلی اضافه شده‌اند.

تاکنون با متغیرها برای ذخیره داده (مانند نام، سن، لیست خرید) و توابع برای انجام عملیات کار کرده‌ایم. اما در دنیای واقعی، معمولاً داده‌ها و عملیات مرتبط با آن‌ها به هم پیوسته‌اند. برای مثال، یک "ماشین" هم داده‌هایی (ویژگی‌هایی) مثل رنگ، مدل و سرعت دارد و هم عملیاتی (رفتارهایی) مثل گاز دادن، ترمز کردن و بوق زدن. برنامه‌نویسی شیءگرا (OOP) روشی برای مدل‌سازی این موجودیت‌های دنیای واقعی در کد است.

کلاس (Class) مانند یک نقشه یا طرح اولیه (Blueprint) برای ساختن این موجودیت‌هاست. با استفاده از کلاس، ما تعریف می‌کنیم که یک "شیء" (Object) از آن نوع، چه ویژگی‌ها و چه رفتارهایی باید داشته باشد.

در این درس چه چیزهایی یاد می‌گیریم؟
  • تفاوت بین کلاس (Class) و شیء (Object/Instance).
  • چگونگی تعریف یک کلاس با کلمه کلیدی class.
  • متد ویژه __init__() یا سازنده (Constructor) برای مقداردهی اولیه به اشیاء.
  • مفهوم کلیدی پارامتر self و نقش آن.
  • تعریف ویژگی‌ها (Attributes) و متدها (Methods) برای یک کلاس.
  • ساختن چندین شیء از روی یک کلاس و استفاده از آن‌ها.

۱. کلاس در مقابل شیء (آبجکت)

این تمایز بسیار مهم است:

  • کلاس (Class): نقشه ساخت یک ماشین است. در این نقشه مشخص شده که ماشین باید فرمان، چرخ، موتور و رنگ داشته باشد و بتواند حرکت کند و توقف کند. این فقط یک طرح است و وجود خارجی ندارد.
  • شیء (Object): یک ماشین واقعی است که از روی آن نقشه ساخته شده است. مثلاً "یک پراید سفید مدل ۱۳۹۸" یا "یک پژو ۲۰۶ قرمز مدل ۱۴۰۱". هر دوی این‌ها اشیائی از کلاس "ماشین" هستند. هر کدام ویژگی‌های خاص خود را دارند (رنگ و مدل متفاوت)، اما همگی رفتارهای یکسانی (حرکت، توقف) را طبق نقشه دنبال می‌کنند. به هر شیء ساخته شده از روی کلاس، یک نمونه (Instance) از آن کلاس نیز می‌گویند.

۲. ساختار یک کلاس در پایتون

یک کلاس با کلمه کلیدی class و سپس نام کلاس (که طبق قرارداد با حرف بزرگ شروع می‌شود) تعریف می‌شود.

class Car:
    # ویژگی‌ها (Attributes) و متدها (Methods) در اینجا تعریف می‌شوند
    pass

۳. متد `__init__`: سازنده (Constructor)

این یک متد جادویی و بسیار مهم است. هر زمان که یک شیء جدید از روی کلاس ساخته می‌شود، پایتون به طور خودکار این متد را فراخوانی می‌کند. وظیفه اصلی آن، مقداردهی اولیه به ویژگی‌های (Attributes) آن شیء جدید است.

متدهای جادویی (Dunder Methods): متدهایی که با دو آندرلاین در ابتدا و انتها (`__نام__`) مشخص می‌شوند، در پایتون کاربردهای ویژه‌ای دارند و معمولاً به صورت خودکار توسط مفسر پایتون فراخوانی می‌شوند.

۴. پارامتر `self`: ارجاع به خود شیء

پارامتر self که همیشه به عنوان اولین پارامتر در متدهای یک کلاس (از جمله __init__) قرار می‌گیرد، به خودِ آن نمونه یا شیء خاصی اشاره دارد که متد را فراخوانی کرده است. این راهی است که شیء می‌تواند به ویژگی‌ها و متدهای خودش دسترسی پیدا کند.

وقتی می‌نویسیم self.color = "white"، در واقع داریم می‌گوییم: "رنگِ این شیء خاص را برابر با سفید قرار بده". وقتی شما یک شیء می‌سازید (مثلاً pride = Car(...))، پایتون به صورت خودکار ارجاع به شیء pride را به جای self قرار می‌دهد.

۵. مثال جامع: ساخت کلاس `Car`

حالا تمام مفاهیم بالا را در یک مثال عملی پیاده‌سازی می‌کنیم.

بخش اول: تعریف کلاس

class Car:
    # 1. متد سازنده (__init__)
    # وقتی یک ماشین جدید می‌سازیم، این متد اجرا می‌شود
    def __init__(self, brand, model, year):
        # 2. تعریف ویژگی‌ها (Attributes)
        # self به این شیء خاص اشاره دارد
        self.brand = brand
        self.model = model
        self.year = year
        self.speed = 0 # سرعت اولیه همه ماشین‌ها صفر است

    # 3. تعریف متدها (Methods - رفتارها)
    def accelerate(self, amount):
        """سرعت ماشین را به مقدار مشخص شده افزایش می‌دهد."""
        self.speed += amount
        print(f"گاز دادیم! سرعت فعلی: {self.speed} km/h")

    def brake(self, amount):
        """سرعت ماشین را به مقدار مشخص شده کاهش می‌دهد."""
        self.speed -= amount
        if self.speed < 0:
            self.speed = 0
        print(f"ترمز کردیم! سرعت فعلی: {self.speed} km/h")
        
    def display_info(self):
        """اطلاعات کامل ماشین را نمایش می‌دهد."""
        print(f"مشخصات خودرو: {self.year} {self.brand} {self.model}")

بخش دوم: ساختن اشیاء (Instances) از روی کلاس

حالا که نقشه را داریم، می‌توانیم ماشین‌های واقعی بسازیم!

# ساخت اولین شیء (نمونه) از کلاس Car
pride = Car("Saipa", "131", 1398)

# ساخت دومین شیء از همان کلاس
peugeot = Car("Peugeot", "206", 1401)

بخش سوم: استفاده از اشیاء

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

# دسترسی به ویژگی‌های پراید
print(f"مدل ماشین اول: {pride.model}") # خروجی: 131

# فراخوانی متدهای پژو
peugeot.display_info() # خروجی: مشخصات خودرو: 1401 Peugeot 206
peugeot.accelerate(80) # خروجی: گاز دادیم! سرعت فعلی: 80 km/h
peugeot.accelerate(30) # خروجی: گاز دادیم! سرعت فعلی: 110 km/h
peugeot.brake(40)      # خروجی: ترمز کردیم! سرعت فعلی: 70 km/h

print("-" * 20)

# فراخوانی متدهای پراید
pride.display_info()   # خروجی: مشخصات خودرو: 1398 Saipa 131
pride.accelerate(50)   # خروجی: گاز دادیم! سرعت فعلی: 50 km/h

# توجه کنید که سرعت پراید و پژو کاملاً مستقل از هم هستند
print(f"سرعت نهایی پراید: {pride.speed}")
print(f"سرعت نهایی پژو: {peugeot.speed}")
نکات کلیدی و جمع‌بندی
  • کلاس (Class) یک نقشه است و شیء (Object) یک موجودیت واقعی ساخته شده از روی آن نقشه.
  • متد __init__ به طور خودکار هنگام ساخت یک شیء جدید اجرا می‌شود و برای مقداردهی اولیه به ویژگی‌ها استفاده می‌شود.
  • self به خودِ نمونه‌ای از کلاس اشاره دارد که در حال کار با آن هستیم و به ما اجازه می‌دهد به ویژگی‌ها و متدهای آن شیء دسترسی داشته باشیم.
  • ویژگی‌ها (Attributes) داده‌های یک شیء هستند (مانند self.model).
  • متدها (Methods) توابعی هستند که رفتارهای یک شیء را تعریف می‌کنند (مانند accelerate).
  • کلاس‌ها به ما کمک می‌کنند کدهای بسیار تمیزتر، سازمان‌یافته‌تر و با قابلیت استفاده مجدد بنویسیم.

در درس قبل یاد گرفتیم که چگونه با استفاده از کلاس‌ها، نقشه‌هایی برای ساخت اشیاء دنیای واقعی طراحی کنیم. اما گاهی اوقات، کلاس‌های ما با یکدیگر ارتباط دارند. برای مثال، در یک دانشگاه، ما "دانشجو"، "استاد" و "کارمند اداری" داریم. همه این‌ها در یک ویژگی مشترک هستند: همگی "شخص" هستند و ویژگی‌های پایه‌ای مانند نام و نام خانوادگی دارند.

ارث‌بری (Inheritance) مکانیزمی است که به ما اجازه می‌دهد یک کلاس جدید (فرزند) بسازیم که تمام ویژگی‌ها و متدهای یک کلاس موجود (والد) را به ارث ببرد. این کار از تکرار کد جلوگیری کرده و یک ساختار منطقی و سلسله مراتبی بین کلاس‌ها ایجاد می‌کند.

در این درس چه چیزهایی یاد می‌گیریم؟
  • مفهوم کلاس والد (Parent/Superclass) و کلاس فرزند (Child/Subclass).
  • چگونگی پیاده‌سازی ارث‌بری در پایتون با سینتکس class Child(Parent):.
  • استفاده از تابع super() برای فراخوانی متدهای کلاس والد (به خصوص __init__).
  • بازنویسی متد (Method Overriding): چگونه کلاس فرزند می‌تواند رفتار یک متد به ارث برده شده را تغییر دهد.
  • ساختن یک سلسله مراتب از کلاس‌ها با مثال دانشگاه.

۱. کلاس والد: `Person`

بیایید با یک کلاس پایه به نام `Person` شروع کنیم. این کلاس ویژگی‌های مشترک بین همه افراد دانشگاه را تعریف می‌کند.

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def display_info(self):
        print(f"نام: {self.first_name} {self.last_name}")

۲. کلاس فرزند: `Student`

یک دانشجو، یک "شخص" است، اما ویژگی‌های اضافی مانند شماره دانشجویی و رشته تحصیلی دارد. پس کلاس `Student` از `Person` ارث‌بری می‌کند.

  • سینتکس ارث‌بری: نام کلاس والد را داخل پرانتز بعد از نام کلاس فرزند قرار می‌دهیم: class Student(Person):.
  • تابع `super()`: برای اینکه ویژگی‌های کلاس والد (first_name, last_name) مقداردهی شوند، باید سازنده (__init__) کلاس والد را فراخوانی کنیم. بهترین راه برای این کار استفاده از super().__init__(...) است.
  • بازنویسی متد (Overriding): ما می‌خواهیم متد display_info اطلاعات دانشجو را هم نمایش دهد. پس آن را در کلاس فرزند دوباره تعریف می‌کنیم. می‌توانیم ابتدا نسخه والد را با super().display_info() اجرا کنیم و سپس اطلاعات جدید را اضافه کنیم.
class Student(Person):
    def __init__(self, first_name, last_name, student_id, major):
        # فراخوانی سازنده کلاس والد (Person) برای مقداردهی نام و نام خانوادگی
        super().__init__(first_name, last_name)
        
        # اضافه کردن ویژگی‌های جدید مختص دانشجو
        self.student_id = student_id
        self.major = major

    # بازنویسی متد display_info برای نمایش اطلاعات کامل‌تر
    def display_info(self):
        super().display_info() # ابتدا متد والد را اجرا می‌کند
        print(f"  شماره دانشجویی: {self.student_id}")
        print(f"  رشته تحصیلی: {self.major}")

۳. ارث‌بری چند سطحی: `Employee` و `Professor`

حالا یک ساختار کمی پیچیده‌تر ایجاد می‌کنیم. یک استاد، نوعی کارمند است و یک کارمند، نوعی شخص است. (Professor -> Employee -> Person)

کلاس `Employee` (فرزند `Person`)

class Employee(Person):
    def __init__(self, first_name, last_name, employee_id, department):
        super().__init__(first_name, last_name)
        self.employee_id = employee_id
        self.department = department

    def display_info(self):
        super().display_info()
        print(f"  شماره کارمندی: {self.employee_id}")
        print(f"  دپارتمان: {self.department}")

کلاس `Professor` (فرزند `Employee`)

این کلاس تمام ویژگی‌های `Employee` (و در نتیجه `Person`) را به ارث می‌برد و ویژگی‌های جدید خود را اضافه می‌کند.

class Professor(Employee):
    def __init__(self, first_name, last_name, employee_id, department, academic_rank):
        # فراخوانی سازنده کلاس والد (Employee)
        super().__init__(first_name, last_name, employee_id, department)
        self.academic_rank = academic_rank
        self.courses = [] # لیستی برای دروسی که تدریس می‌کند

    def add_course(self, course_name):
        self.courses.append(course_name)
        print(f"درس '{course_name}' به لیست دروس دکتر {self.last_name} اضافه شد.")

    # بازنویسی مجدد برای نمایش کامل‌ترین اطلاعات
    def display_info(self):
        super().display_info()
        print(f"  مرتبه علمی: {self.academic_rank}")
        print(f"  دروس تدریسی: {', '.join(self.courses) if self.courses else 'هنوز درسی ارائه نشده'}")

۴. استفاده از کلاس‌های ساخته شده

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

# ساخت یک شیء از نوع Professor
prof1 = Professor("علی", "رضایی", 95102, "مهندسی کامپیوتر", "دانشیار")
prof1.add_course("برنامه‌نویسی پیشرفته")
prof1.add_course("هوش مصنوعی")

print("---------------------------------")

# ساخت یک شیء از نوع Student
student1 = Student("مریم", "احمدی", 401123, "علوم کامپیوتر")

# نمایش اطلاعات هر شخص
print("اطلاعات استاد:")
prof1.display_info()

print("\n---------------------------------\n")

print("اطلاعات دانشجو:")
student1.display_info()
درس 'برنامه‌نویسی پیشرفته' به لیست دروس دکتر رضایی اضافه شد.
درس 'هوش مصنوعی' به لیست دروس دکتر رضایی اضافه شد.
---------------------------------
اطلاعات استاد:
نام: علی رضایی
  شماره کارمندی: 95102
  دپارتمان: مهندسی کامپیوتر
  مرتبه علمی: دانشیار
  دروس تدریسی: برنامه‌نویسی پیشرفته, هوش مصنوعی

---------------------------------

اطلاعات دانشجو:
نام: مریم احمدی
  شماره دانشجویی: 401123
  رشته تحصیلی: علوم کامپیوتر
نکات کلیدی و جمع‌بندی
  • ارث‌بری رابطه "is a" را مدل‌سازی می‌کند (یک استاد، یک کارمند است).
  • کلاس فرزند تمام ویژگی‌ها و متدهای عمومی کلاس والد را به ارث می‌برد.
  • از super() برای دسترسی و فراخوانی متدهای کلاس والد از درون کلاس فرزند استفاده می‌شود. این کار برای جلوگیری از تکرار کد ضروری است.
  • کلاس فرزند می‌تواند متدهای والد را بازنویسی (Override) کند تا رفتار آن‌ها را برای نیازهای خاص خود تغییر دهد.
  • کلاس فرزند می‌تواند ویژگی‌ها و متدهای کاملاً جدیدی داشته باشد که در کلاس والد وجود ندارند (مانند متد add_course در کلاس Professor).

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

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

هدف این درس چیست؟
  • مرور و تثبیت مفاهیم آموخته شده در طول دوره.
  • ارائه یک دسته‌بندی منطقی از کلمات کلیدی برای درک بهتر ساختار زبان.
  • ایجاد یک برگه تقلب (Cheat Sheet) جامع برای مراجعات آینده.

۱. مقادیر و عملگرهای منطقی

کلمه‌ی کلیدی کاربرد اصلی مثال کوتاه
True مقدار بولین (Boolean) برای درستی. is_active = True
False مقدار بولین (Boolean) برای نادرستی. is_admin = False
None نشان‌دهنده "هیچ مقدار" یا پوچی. user_email = None
and عملگر منطقی "و" (هر دو شرط باید درست باشند). if age > 18 and has_license:
or عملگر منطقی "یا" (حداقل یکی از شرط‌ها باید درست باشد). if is_weekend or is_holiday:
not عملگر منطقی "نقیض" (نتیجه شرط را برعکس می‌کند). if not is_logged_in:

۲. ساختارهای کنترلی و شرطی

کلمه‌ی کلیدی کاربرد اصلی مثال کوتاه
if شروع یک بلوک شرطی. if temperature > 30:
elif بررسی یک شرط دیگر در صورتی که if قبلی نادرست باشد. elif temperature > 20:
else اجرای یک بلوک کد در صورتی که تمام شرط‌های قبلی نادرست باشند. else: print("هوا سرد است.")

۳. حلقه‌ها

کلمه‌ی کلیدی کاربرد اصلی مثال کوتاه
for برای ایجاد یک حلقه تکرار بر روی یک دنباله (مانند لیست یا رشته). for item in my_list:
while برای ایجاد یک حلقه تا زمانی که یک شرط درست باشد. while count < 5:
break خروج فوری از حلقه فعلی. if user_input == "exit": break
continue پرش به تکرار بعدی حلقه و نادیده گرفتن ادامه کد در تکرار فعلی. if number % 2 == 0: continue

۴. توابع و دامنه‌ها

کلمه‌ی کلیدی کاربرد اصلی مثال کوتاه
def برای تعریف یک تابع جدید. def my_function(name):
return خروج از تابع و برگرداندن یک مقدار. return x + y
lambda برای ساختن یک تابع ناشناس (کوچک و یک خطی). adder = lambda a, b: a + b
pass یک دستور خالی که هیچ کاری انجام نمی‌دهد. به عنوان placeholder استفاده می‌شود. def new_feature(): pass

۵. مدیریت خطا

کلمه‌ی کلیدی کاربرد اصلی مثال کوتاه
try شروع یک بلوک کد که ممکن است خطا ایجاد کند. try: result = 10 / 0
except گرفتن و مدیریت یک خطا در صورت وقوع در بلوک try. except ZeroDivisionError:
finally اجرای یک بلوک کد، چه خطا رخ بدهد چه ندهد. finally: file.close()
raise ایجاد (پرتاب) یک خطا به صورت دستی. raise ValueError("مقدار نامعتبر است")

۶. ماژول‌ها، فایل‌ها و شیءگرایی

کلمه‌ی کلیدی کاربرد اصلی مثال کوتاه
import وارد کردن یک ماژول به برنامه. import math
from برای وارد کردن بخش خاصی از یک ماژول. from datetime import datetime
as برای دادن یک نام مستعار به یک ماژول یا تابع وارد شده. import numpy as np
with برای مدیریت منابع (مانند فایل‌ها) که تضمین می‌کند در پایان بسته می‌شوند. with open("data.txt") as f:
class برای تعریف یک کلاس جدید. class Person:
del برای حذف یک متغیر، آیتم از لیست یا ویژگی از یک شیء. del my_list[0]
in بررسی وجود یک آیتم در یک دنباله (عملگر عضویت). if "a" in "banana":
is بررسی اینکه آیا دو متغیر به یک شیء یکسان در حافظه اشاره دارند (عملگر هویت). if x is None:
مسیر شما ادامه دارد...

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