python

1405/03/02

جلسه اول: شروع کار با پایتون و اصول اولیه


پایتون چیست؟

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

نصب پایتون و ابزارهای مورد نیاز

  1. نصب Python: کافیه به سایت python.org برید و آخرین نسخه رو دانلود و نصب کنید. (یادتون نره موقع نصب تیک Add Python to PATH رو بزنید).
  2. PyCharm چیست؟ یک محیط توسعه (IDE) حرفه‌ای مخصوص پایتون است. اگر قصد دارید پروژه‌های بزرگ و جدی بنویسید، پای‌چارم با ابزارهای هوشمندی که داره بهترین دستیار شماست.
  3. Jupyter Notebook چیست؟ یک محیط تعاملیه که اجازه میده کدها رو در بلوک‌های جداگانه اجرا کنید و نتیجه رو همون لحظه ببینید. برای کارهای آموزشی، تحلیل داده و تست سریع کدها عالیه.
  4. Pip چیست؟ مخفف "Preferred Installer Program" است. در واقع ابزاریه که باهاش کتابخانه‌ها و بسته‌های آماده پایتون رو از اینترنت دانلود و نصب می‌کنیم.

متغیرها و انواع آن‌ها

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

  • int: اعداد صحیح (مثل 10)
  • float: اعداد اعشاری (مثل 15.5)
  • str: رشته یا متن (مثل "Hello")
  • bool: مقادیر منطقی (True یا False)

تشخیص و تبدیل نوع (Type Casting)

برای دیدن نوع یک متغیر از تابع type() استفاده می‌کنیم:

x = 10
print(type(x))  # <class 'int'>

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

y = float(x)   # تبدیل عدد صحیح به اعشاری -> 10.0
s = str(x)     # تبدیل عدد به متن -> "10"

ورودی و خروجی

برای گرفتن داده از کاربر از input() و برای نمایش خروجی از print() استفاده می‌کنیم:

name = input("نام خود را وارد کنید: ")
print("سلام", name)

نکته: خروجی input() همیشه متن (string) است. اگر عدد می‌خواهید، باید آن را تبدیل کنید: int(input()).

عملگرهای ریاضی

  • جمع: + | تفریق: - | ضرب: * | تقسیم: /
  • توان: ** (مثلا 2 ** 3 می‌شود ۸)
  • باقیمانده: %
  • تقسیم صحیح (بدون اعشار): //

تمرینات جلسه اول


جابجایی دو متغیر بدون متغیر کمکی

در پایتون این کار به سادگی و در یک خط انجام می‌شود:

a = 5
b = 10
a, b = b, a
print("a:", a, "b:", b)

توضیح: پایتون ابتدا سمت راست تساوی را به صورت یک "تاپل" ایجاد می‌کند و سپس مقادیر را به ترتیب به متغیرهای سمت چپ اختصاص می‌دهد.


محاسبه انعام، مالیات و جمع کل

food_cost = float(input("مبلغ غذا را وارد کنید: "))

tip = food_cost * 0.18
tax = food_cost * 0.07
total = food_cost + tip + tax

print(f"انعام: {tip}")
print(f"مالیات: {tax}")
print(f"جمع کل: {total}")

توضیح: ابتدا ورودی را به float تبدیل کردیم تا محاسبات اعشاری دقیق باشد، سپس درصدها را ضرب و در نهایت با هم جمع کردیم.


تنظیم‌کننده مواد کوکی

برای ۴۸ کوکی مقادیر مشخص است، پس برای هر ۱ کوکی مقادیر را تقسیم بر ۴۸ می‌کنیم و در تعداد درخواستی ضرب می‌کنیم.

cookies_needed = int(input("چند عدد کوکی می‌خواهید؟ "))

ratio = cookies_needed / 48

sugar = 1.5 * ratio
butter = 1 * ratio
flour = 2.75 * ratio

print(f"شکر مورد نیاز: {sugar} فنجان")
print(f"کره مورد نیاز: {butter} فنجان")
print(f"آرد مورد نیاز: {flour} فنجان")

پیدا کردن ریشه‌های معادله درجه دو

برای این کار از فرمول دلتا $$Delta = b^2 - 4ac$$ استفاده می‌کنیم.

import math

a = float(input("ضریب a: "))
b = float(input("ضریب b: "))
c = float(input("ضریب c: "))

delta = b**2 - 4*a*c

if delta > 0:
    x1 = (-b + math.sqrt(delta)) / (2*a)
    x2 = (-b - math.sqrt(delta)) / (2*a)
    print(f"دو ریشه واقعی: {x1} و {x2}")
elif delta == 0:
    x = -b / (2*a)
    print(f"یک ریشه تکراری: {x}")
else:
    print("این معادله ریشه حقیقی ندارد.")

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


جلسه دوم: منطق، شرط‌ها و کنترل جریان برنامه

عملگرهای مقایسه‌ای (پایه و اساس منطق)

ما برای مقایسه مقادیر از این عملگرها استفاده می‌کنیم. خروجی این مقایسه‌ها همیشه یک مقدار Boolean یعنی یا True (درست) است یا False (غلط).

  • == : مساوی بودن
  • != : نامساوی بودن
  • < و > : بزرگتر یا کوچکتر بودن
  • <= و >= : بزرگتر یا کوچکتر مساوی بودن

عملگرهای منطقی (ترکیب شرط‌ها)

گاهی نیاز داریم چند شرط را با هم چک کنیم:

  • and: وقتی درست است که هر دو طرف درست باشند.
  • or: وقتی درست است که حداقل یکی از طرفین درست باشد.
  • not: نتیجه را برعکس می‌کند (مثلاً True را به False تبدیل می‌کند).

ساختارهای شرطی (If, Elif, Else)

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

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

ساختار جدید Match Case (پایتون ۳.۱۰ به بالا)

این ساختار برای زمانی که می‌خواهی یک متغیر را با چندین مقدار مختلف مقایسه کنی، بسیار تمیزتر و خواناتر از ifهای طولانی است. علامت _ در این ساختار نقش "در غیر این صورت" یا همان حالت پیش‌فرض را دارد.


تمرینات جلسه دوم


تحلیل‌گر مثلث

هدف: بررسی معتبر بودن مثلث و نوع آن.

a = float(input("ضلع اول: "))
b = float(input("ضلع دوم: "))
c = float(input("ضلع سوم: "))

# بررسی قانون مثلث
if (a + b > c) and (a + c > b) and (b + c > a):
    if a == b == c:
        print("مثلث متساوی‌الاضلاع (Equilateral)")
    elif a == b or a == c or b == c:
        print("مثلث متساوی‌الساقین (Isosceles)")
    else:
        print("مثلث مختلف‌الاضلاع (Scalene)")
else:
    print("مثلث نامعتبر (Invalid Triangle)")

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


سیستم تخفیف فروشگاه

هدف: اولویت‌بندی شرط‌ها و مدیریت هدیه ویژه.

amount = float(input("مبلغ خرید: "))
is_member = input("عضو هستید؟ (True/False): ").lower() == "true"

if amount > 200:
    discount = 0.20 if is_member else 0.10
elif 100 <= amount <= 200:
    discount = 0.05
else:
    discount = 0

final_price = amount * (1 - discount)
print(f"قیمت نهایی: {final_price}")

if amount > 500:
    print("تبریک! شما یک هدیه ویژه (Special Gift) دریافت کردید.")

توضیح: اینجا از elif استفاده کردیم تا محدوده‌های قیمتی را جدا کنیم. دقت کن که شرط هدیه ویژه (بالای ۵۰۰) کاملاً جداست، چون ربطی به تخفیف ندارد و باید جداگانه چاپ شود.


تشخیص سال کبیسه

هدف: پیاده‌سازی منطق پیچیده ریاضی.

year = int(input("سال را وارد کنید: "))

if (year % 400 == 0) or (year % 4 == 0 and year % 100 != 0):
    print("این سال کبیسه است.")
else:
    print("این سال کبیسه نیست.")

توضیح: فرمول اصلی این است: سالی کبیسه است که (بر ۴۰۰ بخش‌پذیر باشد) یا (بر ۴ بخش‌پذیر باشد و بر ۱۰۰ نباشد). با ترکیب and و or این منطق را در یک خط نوشتیم.


سیستم احراز هویت امنیتی

هدف: تمرین اولویت‌بندی در شرط‌های تودرتو.

username = input("نام کاربری: ")
password = input("رمز عبور: ")
is_banned = input("آیا کاربر مسدود است؟ (True/False): ").lower() == "true"

if is_banned:
    print("Account Locked")
else:
    if username == "admin":
        if password == "1234":
            print("Welcome Admin")
        else:
            print("Wrong Password")
    else:
        print("User Not Found")

توضیح: در اینجا امنیت اولویت دارد. اول از همه چک می‌کنیم کاربر مسدود است یا نه. اگر نبود، لایه‌های بعدی (نام کاربری و بعد رمز عبور) را چک می‌کنیم.


مبدل واحد هوشمند (با Match Case)

هدف: استفاده از ساختار Match برای انتخاب عملیات.

number = float(input("عدد را وارد کنید: "))
mode = input("کد عملیات (1-3): ")

match mode:
    case "1":
        if number < 0:
            print("Negative input not allowed")
        else:
            print(f"نتیجه: {number * 0.62} مایل")
    case "2":
        if number < -273.15: # دمای مطلق
            print("Negative input not allowed")
        else:
            print(f"نتیجه: {(number * 1.8) + 32} فارنهایت")
    case "3":
        res = "زوج" if number % 2 == 0 else "فرد"
        print(f"عدد {res} است.")
    case _:
        print("Invalid Code")

توضیح: match mode اجازه می‌دهد بر اساس ورودی کاربر، یکی از شاخه‌ها را انتخاب کنیم. در شاخه ۱ و ۲، یک if داخلی گذاشتیم تا از ورود اعداد نامعتبر (منفی) جلوگیری کنیم.


جلسه سوم: حلقه‌ها و ساختارهای تکرار


منطق تکرار در برنامه‌نویسی

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

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

حلقه‌ی while (تکرار بر اساس شرط)

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

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

الگوریتم تفکیک ارقام یک عدد با while

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

#!/usr/bin/env python3

# دریافت عدد صحیح از کاربر
enter = int(input('enter a integer number: '))

while enter != 0:
    number = enter % 10     # استخراج رقم سمت راست (باقی‌مانده بر 10)
    enter = int(enter / 10) # حذف رقم سمت راست از طریق تقسیم صحیح
    
    print(number)           # چاپ رقم استخراج شده
    
print('Finish!')

شبیه‌سازی ساختار do-while (اعتبارسنجی ورودی)

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

کد زیر نمونه‌ای از این کاربرد را جهت دریافت الزامی یک عدد مثبت نشان می‌دهد:

#!/usr/bin/env python3

while True:
    enter = int(input('enter a number: '))
    
    if enter > 0:
        break     # خروج فوری از حلقه در صورت صحت شرط
    
print(f'ok! {enter}')


حلقه‌ی for و تابع range

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

قواعد ساختاری تابع range:

  • range(stop): تولید اعداد از صفر تا stop-1.
  • range(start, stop): تولید اعداد از مبدا start تا مقصد stop-1.
  • range(start, stop, step): تولید اعداد از مبدا تا مقصد با گام‌های جهشی مشخص.

در برنامه زیر، یک حلقه for مأموریت دارد تا به تعداد عدد وارد شده توسط کاربر، پیام معینی را به همراه شاخص عددی آن تکرار کند:

#!/usr/bin/env python3

enter = int(input('enter a number: '))

# متغیر i به عنوان شمارنده از عدد 0 تا یکی مانده به enter تغییر می‌کند
for i in range(enter):
    print(f'{i}) hello')


دستورات کنترل جریان حلقه (break و continue)

جهت مدیریت و تغییر رفتار پیش‌فرض حلقه‌ها در شرایط خاص، دو دستور کلیدی تعبیه شده است:

دستور break (توقف کامل)

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

دستور continue (عبور از تکرار جاری)

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

در کد زیر، نحوه به‌کارگیری دستور continue جهت غربالگری و چاپ انحصاری اعداد فرد نمایش داده شده است:

#!/usr/bin/env python3

n = int(input('enter a number: '))

for i in range(n + 1):
    if i % 2 == 0:
        continue # در صورت زوج بودن عدد، از چاپ عبور کرده و به دور بعد می‌رود
    print(f'i -> {i}')


پیاده‌سازی ترکیبی: اعتبارسنجی محاسباتی

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

#!/usr/bin/env python3

# فاز اول: تضمین دریافت ورودی معتبر و مثبت
while True:
    n = int(input('Enter a number(number > 0): '))
    if n > 0:
        break

# فاز دوم: محاسبه مجموع اعداد زوج محدوده
sum = 0
for i in range(n + 1):
    if i % 2 == 0:
        sum += i
        
print(f'sum: {sum}')


حلقه‌های تو در تو (Nested Loops)

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

نمونه زیر ساختار یک جدول ضرب استاندارد ریاضی را با استفاده از این مفهوم پیاده‌سازی کرده است:

#!/usr/bin/env python3

# حلقه‌ی بیرونی جهت کنترل سطرها
for i in range(1, 10):
    # حلقه‌ی داخلی جهت کنترل ستون‌ها و محاسبات
    for j in range(1, 10):
        # استفاده از end = '' جهت جلوگیری از شکستن خط و ایجاد فاصله منظم
        print(f'{i} * {j} = {i*j} \t', end='')
    print() # ایجاد خط جدید پس از اتمام هر سطر کامل


ترسیم الگوهای هندسی

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

My Image

الگوی هرم قائم‌الزاویه ستاره‌ای

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

#!/usr/bin/env python3

n = int(input('enter a number: '))

for i in range(1, n + 1):
    for j in range(i): # تعداد تکرار وابسته به گام حلقه‌ی بیرونی است
        print('* ', end='')
    print()

الگوی مثلث عددی افزایشی

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

#!/usr/bin/env python3

n = int(input('enter a number: '))
counter = 1

for i in range(1, n + 1):
    for j in range(i):
        print(f'{counter} ', end='')
        counter += 1 # افزایش مقدار شمارنده پس از هر بار چاپ
    print()

الگوی هرم متقارن مبتنی بر فضاگذاری

در این ساختار، کنترل فضاهای خالی پیش از چاپ کاراکتر اصلی، جهت حفظ تقارن هرم از اهمیت بالایی برخوردار است:

#!/usr/bin/env python3

n = int(input('enter a number: ')) # دریافت عمق هرم
space = n - 1 # تبیین تعداد فضاهای خالی اولیه برای سطر نخست

for i in range(1, n + 1):
    # حلقه‌ی اختصاصی جهت چاپ فضاهای خالی
    for j in range(space):
        print(' ', end='')
    space -= 1 # کاهش گام‌به‌گام فضای خالی در هر سطر
    
    # حلقه‌ی اختصاصی جهت چاپ کاراکترهای ستاره
    for k in range(i):
        print('* ', end='')
    print()


جلسه چهارم: ساختارهای داده پیشرفته


مفهوم دنباله و ایندکس

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

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

ایندکس چیست؟

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

به عنوان مثال، رشته زیر را در نظر بگیرید:

s = "python"
print(s[0])   # خروجی: p (اولین کاراکتر از سمت چپ)
print(s[-1])  # خروجی: n (اولین کاراکتر از سمت راست)

عملیات برش‌زدن یا اسلایس

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

sequence[start:stop:step]

  • start: ایندکس مبدا و نقطه شروع حرکت (شامل خود ایندکس می‌شود). اگر آن را ننویسید، پایتون فرض می‌کند از ابتدای رشته (یعنی صفر) منظور شماست.
  • stop: ایندکس مقصد و نقطه پایان حرکت. یک قانون خیلی مهم در پایتون وجود دارد: نقطه پایان هیچ‌وقت خودش حساب نمی‌شود. یعنی برنامه تا یکی مانده به مقصد (تا خانه قبل از نقطه پایان) را برش می‌زند.
  • step: میزان گام حرکتی بر روی عناصر یا همان میزان جهش گام‌ها. اگر نوشته نشود، به صورت پیش‌فرض یک در نظر گرفته می‌شود.
s = "python"
print(s[1:4])   # خروجی: yth (اندیس‌های ۱، ۲ و ۳ جدا می‌شوند و خود اندیس ۴ حساب نمی‌شود)
print(s[:3])    # خروجی: pyt (از ابتدا تا اندیس ۲ را جدا می‌کند)
print(s[::2])   # خروجی: pto (حرکت از ابتدا با گام‌های ۲ تایی و یکی در میان)

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

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

s = 'hello'

print(s[0])       # خروجی: h
print(s[4:1:-2])   # خروجی: ol

در خط اول حرف h چاپ می‌شود. در خط دوم، از اندیس ۴ که حرف o است شروع می‌کند، دو گام به عقب می‌رود و به اندیس ۲ که حرف l است می‌رسد. از آنجا که اندیس ۱ (مقصد) خودش در خروجی نمی‌آید، عملیات همین‌جا تمام می‌شود و خروجی ol به دست می‌آید.

در نمونه بعدی، برنامه‌ای را می‌بینید که کاراکترهای غیرعددی را از متن جدا می‌کند. تابع ord در پایتون، کد اسکی یک کاراکتر را نشان می‌دهد. اعداد صفر تا نه در جدول اسکی بین کدهای ۴۸ تا ۵۷ قرار دارند:

enter = input() # به عنوان مثال ورودی: he2llo
result = '' 

for i in range(len(enter)):
    # بررسی اینکه کاراکتر جاری جزو ارقام عددی نیست
    if ord(enter[i]) < 48 or ord(enter[i]) > 57: 
        result += enter[i] 
		
print(result) # خروجی: hello

این برنامه با استفاده از طول رشته (len) تک‌تک حروف ورودی را بررسی می‌کند. اگر کد اسکی کاراکتری کمتر از ۴۸ یا بیشتر از ۵۷ باشد، یعنی آن کاراکتر عدد نیست. در نتیجه آن را به متغیر result اضافه می‌کند تا در نهایت متنی خالص و بدون عدد چاپ شود.

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

s1 = 'hello'
s2 = 'world'

s3 = s1 + ' ' + s2
print(s3) # خروجی: hello world

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

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

s = 'python'
print(s[-len(s):-1]) # خروجی: pytho

تابع len طول رشته را که عدد ۶ است برمی‌گرداند. با گذاشتن علامت منفی، بازه اسلایس از اندیس ۶- تا ۱- تنظیم می‌شود. از آنجا که خود اندیس ۱- (حرف n) در اسلایس نهایی آورده نمی‌شود، خروجی برنامه عبارت pytho خواهد بود.

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

name = input('Name: ') # نمونه ورودی: saleh
family = input('Family: ') # نمونه ورودی: askari

# دریافت اولین حرف نام، اولین حرف نام خانوادگی به صورت بزرگ و اتصال به اندیس‌های ۱ تا ۲ فامیل
username = name[0].upper() + family[0].upper() + family[1:3]

print(f'UserName: {username}') # خروجی نمونه: SAsk

این برنامه حرف اول نام و حرف اول فامیل را برمی‌دارد و با متد upper به حروف بزرگ تبدیل می‌کند. سپس حروف موجود در اندیس‌های ۱ و ۲ فامیل (family[1:3]) را به آن می‌چسباند تا یک نام کاربری خلاصه ایجاد شود.


چالش و حل مسئله بخش ایندکس

سؤال: با فرض اینکه رشته ما عبارت msg = "Networking" باشد، چطور می‌توان با اسلایس خروجی‌های زیر را ایجاد کرد؟

msg = "Networking"

# ۱. استخراج کاراکتر N
print(msg[0])

# ۲. استخراج بخش ing
print(msg[-3:])

# ۳. استخراج بخش work
print(msg[3:7])

# ۴. رشته را طوری اسلایس کن که فقط حروف ایندکس‌های زوج چاپ شود
print(msg[::2])


لیست

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

تعریف و دسترسی به عناصر لیست

برای ساختن لیست از کروشه [] استفاده می‌کنیم و اعضا را با ویرگول جدا می‌کنیم. دسترسی به آن‌ها نیز دقیقاً مثل رشته‌ها با شماره خانه یا همان ایندکس انجام می‌شود:

lst = [10, 20, 30, "hi", True]
print(lst[0])   # خروجی: 10
print(lst[-1])  # خروجی: True

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

lst[1] = 999
print(lst)  # خروجی: [10, 999, 30, 'hi', True]

متدهای شاخص مدیریت لیست

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

متد شرح عملکرد
append(x) افزودن عنصر جدید x به انتهای لیست
insert(i, x) درج عنصر جدید x در ایندکس تعیین‌شده i
pop(i) حذف و بازگرداندن عنصر موجود در ایندکس i (در صورت عدم درج ایندکس، آخرین عنصر حذف می‌شود)
remove(x) جستجو و حذف اولین عنصر با مقدار x
clear() حذف کامل تمامی عناصر و خالی کردن لیست
sort() مرتب‌سازی صعودی عناصر لیست
reverse() معکوس کردن ترتیب قرارگیری عناصر لیست

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

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

l = [True , 12,'hello',12.89,[23,'world']]
l1 = l[4] # استخراج لیست داخلی موجود در اندیس ۴ یعنی [23, 'world']

print(l1[1]) # خروجی: world (اندیس ۱ از لیست داخلی)

در این کد، خانه چهارم اصلی خودش یک لیست شامل دو عضو است. برنامه ابتدا این لیست داخلی را در متغیر l1 کپی می‌کند و سپس خانه اول آن یعنی عبارت world را بیرون می‌کشد و چاپ می‌کند.

در کد زیر رفتار و کاربرد متدهای اصلی روی لیست نمرات بررسی شده است:

score = [12, 18, 17, 18, 18]
print(score)

score.insert(1, 20) # درج عدد ۲۰ در اندیس ۱ و هل دادن بقیه اعضا به جلو
score.append(10)     # افزودن عدد ۱۰ به آخر لیست
print(score)        # خروجی: [12, 20, 18, 17, 18, 18, 10]

score.remove(18)    # حذف اولین مقداری که برابر با ۱۸ باشد
print(score)        # خروجی: [12, 20, 17, 18, 18, 10]

score.sort()        # مرتب‌سازی صعودی از کوچک به بزرگ
score.reverse()     # معکوس کردن ترتیب قرارگیری اعضا
print(f'Sorted(reverse): {score}') # خروجی: [20, 18, 18, 17, 12, 10]

score.clear()       # خالی کردن کامل قفسه لیست
print(score)        # خروجی: []

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

score = [18, 19, 15, 12, 11, 10, 9, 16]

min = 20
max = 0
sum = 0

for item in score:
    sum += item
    
    if min > item:
        min = item # به‌روزرسانی مقدار کمینه
    if max < item:
        max = item # به‌روزرسانی مقدار بیشینه

avrage = sum / len(score)
print(f'SumFinal: {sum} \t Avrage: {avrage}')
print(f'Min: {min} \t Max:{max}')

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

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

lst = [1, 2, 3]
lst.insert(6, 8)
print(lst) # خروجی: [1, 2, 3, 8]

طول لیست ۳ است ولی ما خواسته‌ایم عدد ۸ در خانه ششم درج شود. پایتون بدون اعتراض، عدد ۸ را به انتهای لیست اضافه می‌کند.


چالش و حل مسئله بخش لیست

سؤال: فرض کنید لیستی به صورت a = [3, 1, 4] دارید. چطور می‌توان بدون ساختن لیست جدید و با کمک متدها، آن را به شکل [1, 3, 4, 10, 20] درآورد؟

a = [3, 1, 4]

a.sort()        # مرحله اول: مرتب‌سازی -> [1, 3, 4]
a.append(10)    # مرحله دوم: افزودن ۱۰ به انتها -> [1, 3, 4, 10]
a.insert(3, 20) # مرحله سوم: درج ۲۰ در ایندکس ۳ -> [1, 3, 4, 20, 10]

print(a)


تاپل

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

متدهای محدود تاپل

به دلیل ماهیت غیرقابل تغییر تاپل، متدهای اصلاحی (مثل append یا remove) در آن وجود ندارند و ابزارهای آن به بخش جستجو و شمارش محدود می‌شوند:

  • count(x): محاسبه تعداد دفعات تکرار مقدار x در تاپل.
  • index(x): یافتن شماره خانه (ایندکس) اولین جایگاه حضور مقدار x.

بررسی کدهای نمونه برای تاپل

شاید بپرسید اگر مجبور شدیم داده درون یک تاپل را تغییر دهیم چه کار کنیم؟ راهکار این است که موقتاً نقاب تاپل را برداریم، آن را به لیست تبدیل کنیم، تغییرات را اعمال کنیم و دوباره به حالت اول برگردانیم:

t = ('saleh', 'askari', 23)
print(type(t)) # خروجی: <class 'tuple'>

t = list(t)    # تبدیل به لیست پویا
t[0] = 'ahmad' # اعمال تغییر روی اندیس صفر

t = tuple(t)   # تبدیل مجدد به تاپل امن و قفل شده
print(t)       # خروجی: ('ahmad', 'askari', 23)

در نمونه زیر، استفاده از متدهای جستجو روی مقادیر ثابت نمایش داده شده است:

data = (5, 2, 9, 2, 2)
print(data.count(2))  # خروجی: 3 (عدد ۲ سه بار تکرار شده است)
print(data.index(2))  # خروجی: 1 (اولین بار عدد ۲ در اندیس ۱ دیده شده)


چالش و حل مسئله بخش تاپل

سؤال: چطور می‌توان از تاپل t = (10, 20, 30, 40, 50)، مقادیر وسطی یعنی (20, 30, 40) را با اسلایس استخراج کرد؟

t = (10, 20, 30, 40, 50)
result = t[1:4]
print(result) # خروجی: (20, 30, 40)

برش را از اندیس ۱ (عدد ۲۰) شروع می‌کنیم و تا اندیس ۴ تنظیم می‌کنیم تا خود خانه چهارم برداشته نشود و فقط سه عدد میانی جدا شوند.


دیکشنری

در زندگی واقعی ما برای پیدا کردن معنی یک کلمه، به سراغ شماره صفحه نمی‌رویم، بلکه خود کلمه را جستجو می‌کنیم. دیکشنری در پایتون نیز دقیقاً همین‌طور عمل می‌کند. یک ساختار داده غیردنباله‌ای است که بر پایه نگاشت کلید به مقدار (Key-Value) عمل می‌کند. در دیکشنری خبری از ایندکس‌های عددی (صفر، یک، دو) نیست؛ به جای آن، ما جفت‌های کلید و مقدار داریم. شما یک کلید منحصربه‌فرد (مانند کلمه یا کد ملی) می‌دهید و پایتون مقدار متناظر با آن (مانند معنی یا مشخصات فرد) را به شما تحویل می‌دهد. عناصر دیکشنری درون آکولاژ {} قرار می‌گیرند.

متدهای ساختاری دیکشنری

متد شرح عملکرد
keys() بازگرداندن مجموعه‌ای از تمام کلیدهای موجود در دیکشنری
values() بازگرداندن مجموعه‌ای از تمام مقادیر ذخیره‌شده در دیکشنری
items() بازگرداندن جفت‌های کلید و مقدار به صورت توالی‌هایی از تاپل‌ها
update(dict) به‌روزرسانی دیکشنری جاری با استفاده از داده‌های یک دیکشنری دیگر
pop(key) حذف کلید تعیین‌شده و بازگرداندن مقدار آن
popitem() حذف و بازگرداندن آخرین جفت کلید و مقدار وارد شده
get(key, default) دریافت مقدار یک کلید (جلوگیری از بروز خطا در صورت عدم وجود کلید با بازگرداندن مقدار پیش‌فرض)

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

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

d = {'name': 'saleh', 'family': 'askari', 'birth_day': (1382, 7, 22)}

for key in d.keys():
    print(f'{key} -> {d[key]}')

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

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

students = []

student = {
    'student_code': '40113119821',
    'name': ('saleh', 'askari'),
    'birth_day': (1382, 7, 22),
    'course': {
        'AI': {'unit': 3 , 'score': 20},
        'DB': {'unit': 3 , 'score': 15},
        'QH': {'unit': 2 , 'score': 11}
    }
}
students.append(student)

for i in range(len(students)):
    for key in students[i].keys():
        if key == 'name':
            print(f"name: {students[i][key][0]} \t family: {students[i][key][1]}")
            continue
        if key == 'birth_day':
            print(f"birth day: {students[i][key][0]}/{students[i][key][1]}/{students[i][key][2]}")
            continue
        print(f'{key}: {students[i][key]}')

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


چالش و حل مسئله بخش دیکشنری

سؤال: چطور می‌توان از میان یک دیکشنری از نمرات دانش‌آموزان (scores)، نام کسانی که نمره‌شان بالای ۱۵ است را فیلتر کرد و در یک لیست مجزا گذاشت؟

scores = {"Ali": 18, "Sara": 20, "Reza": 12}
high_scorers = []

for name, score in scores.items():
    if score > 15:
        high_scorers.append(name)

print(high_scorers) # خروجی: ['Ali', 'Sara']

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


ست یا مجموعه

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

متدهای مدیریت ست

  • add(x): افزودن عضو جدید x به مجموعه.
  • discard(x) / remove(x): حذف عضو مشخص از مجموعه (متد discard بر خلاف remove در صورت عدم وجود عضو، خطا نمی‌دهد).

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

برای اضافه کردن عضو جدید به یک ست، از متد add استفاده می‌کنیم. در صورت ورود داده‌های تکراری، پایتون به صورت خودکار مقادیر مازاد را حذف می‌کند:

s = {1, 2, 3, 4, 5}
s.add(6)
print(s) # خروجی: {1, 2, 3, 4, 5, 6}


چالش و حل مسئله بخش ست

سؤال: اگر دو مجموعه داشته باشیم، چطور می‌توانیم عملیات‌های اصلی ریاضی (اجتماع، اشتراک، تفاضل و تفاضل متقارن) را روی آن‌ها پیاده کنیم؟

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

# ۱. پیدا کردن اعضای مشترک در هر دو مجموعه (اشتراک)
print(a & b) # خروجی: {3, 4}

# ۲. اعضایی که در هر دو مجموعه هستند ولی مشترک نیستند (تفاضل متقارن)
print(a ^ b) # خروجی: {1, 2, 5, 6}

# ۳. اعضایی که در مجموعه اول هستند اما در مجموعه دوم وجود ندارند (تفاضل)
print(a - b) # خروجی: {1, 2}

# ۴. ترکیب و یکپارچه‌سازی تمام اعضای هر دو مجموعه (اجتماع)
print(a | b) # خروجی: {1, 2, 3, 4, 5, 6}


دریافت ورودی مستمر تا زمان خروج

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

score = []

while True:
    enter = int(input('Enter a Score(enter -1 to end): '))
    
    if enter == -1:
        break # خروج فوری از حلقه در صورت ورود عدد ۱-
    
    score.append(enter) # افزودن نمره به لیست نمرات
	
print(f'Score: {score}')

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


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

بیایید این جلسه را از پایه دوباره بکوبیم و بسازیم. لحن متن را مثل قبل کاملاً صمیمی، ساده و روان نگه می‌داریم، هر مفهوم را با تشبیه‌های واقعی باز می‌کنیم، کدهای نمونه را بسیار کاربردی‌تر و ملموس‌تر می‌نویسیم و هیچ شماره‌گذاری یا علائم شمارشی در عناوین استفاده نمی‌کنیم.


جلسه پنجم: توابع و بومی‌سازی کدها


مفهوم تابع و علت استفاده از آن

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

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

استفاده از تابع چند فایده بزرگ برای ما دارد:

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

تعریف و فراخوانی تابع

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

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

def say_hello():
    print("سلام، به کلاس پایتون خوش آمدید!")

# صدا زدن تابع
say_hello()

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


توابع ورودی‌محور یا آرگومان‌ها

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

توابع با یک یا چند ورودی

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

def greet(name):
    print("سلام", name, "روزت بخیر!")

greet("علی")
greet("سارا")

در این کد، یک بار کلمه "علی" درون متغیر name قرار می‌گیرد و پیام برای علی چاپ می‌شود و در دور بعد، این کار برای "سارا" تکرار می‌شود.

اگر تابع چند ورودی داشته باشد، ترتیب فرستادن مقدارها بسیار مهم است:

def show_score(student_name, score):
    print(f"نمره درس پایتون {student_name} برابر است با: {score}")

show_score("رضا", 18)
show_score(20, "مریم") # این خط جابجا چاپ می‌شود و منطق کد را به هم می‌زند

در فراخوانی اول، رضا به عنوان نام و ۱۸ به عنوان نمره می‌نشیند که کاملاً درست است. اما در خط بعدی به دلیل رعایت نکردن ترتیب، عدد ۲۰ به عنوان نام دانشجو معرفی می‌شود که یک غلط منطقی ایجاد می‌کند.


معجزه مقدار بازگشتی یا دستور return

این بخش یکی از مهم‌ترین و حیاتی‌ترین بخش‌های یادگیری توابع است. فرض کنید به دوستتان می‌گویید: «روی تخته سیاه بنویس ۲ به علاوه ۲ چند می‌شود؟» او این کار را می‌کند و شما فقط نتیجه را روی تخته می‌بینید. این کار شبیه دستور print است.

حالا فرض کنید به دوستتان می‌گویید: «۲ را با ۲ جمع کن و جوابش را توی دستت نگه دار و به من پس بده تا من بتوانم با آن جواب، یک پراید بخرم!» به این کار پس دادن یا برگشت دادن مقدار می‌گویند که در پایتون با دستور return انجام می‌شود.

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

بررسی تفاوت عمیق print و return

بگذارید با یک کد ملموس این تفاوت بزرگ را ببینیم:

def func_print():
    print("سلام")

def func_return():
    return "سلام"

x = func_print()
y = func_return()

print("مقدار ذخیره شده در ایکس:", x)
print("مقدار ذخیره شده در وای:", y)

خروجی این کد شگفت‌انگیز است: وقتی func_print صدا زده می‌شود، کلمه "سلام" روی صفحه چاپ می‌شود. اما این تابع هیچ چیزی به متغیر x پس نمی‌دهد! بنابراین دست متغیر x خالی می‌ماند و مقدار آن برابر با None (یعنی هیچ چیز) می‌شود. اما وقتی func_return اجرا می‌شود، کلمه "سلام" روی صفحه چاپ نمی‌شود، بلکه این رشته به عنوان یک کادو به متغیر y پس داده می‌شود. حالا متغیر y صاحب کلمه "سلام" است و در خطوط بعدی به راحتی چاپ یا استفاده می‌شود.

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


مدیریت پیشرفته ورودی‌ها

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

ورودی‌های پیش‌فرض

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

def power(base, exp=2):
    return base ** exp

print(power(5))    
print(power(5, 3)) 

در فراخوانی اول، ما فقط یک عدد (پایه) فرستادیم. پایتون متوجه می‌شود که ورودی دوم فرستاده نشده، پس خودش عدد ۲ را برای توان در نظر می‌گیرد و ۵ را به توان ۲ می‌رساند (جواب ۲۵). در فراخوانی دوم، چون ما خودمان عدد ۳ را فرستادیم، مقدار پیش‌فرض نادییده گرفته می‌شود و ۵ به توان ۳ می‌رسد (جواب ۱۲۵).

ورودی‌های کلیدی

اگر یادتان باشد گفتیم در توابع چند ورودی، ترتیب بسیار مهم است. اما اگر نام متغیرهای ورودی را موقع صدا زدن بنویسید، دیگر ترتیب هیچ اهمیتی ندارد:

def display_info(name, job):
    print(f"نام: {name} | شغل: {job}")

display_info(job="برنامه‌نویس", name="صالح")

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


جادوی ستاره‌ها در ورودی‌های نامشخص

گاهی اوقات شما تابعی می‌نویسید که اصلاً نمی‌دانید کاربر قرار است چند تا ورودی به آن بدهد! مثلاً می‌خواهید تابعی بسازید که معدل نمرات را حساب کند؛ یک کاربر ممکن است ۳ نمره وارد کند و کاربر دیگر ۱۰ نمره. پایتون برای این کار دو ابزار فوق‌العاده با علامت ستاره دارد.

ابزار تک‌ستاره یا args*

وقتی پشت یک متغیر ورودی یک ستاره بگذارید، به پایتون می‌گویید: «هر چقدر عدد یا داده آمد را قبول کن و همه آن‌ها را درون یک کیسه از نوع تاپل (Tuple) بسته‌بندی کن.»

def calculate_average(*numbers):
    total = sum(numbers)
    return total / len(numbers)

print("معدل سری اول:", calculate_average(10, 20, 30))
print("معدل سری دوم:", calculate_average(18, 19, 15, 20, 14))

تابع به صورت پویا متوجه می‌شود که در بار اول یک تاپل ۳ عضوی و در بار دوم یک تاپل ۵ عضوی دارد و بدون هیچ مشکلی محاسبات را انجام می‌دهد.

ابزار دوستاره یا kwargs

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

def save_user_profile(**info):
    print("نوع داده دریافتی:", type(info))
    for key, value in info.items():
        print(f"{key}: {value}")

save_user_profile(username="saleh_99", age=23, country="Iran")

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


قلمرو متغیرها: محلی در برابر سراسری

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

متغیرهای محلی

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

def test_scope():
    inside_variable = 50
    print("داخل تابع:", inside_variable)

test_scope()
# print(inside_variable) -> این خط خطای شدید می‌دهد چون بیرون از تابع چنین متغیری وجود ندارد!

متغیرهای سراسری و کلمه کلیدی global

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

اگر یک تابع بخواهد متغیر سراسری بیرون از خودش را دستکاری کند و مقدار جدیدی درونش بریزد، باید از کلمه کلیدی global استفاده کند تا به پایتون بگوید قصد دارد مستقیماً روی متغیر اصلی اثر بگذارد:

sugar_bags = 10 # متغیر سراسری

def Hiringـnewـemployee():
    sugar_bags = 20 # این یک متغیر محلی جدید همنام است و به متغیر بیرون کاری ندارد

def Hiringـwarehouseـmanager():
    global sugar_bags
    sugar_bags = 50 # این دستور متغیر اصلی بیرون را تغییر می‌دهد

Hiringـnewـemployee()
print("بعد از کارمند:", sugar_bags) # هنوز ۱۰ است

Hiringـwarehouseـmanager()
print("بعد از رئیس انبار:", sugar_bags) # به ۵۰ تغییر یافت


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

توابع بازگشتی (Recursive Functions) یکی از مباحث جذاب و چالش‌برانگیز برنامه‌نویسی هستند. یک تصویر تکرارشونده در آینه را تصور کنید که در دل خودش یک آینه دیگر دارد. تابع بازگشتی تابعی است که در داخل بدنه خودش، دوباره خودش را فراخوانی می‌کند!

برای اینکه این نوع توابع در یک حلقه بی‌نهایت گیر نکنند و کامپیوتر را هنگی نکنند، داشتن یک شرط توقف (Base Case) کاملاً اجباری است. یعنی باید یک جا بگوییم «دیگر بس است، برگرد».

مثال ملموس: محاسبه فاکتوریل

ریاضی را یادمان هست؛ فاکتوریل عدد ۵ یعنی $5 \times 4 \times 3 \times 2 \times 1$. ما می‌توانیم بگوییم فاکتوریل ۵ برابر است با ۵ ضربدر فاکتوریل ۴! این زنجیره همین‌طور به عقب می‌رود تا به عدد ۱ برسد.

def factorial(n):
    # شرط توقف حیاتی
    if n == 1:
        return 1
    
    # گام بازگشتی که تابع دوباره خودش را با یک عدد کمتر صدا می‌زند
    return n * factorial(n - 1)

print("فاکتوریل عدد ۵ برابر است با:", factorial(5))

وقتی factorial(5) صدا زده می‌شود، پایتون محاسبه می‌کند: 5 * factorial(4). اما هنوز جواب فاکتوریل ۴ را نمی‌داند! پس می‌رود سراغ محاسبه فاکتوریل ۴ که می‌شود 4 * factorial(3). این نردبان تا پایین ساخته می‌شود و وقتی به عدد ۱ رسید، شرط توقف اجرا شده و مقادیر زنجیره‌وار از پایین به بالا ضرب شده و جواب نهایی ۱۲۰ به دست می‌آید.


تمرین‌ها و چالش‌ها

برای اینکه تمام مفاهیم بالا کاملاً ملکه ذهن شما شوند، چند چالش واقعی و کاربردی را با هم حل می‌کنیم.

چالش فیلتر کردن و محاسبات نمرات

سؤال: تابعی بنویسید که لیستی از نمرات دانش‌آموزان را بگیرد، نمرات زیر ۱۰ را حذف کند (چون افتاده‌اند!)، و معدل نمرات قبولی را حساب کرده و به برنامه برگرداند.

def clean_and_average(all_scores):
    passed_scores = []
    
    for score in all_scores:
        if score >= 10:
            passed_scores.append(score)
            
    if len(passed_scores) == 0:
        return 0 # اگر هیچکس پاس نشده بود، معدل صفر است
        
    total = sum(passed_scores)
    return total / len(passed_scores)

scores_list = [18, 7, 15, 9, 20, 11]
final_result = clean_and_average(scores_list)
print("معدل افراد قبولی کلاس:", final_result)

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

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

def print_invoice(customer_name, **items):
    print(f"--- فاکتور خرید آقای/خانم {customer_name} ---")
    total_price = 0
    
    for item_name, price in items.items():
        print(f"کالا: {item_name} \t قیمت: {price} تومان")
        total_price += price
        
    print("---------------------------------------")
    return total_price

# صدا زدن تابع با اقلام خرید مختلف
final_bill = print_invoice("صالح", لپ_تاپ=35000000, موس=450000, کیبورد=1200000)
print(f"مبلغ قابل پرداخت نهایی: {final_bill} تومان")

چالش شمارش معکوس به روش بازگشتی

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

def countdown(number):
    # شرط توقف
    if number < 0:
        print("پرتاب!")
        return
        
    print("شماره:", number)
    # صدا زدن خود با یک عدد کمتر
    countdown(number - 1)

countdown(5)


جلسه ششم: مدیریت فایل‌ها و کنترل خطاهای ساختاری


مفهوم کار با فایل و اهمیت آن در برنامه‌نویسی

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

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


انواع فایل‌ها و شیوه‌های باز کردن آن‌ها

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

برای باز کردن فایل از تابع open استفاده می‌شود. این تابع دو ورودی اصلی می‌گیرد: نام فایل و حالت (Mode) باز کردن فایل. حالتهای اصلی عبارتند از:

  • حالت r (Reading): فقط برای خواندن فایل. اگر فایل وجود نداشته باشد، برنامه خطا می‌دهد.
  • حالت w (Writing): برای نوشتن در فایل. اگر فایل وجود نداشته باشد، آن را می‌سازد. اگر وجود داشته باشد، تمام محتویات قبلی آن را پاک می‌کند و از نو می‌نویسد!
  • حالت a (Appending): برای اضافه کردن داده به انتهای فایل قبلی، بدون اینکه محتویات گذشته پاک شوند.

دستور هوشمندانه with

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


کار با فایل‌های متنی ساده (txt)

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

نوشتن درون فایل متنی

با استفاده از متد write می‌توانیم متن‌ها را درون فایل بنویسیم. برای رفتن به خط بعدی از کاراکتر \n استفاده می‌کنیم.

with open("students.txt", "w", encoding="utf-8") as file:
    file.write("صالح عسکری\n")
    file.write("علی مرادی\n")
    file.write("محمد نظری\n")

عبارت encoding="utf-8" به پایتون می‌گوید که ما می‌خواهیم حروف فارسی را بدون خراب شدن و به شکل درست ذخیره کنیم.

خواندن از فایل متنی

برای خواندن فایل راه‌های مختلفی وجود دارد. متد read کل فایل را یکجا می‌خواند. اما بهترین و بهینه‌ترین روش برای فایل‌های خطی، چرخیدن روی فایل با یک حلقه for ساده است. پایتون هر خط را به عنوان یک عضو در نظر می‌گیرد:

with open("students.txt", "r", encoding="utf-8") as file:
    for line in file:
        # متد strip فاصله‌ها و اینترهای مازاد آخر خط را پاک می‌کند
        print("نام دانشجو:", line.strip())


آشنایی با ساختار محبوب JSON

فایل‌های متنی ساده برای یادداشت کارهای روزمره عالی هستند، اما برای ذخیره اطلاعات پیچیده (مثل اطلاعات یک دانشجو به همراه نمرات و دروسش) مناسب نیستند. اینجاست که فرمت JSON وارد میدان می‌شود. جی‌سان مخفف JavaScript Object Notation است و استاندارد جهانی برای انتقال و ذخیره داده‌ها در وب و اپلیکیشن‌ها به شمار می‌رود.

ظاهر ساختار JSON دقیقاً شبیه به دیکشنری‌ها و لیست‌های تودرتو در پایتون است. برای کار با این فرمت، پایتون کتابخانه آماده‌ای به نام json دارد که باید آن را در بالای برنامه خود import کنیم. دو متد اصلی که با آن‌ها کار می‌کنیم عبارتند از:

  • json.dump: تبدیل دیکشنری یا لیست پایتون به ساختار جی‌سان و ذخیره آن درون فایل.
  • json.load: خواندن فایل جی‌سان و تبدیل مستقیم آن به دیکشنری یا لیست پایتون.

ذخیره اطلاعات ساختاریافته در فایل JSON

فرض کنید می‌خواهیم اطلاعات کامل پروفایل یک کاربر را به صورت دسته‌بندی شده ذخیره کنیم:

import json

user_profile = {
    "username": "saleh_askari",
    "skills": ["Python", "Linux", "Network"],
    "is_active": True,
    "age": 23
}

with open("profile.json", "w", encoding="utf-8") as json_file:
    # آرگومان indent برای این است که فایل جی‌سان با تب و فاصله‌های خوانا ذخیره شود
    json.dump(user_profile, json_file, indent=4)

اگر فایل profile.json را باز کنید، می‌بینید داده‌ها با یک نظم ساختاری فوق‌العاده ذخیره شده‌اند.

خواندن اطلاعات از فایل JSON

حالا می‌خواهیم همان فایل جی‌سان را بخوانیم و دوباره در برنامه به عنوان یک دیکشنری با آن رفتار کنیم:

import json

with open("profile.json", "r", encoding="utf-8") as json_file:
    data = json.load(json_file)

print("نوع داده بازیابی شده:", type(data))
print("نام کاربری:", data["username"])
print("اولین مهارت:", data["skills"][0])


کار با فایل‌های جدول‌بندی شده (CSV)

فایل‌های CSV مخفف Comma-Separated Values هستند. این فایل‌ها اطلاعات را به صورت سطری و ستونی ذخیره می‌کنند و اعضای هر سطر را با علامت ویرگول (comma) از هم جدا می‌کنند. بزرگترین مزیت فایل‌های CSV این است که مستقیماً در نرم‌افزارهای اکسل (Excel) یا گوگل شیت به صورت جدول‌های تمیز باز می‌شوند.

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

نوشتن داده‌های جدولی در فایل CSV

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

import csv

products = [
    ["نام کالا", "قیمت (تومان)", "موجود در انبار"],
    ["لپ‌تاپ", 35000000, "بله"],
    ["موس", 450000, "بله"],
    ["کیبورد", 1200000, "خیر"]
]

with open("inventory.csv", "w", newline="", encoding="utf-8") as csv_file:
    writer = csv.writer(csv_file)
    # متد writerows کل لیست‌های تودرتو را به صورت سطری در فایل می‌نویسد
    writer.writerows(products)

نکته: عبارت newline="" برای این است که در سیستم‌عامل‌های مختلف، خط‌های سفید مازاد بین سطرها ایجاد نشود.

خواندن داده‌ها از فایل CSV

حالا بیایید جدول ساخته شده را بخوانیم و اطلاعاتش را سطر به سطر نمایش دهیم:

import csv

with open("inventory.csv", "r", encoding="utf-8") as csv_file:
    reader = csv.reader(csv_file)
    for row in reader:
        # متغیر row در هر دور یک لیست از خانه‌های آن سطر است
        print(f"کالا: {row[0]} \t| قیمت: {row[1]} \t| موجودی: {row[2]}")


مدیریت خطا و استثناها (Try, Except, Finally)

در دنیای واقعی، همه چیز همیشه خوب پیش نمی‌رود! فرض کنید برنامه‌ای نوشته‌اید که فایلی را از روی دسکتاپ بخواند، اما کاربر اشتباهاً آن فایل را پاک کرده است. یا از کاربر می‌خواهید سن خودش را وارد کند، اما او به حروف می‌نویسد "بیست". در این حالت‌ها، برنامه شما با مغز به زمین می‌خورد، کرش (Crash) می‌کند و متن‌های قرمز و ترسناک خطای سیستم را به کاربر نشان می‌دهد.

به این خطاهای زمان اجرا، استثنا (Exception) می‌گویند. یک برنامه‌نویس حرفه‌ای باید این خطاها را پیش‌بینی و مدیریت کند تا برنامه حتی در صورت بروز مشکل، به آرامی به کارش ادامه دهد و پیام مناسبی به کاربر نشان دهد. ابزار پایتون برای این کار بلوک‌های try و except است.

ساختار بلوک مدیریت خطا

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

مثال ملموس مدیریت خطا در تبدیل داده و فایل

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

try:
    with open("numbers.txt", "r") as file:
        content = file.read()
        number = int(content) # احتمال بروز خطای تبدیل متن به عدد
        result = 100 / number # احتمال بروز خطای تقسیم بر صفر
        print("نتیجه محاسبات:", result)

except FileNotFoundError:
    print("خطا: فایلی به نام numbers.txt در سیستم پیدا نشد!")

except ValueError:
    print("خطا: محتوای داخل فایل عدد نیست و قابل تبدیل نمی‌باشد!")

except ZeroDivisionError:
    print("خطا: عدد داخل فایل صفر است و تقسیم بر صفر در ریاضی ممکن نیست!")

except Exception as error:
    # این بخش به عنوان یک تور نجات عمومی برای بقیه خطاهای پیش‌بینی نشده عمل می‌کند
    print(f"یک خطای غیرمنتظره رخ داد: {error}")

finally:
    print("عملیات بررسی فایل به پایان رسید.")

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

چند خطای معروف که باید بلد باشیم:

ValueError      # مقدار نامعتبر
TypeError       # نوع داده اشتباه
IndexError      # اندیس خارج از محدوده
KeyError        # کلید پیدا نشد
FileNotFoundError # فایل پیدا نشد
ZeroDivisionError # تقسیم بر صفر
NameError       # متغیر تعریف نشده
AttributeError  # ویژگی یا متد وجود ندارد
Exception       # پدر اکثر خطاها

چالش‌ها و تمرین‌ها

برای تثبیت کامل مفاهیم این جلسه، سه چالش ترکیبی و پیشرفته را همراه با تحلیل خط‌به‌خط روش حل با هم بررسی می‌کنیم.

چالش سیستم ثبت‌نام و لاگین با فایل متنی

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

شیوه حل کردن و توضیح منطق کد: برای حل این مسئله، ابتدا باید چک کنیم آیا فایل وجود دارد یا خیر. بهترین راه این است که در حالت خواندن (r) به سراغ فایل برویم؛ اگر فایل نبود، خطای FileNotFoundError رخ می‌دهد که در بخش except آن را مهار می‌کنیم و فایل را برای بار اول می‌سازیم. برای جلوگیری از تکراری بودن، تمام کدهای ملی داخل فایل را می‌خوانیم و در یک مجموعه (Set) می‌ریزیم (چون در ست عضو تکراری نداریم و جستجو در آن سریع است). اگر کد ملی جدید در این مجموعه نبود، آن را به انتهای فایل الحاق (a) می‌کنیم.

def load_national_codes():
    codes = set()
    try:
        with open("database.txt", "r", encoding="utf-8") as file:
            for line in file:
                codes.add(line.strip())
    except FileNotFoundError:
        # اگر فایل نبود، یعنی هنوز کسی ثبت‌نام نکرده، پس یک فایل خالی می‌سازیم
        with open("database.txt", "w", encoding="utf-8") as file:
            pass
    return codes

def register_user(new_code):
    existing_codes = load_national_codes()
    
    if new_code in existing_codes:
        print("خطا: این کد ملی قبلاً در سامانه ثبت شده است!")
        return
        
    with open("database.txt", "a", encoding="utf-8") as file:
        file.write(new_code + "\n")
    print("ثبت‌نام شما با موفقیت انجام شد.")

# تست برنامه
register_user("1234567890")
register_user("1234567890") # این بار پیام تکراری بودن می‌دهد

چالش پشتیبان‌گیری و تبدیل اطلاعات از CSV به JSON

سؤال: یک فایل اطلاعات فروشگاهی به فرمت جدولی (CSV) داریم. برنامه‌ای بنویسید که این فایل را بخواند، قیمت هر محصول را با احتساب ۹ درصد مالیات بر ارزش افزوده محاسبه کند، و خروجی نهایی را به صورت یک فایل مدرن ساختاریافته (JSON) پشتیبان‌گیری کند.

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

import csv
import json

def convert_csv_to_json(csv_filename, json_filename):
    data_list = []
    
    try:
        with open(csv_filename, "r", encoding="utf-8") as csv_file:
            reader = csv.reader(csv_file)
            # رد کردن سطر اول (سرستون‌ها)
            header = next(reader)
            
            for row in reader:
                item_name = row[0]
                base_price = float(row[1])
                # محاسبه قیمت با ۹ درصد مالیات
                final_price = base_price * 1.09
                
                # ساخت دیکشنری برای هر محصول
                product_dict = {
                    "product_name": item_name,
                    "original_price": base_price,
                    "tax_included_price": round(final_price, 2)
                }
                data_list.append(product_dict)
                
        # نوشتن خروجی در فایل JSON
        with open(json_filename, "w", encoding="utf-8") as json_file:
            json.dump(data_list, json_file, indent=4, ensure_ascii=False)
        print("تبدیل فرمت و محاسبات مالیات با موفقیت انجام و ذخیره شد.")
        
    except FileNotFoundError:
        print("خطا: فایل CSV مبدا یافت نشد!")
    except ValueError:
        print("خطا: برخی قیمت‌ها در فایل معتبر یا عدد نیستند!")

# شبیه‌سازی اجرای چالش
# برای اجرای واقعی، ابتدا باید فایل inventory.csv از چالش‌های قبلی وجود داشته باشد.
convert_csv_to_json("inventory.csv", "backup_products.json")

چالش ماشین‌حساب ضد کرش با دریافت مستمر ورودی

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

شیوه حل کردن و توضیح منطق کد: در داخل یک حلقه while True دستورات دریافت ورودی را می‌نویسیم. برای اینکه برنامه با ورودی‌های اشتباه بسته نشود، کل فرآیند محاسبات و دریافت‌ها را درون شکم یک بلوک try قرار می‌دهیم. اگر کاربر در هر مرحله کلمه "exit" را نوشت، با دستور break از حلقه فرار می‌کنیم. در غیر این صورت مقادیر را به عدد تبدیل می‌کنیم. خطای تبدیل متن به عدد توسط ValueError و خطای تقسیم بر صفر توسط ZeroDivisionError شکار می‌شوند و حلقه بدون وقفه به دور بعدی هدایت می‌شود.

print("--- به ماشین‌حساب امن پایتون خوش آمدید (برای خروج exit بنویسید) ---")

while True:
    try:
        user_input1 = input("عدد اول: ").strip()
        if user_input1.lower() == "exit":
            break
            
        operator = input("عملگر ریاضی (+، -، *، /): ").strip()
        
        user_input2 = input("عدد دوم: ").strip()
        if user_input2.lower() == "exit":
            break
            
        # تبدیل ورودی‌ها به عدد اعشاری
        num1 = float(user_input1)
        num2 = float(user_input2)
        
        # انجام محاسبات بر اساس عملگر
        if operator == "+":
            print("نتیجه:", num1 + num2)
        elif operator == "-":
            print("نتیجه:", num1 - num2)
        elif operator == "*":
            print("نتیجه:", num1 * num2)
        elif operator == "/":
            print("نتیجه:", num1 / num2)
        else:
            print("خطا: عملگر وارد شده معتبر نیست!")
            
        print("-" * 30)
        
    except ValueError:
        print("خطای ورودی: لطفا فقط از اعداد استفاده کنید! دوباره تلاش کنید.")
        print("-" * 30)
    except ZeroDivisionError:
        print("خطای ریاضی: تقسیم هر عددی بر صفر ناپذیر است! دوباره تلاش کنید.")
        print("-" * 30)

print("از استفاده شما متشکریم. خروج از برنامه.")


جلسه هفتم: برنامه‌نویسی شی‌گرا (OOP)


مفهوم کلاس و شی

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

برنامه‌نویسی شی‌گرا یا به اختصار OOP، یک نگاه جدید به دنیای کدنویسی است. در این روش، ما تلاش می‌کنیم دنیای واقعی را شبیه‌سازی کنیم. در دنیای واقعی، ما با «اشیاء» سر و کار داریم؛ ماشین، انسان، لپ‌تاپ، یا حتی یک حساب بانکی. هر کدام از این اشیاء دو ویژگی اصلی دارند:

  • مشخصات (Attributes): داده‌ها یا اطلاعاتی که مربوط به آن شیء است (مثلاً رنگ ماشین، نام انسان).
  • رفتارها (Methods): کارهایی که آن شیء می‌تواند انجام دهد (مثلاً حرکت کردن ماشین، حرف زدن انسان).

تفاوت کلاس و شیء چیست؟

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

در برنامه‌نویسی پایتون:

  • کلاس (Class): همان نقشه ساخت، قالب یا دستورالعمل کلی است.
  • شیء (Object): ماشین واقعی است که از روی آن نقشه ساخته شده است. به فرآیند ساختن یک شیء از روی کلاس، نمونه‌سازی (Instantiation) می‌گویند.

ساخت اولین کلاس و شیء در پایتون

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

class Car:
    pass # فعلاً بدنه کلاس را خالی می‌گذاریم

حالا بیایید از روی این قالب، دو شیء یا ماشین واقعی بسازیم:

car1 = Car()
car2 = Car()

print(type(car1)) # خروجی نشان می‌دهد که این یک شیء از کلاس کار است


سازنده‌های کلاس و متد init

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

در پایتون، برای اینکه به محض ساخته شدن یک شیء، مشخصات اولیه آن را تنظیم کنیم، از یک تابع جادویی و ویژه به نام سازنده (Constructor) استفاده می‌کنیم. این تابع نامش همیشه ثابت است: __init__ (دو خط تیره در ابتدا و دو خط تیره در انتها).

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

وقتی کدهای مربوط به کلاس را می‌نویسید، پایتون از کجا بداند که ویژگی‌های کدام ماشین را باید تغییر دهد؟ اینجاست که کلمه کلیدی self وارد می‌شود. self اشاره به همان شیئی دارد که همین الان در حال ساخته شدن یا کار کردن است. وقتی می‌گوییم self.color یعنی رنگِ «همین ماشینی که داری می‌سازی».

class Car:
    # تابع سازنده با مشخصات اولیه
    def __init__(self, brand, model, color):
        self.brand = brand   # برند ماشین
        self.model = model   # مدل ماشین
        self.color = color   # رنگ ماشین
        self.speed = 0       # سرعت اولیه همه ماشین‌ها صفر است

# حالا موقع ساختن شیء، مشخصات را می‌فرستیم
my_car = Car("Toyota", "Corolla", "White")
friend_car = Car("BMW", "X5", "Black")

print(my_car.brand)     # خروجی: Toyota
print(friend_car.brand) # خروجی: BMW


ویژگی‌ها و رفتارهای کلاس (Methods)

مشخصاتی که درون __init__ ساختیم، متغیرهای شیء هستند. حالا وقت آن است که به کلاس خود «رفتار» اضافه کنیم. رفتارهای یک کلاس، همان توابعی هستند که درون کلاس نوشته می‌شوند و به آن‌ها متد (Method) می‌گویند. تنها تفاوت این توابع با توابع معمولی این است که همیشه اولین ورودی آن‌ها باید self باشد تا به داده‌های خودِ شیء دسترسی داشته باشند.

class Car:
    def __init__(self, brand, model, color):
        self.brand = brand
        self.model = model
        self.color = color
        self.speed = 0
    
    # متد نمایش اطلاعات
    def show_info(self):
        print(f"ماشین: {self.brand} {self.model} | رنگ: {self.color} | سرعت: {self.speed}")
        
    # متد گاز دادن و افزایش سرعت
    def accelerate(self, amount):
        self.speed += amount
        print(f"ماشین گاز داد! سرعت فعلی: {self.speed}")

# استفاده از متدها
my_car = Car("Persia", "Pars", "Gray")
my_car.show_info() 

my_car.accelerate(40)
my_car.accelerate(20)
my_car.show_info()


وراثت (Inheritance)

فرض کنید می‌خواهید یک کلاس جدید برای «کامیون» و یک کلاس برای «موتورسیکلت» بسازید. کامیون و موتورسیکلت هم مثل ماشین ویژگی‌هایی مثل برند، مدل، رنگ و سرعت دارند و کار گاز دادن را هم انجام می‌دهند. آیا منطقی است تمام این کدهایی که برای کلاس Car نوشتیم را دوباره کپی کنیم و برای کامیون بنویسیم؟ اصلاً!

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

پیاده‌سازی وراثت و دستور super

بیایید یک کلاس پایه به نام Vehicle (وسیله نقلیه) بسازیم و کلاس‌های ماشین و کامیون را فرزند آن کنیم:

# کلاس پدر
class Vehicle:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
        
    def honk(self):
        print("بوق بوق!")

# کلاس فرزند (کلاس درون پرانتز یعنی از چه کسی ارث می‌برد)
class Truck(Vehicle):
    def __init__(self, brand, model, capacity):
        # دستور super مشخصات را به سازنده کلاس پدر پاس می‌دهد
        super().__init__(brand, model)
        self.capacity = capacity # ویژگی اختصاصی کامیون (ظرفیت بار)
        
    def load_cargo(self):
        print(f"کامیون بارگیری شد. ظرفیت: {self.capacity} تن")

# ساخت شیء از کلاس فرزند
heavy_truck = Truck("Volvo", "FH500", 25)

# متد ارث‌برده شده از پدر
heavy_truck.honk() 

# متد اختصاصی خود فرزند
heavy_truck.load_cargo()


چندریختی (Polymorphism)

کلمه چندریختی یا پلی‌مورفیسم به زبان ساده یعنی «یک رفتار ثابت با شکل‌های اجرای متفاوت».

فرض کنید یک دکمه «پخش کردن صدا» داریم. اگر این دکمه را روی شیءِ «گربه» فشار دهیم، صدای میو می‌دهد. اگر همان دکمه را روی شیءِ «سگ» فشار دهیم، صدای هاو می‌دهد. نام متد در هر دو کلاس یکی است (make_sound)، اما هر کدام بر اساس ماهیت خودشان کار متفاوتی انجام می‌دهند.

در وراثت، فرزند می‌تواند رفتار پدر را تغییر دهد که به این کار بازنویسی متد (Method Overriding) می‌گویند.

class Animal:
    def make_sound(self):
        print("صداهای نامشخص حیوان")

class Cat(Animal):
    # بازنویسی متد پدر
    def make_sound(self):
        print("میو میو!")

class Dog(Animal):
    # بازنویسی متد پدر
    def make_sound(self):
        print("هاو هاو!")

# یک تابع که یک شیء حیوان می‌گیرد و صدای آن را در می‌آورد
def animal_concert(animal_object):
    animal_object.make_sound()

# ساخت اشیاء
my_cat = Cat()
my_dog = Dog()

# چندریختی در عمل: یک تابع ثابت با ورودی‌های مختلف، نتایج متفاوتی می‌دهد
animal_concert(my_cat) # خروجی: میو میو!
animal_concert(my_dog) # خروجی: هاو هاو!


چالش‌ها و تمرین‌ها

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

چالش سیستم مدیریت حساب بانکی امن

سؤال: کلاسی به نام BankAccount بسازید که نام صاحب حساب و موجودی اولیه را بگیرد. متدهایی برای «واریز وجه»، «برداشت وجه» و «نمایش موجودی» طراحی کنید. در متد برداشت وجه دقت کنید که اگر موجودی کافی نبود، اجازه برداشت ندهد و پیام خطا چاپ کند.

شیوه حل کردن و توضیح منطق کد: در سازنده کلاس، نام صاحب حساب و موجودی را ذخیره می‌کنیم. در متد واریز (deposit)، مبلغ را به موجودی اضافه می‌کنیم. در متد برداشت (withdraw)، ابتدا یک شرط با if می‌گذاریم؛ اگر مبلغ درخواستی بزرگتر از موجودی فعلی (self.balance) بود، یک پیام هشدار چاپ می‌کنیم و عملیات را متوقف می‌کنیم؛ در غیر این صورت مبلغ را کسر می‌کنیم.

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
        
    def deposit(self, amount):
        self.balance += amount
        print(f"مبلغ {amount} تومان واریز شد. موجودی جدید: {self.balance}")
        
    def withdraw(self, amount):
        if amount > self.balance:
            print(f"خطا: موجودی کافی نیست! موجودی فعلی شما: {self.balance} تومان")
        else:
            self.balance -= amount
            print(f"مبلغ {amount} تومان برداشت شد. موجودی جدید: {self.balance}")
            
    def display(self):
        print(f"حساب آقای/خانم: {self.owner} | موجودی: {self.balance} تومان")

# تست سیستم بانکی
account = BankAccount("صالح عسکری", 500000)
account.deposit(200000)
account.withdraw(800000) # خطا می‌دهد چون موجودی ۷۰۰ هزار تومان است
account.withdraw(300000) # با موفقیت انجام می‌شود

چالش سیستم حقوق و دستمزد کارمندان با وراثت

سؤال: یک کلاس پایه به نام Employee بسازید که نام و شناسه کارمندی را بگیرد. سپس دو کلاس فرزند به نام‌های FullTimeEmployee (حقوق ثابت ماهانه) و PartTimeEmployee (حقوق ساعتی: بر اساس میزان ساعت کارکرد و نرخ هر ساعت) ایجاد کنید. هر دو کلاس فرزند باید متدی به نام calculate_salary برای محاسبه حقوق داشته باشند (چندریختی).

شیوه حل کردن و توضیح منطق کد: کلاس پدر مشخصات عمومی را ثبت می‌کند. کلاس فرزند اول یک ویژگی به نام monthly_salary می‌گیرد و همان را برمی‌گرداند. کلاس فرزند دوم دو ویژگی اختصاصی به نام‌های hours_worked (ساعات کارکرد) و hourly_rate (نرخ هر ساعت) می‌گیرد. متد محاسبه حقوق در فرزند دوم، این دو عدد را در هم ضرب می‌کند. این یک مثال کامل از ترکیب وراثت و چندریختی است.

class Employee:
    def __init__(self, name, id):
        self.name = name
        self.id = id

class FullTimeEmployee(Employee):
    def __init__(self, name, id, monthly_salary):
        super().__init__(name, id)
        self.monthly_salary = monthly_salary
        
    def calculate_salary(self):
        return self.monthly_salary

class PartTimeEmployee(Employee):
    def __init__(self, name, id, hours_worked, hourly_rate):
        super().__init__(name, id)
        self.hours_worked = hours_worked
        self.hourly_rate = hourly_rate
        
    def calculate_salary(self):
        return self.hours_worked * self.hourly_rate

# ساخت اشیاء و محاسبه حقوق‌ها
emp1 = FullTimeEmployee("محمد مرادی", "101", 15000000)
emp2 = PartTimeEmployee("علی شفیعی", "102", 80, 120000)

print(f"حقوق آقای {emp1.name}: {emp1.calculate_salary()} تومان")
print(f"حقوق آقای {emp2.name}: {emp2.calculate_salary()} تومان")

چالش سیستم نقش‌ها و دسترسی‌ها در وب‌سایت

سؤال: کلاسی به نام User بسازید که نام کاربری و ایمیل افراد را ثبت کند و متدی به نام show_permissions داشته باشد که بنویسد "دسترسی: فقط مطالعه مطالب". سپس کلاس فرزندی به نام Admin بسازید که علاوه بر ارث‌بری، متد دسترسی را بازنویسی کند تا بنویسد "دسترسی: مدیریت کامل سایت و کاربران". همچنین ادمین باید بتواند متد اختصاصی delete_comment برای پاک کردن کامنت‌ها داشته باشد.

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

class User:
    def __init__(self, username, email):
        self.username = username
        self.email = email
        
    def show_permissions(self):
        print(f"کاربر {self.username} -> دسترسی: فقط مطالعه مطالب")

class Admin(User):
    def __init__(self, username, email, admin_level):
        super().__init__(username, email)
        self.admin_level = admin_level # سطح مدیریت
        
    # بازنویسی متد پدر (چندریختی)
    def show_permissions(self):
        print(f"مدیر {self.username} (سطح {self.admin_level}) -> دسترسی: مدیریت کامل سایت")
        
    # متد اختصاصی ادمین
    def delete_comment(self, comment_id):
        print(f"مدیر {self.username} کامنت با شناسه {comment_id} را حذف کرد.")

# بررسی دسترسی‌ها
user_simple = User("saleh_user", "saleh@example.com")
user_admin = Admin("narjes_admin", "narjes@example.com", "ارشد")

user_simple.show_permissions()
user_admin.show_permissions()

# کاربری معمولی این متد را ندارد و اگر صدا بزند برنامه خطا می‌دهد
user_admin.delete_comment(2044)


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

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


جلسه هشتم: ماژول‌ها، کتابخانه‌ها و ابزار pip


مفهوم ماژول، پکیج و کتابخانه

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

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

  • ماژول (Module): یک فایل پایتونی ساده با پسوند .py است که شامل تعدادی تابع یا متغیر آماده است. ماژول مثل یک «ابزار خاص» (مثلاً یک آچار فرانسه یا پیچ‌گوشتی) در جعبه‌ابزار شماست.
  • پکیج (Package): مجموعه‌ای از چند ماژول (فایل) مرتبط به هم است که درون یک پوشه (Folder) قرار گرفته‌اند. پکیج شبیه به یک «کشو یا طبقه خاص» از جعبه‌ابزار بزرگ شماست که ابزارهای مشابه را یکجا جمع کرده است.
  • کتابخانه (Library): یک مجموعه بسیار بزرگ و جامع از پکیج‌ها و ماژول‌های مختلف است که برای حل یک دسته از مسائل بزرگ (مثل کارهای گرافیکی، ریاضی یا بازی‌سازی) جمع‌آوری شده است. کتابخانه مثل خود «میز کار یا جعبه‌ابزار غول‌پیکر» شماست.

دستورات ورود کد و شیوه‌های مختلف Import

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

روش اول: ورود کل ماژول

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

import math

# استفاده از تابع جذر گرفتن از کتابخانه ریاضی
result = math.sqrt(16)
print(result) # خروجی: 4.0

روش دوم: ورود یک ابزار خاص (From ... Import)

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

from math import sqrt, pi

print(sqrt(25)) # خروجی: 5.0
print(pi)       # خروجی: 3.1415

روش سوم: نام‌گذاری مستعار (As)

گاهی نام یک ماژول یا کتابخانه خیلی طولانی است یا می‌خواهید نام آن را در برنامه‌تان خلاصه کنید تا سرعت کدنویسی بالا برود:

import datetime as dt

# دریافت زمان فعلی سیستم
now = dt.datetime.now()
print(now)


ساخت یک ماژول اختصاصی توسط خودمان

بزرگترین مزیت ماژول‌ها این است که خودتان هم می‌توانید آن‌ها را بسازید! بیایید یک ابزار کوچک برای محاسبات هندسی درست کنیم.

یک فایل به نام geometry.py بسازید و کدهای زیر را درون آن قرار دهید (این فایل ماژول ماست):

# محتویات فایل geometry.py
PI = 3.1415

def circle_area(radius):
    return PI * (radius ** 2)

def rectangle_area(width, height):
    return width * height

حالا یک فایل جدید در همان پوشه به نام main.py (برنامه اصلی) بسازید و ماژول خودتان را در آن فراخوانی کنید:

# محتویات فایل main.py
import geometry

area1 = geometry.circle_area(5)
area2 = geometry.rectangle_area(4, 6)

print(f"مساحت دایره: {area1}")
print(f"مساحت مستطیل: {area2}")

به همین سادگی شما کدهای خود را دسته‌بندی و تفکیک کردید!


ابزار دانلود کتابخانه یا pip چیست؟

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

ابزار pip (مخفف Pip Installs Packages) در واقع فروشگاه یا بازارچه بزرگ پایتون است (چیزی شبیه به بازار، گوگل‌پلی یا اپ‌استور روی گوشی شما). شما نیازی ندارید به سایت‌های مختلف سر بزنید و فایلی دانلود کنید؛ فقط کافیست نام کتابخانه مورد نظرتان را بدانید.

نحوه استفاده از pip

برای استفاده از pip نباید کدی داخل محیط پایتون بنویسید! بلکه باید محیط ترمینال (Terminal) یا همان خط فرمان (Command Prompt / CMD) ویندوز را باز کنید و دستورات زیر را تایپ کنید:

  • دستور نصب یک کتابخانه جدید:
pip install library_name

(به جای library_name نام کتابخانه مورد نظر، مثلاً turtle را می‌نویسید).

  • دستور حذف یک کتابخانه:
pip uninstall library_name

  • دیدن لیست تمام کتابخانه‌هایی که روی سیستم شما نصب هستند:
pip list


معرفی کتابخانه‌های استاندارد و جذاب برای شروع کار

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

کتابخانه جادویی و بصری Turtle (لاک‌پشت نقاش)

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

import turtle

# ساخت یک لاک‌پشت نقاش
my_turtle = turtle.Turtle()
my_turtle.shape("turtle") # شکل ظاهری نقاش ما شبیه لاک‌پشت شود
my_turtle.color("red")    # رنگ قلم سرخ شود

# دستور به لاک‌پشت برای کشیدن یک مربع با حلقه for
for i in range(4):
    my_turtle.forward(100) # ۱۰۰ قدم برو جلو
    my_turtle.right(90)    # ۹۰ درجه بچرخ به راست

# باز نگه داشتن پنجره نقاشی پس از اتمام کار
turtle.done()

کتابخانه Random (تولید انتخاب‌های تصابفی)

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

import random

# ۱. تولید یک عدد صحیح تصادفی بین ۱ تا ۶ (شبیه‌سازی تاس)
dice = random.randint(1, 6)
print(f"عدد تاس شما: {dice}")

# ۲. انتخاب یک همکلاسی به صورت تصادفی برای کنفرانس
students = ["علی", "سارا", "رضا", "مریم"]
selected = random.choice(students)
print(f"فرد انتخاب شده: {selected}")

کتابخانه خلاقانه Cowsay (گاو سخنگو - نیاز به نصب با pip)

بیایید از ابزار pip که یاد گرفتیم استفاده کنیم! ابتدا به CMD یا ترمینال کامپیوتر بروید و بنویسید: pip install cowsay. حالا این کتابخانه بامزه به پایتون شما اضافه شده است و می‌توانید کدهای خنده‌دار با آن بنویسید:

import cowsay

# دستور به یک گاو کارتونی برای زدن حرف‌های شما!
cowsay.cow("سلام! خوشحالم که داری پایتون یاد می‌گیری!")


چالش‌ها و تمرین‌های حل‌شده کلاسی

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

چالش کشیدن چندضلعی‌های منظم با لاک‌پشت و توابع

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

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

import turtle

def draw_shape(sides, side_length):
    my_turtle = turtle.Turtle()
    my_turtle.color("blue")
    
    # محاسبه زاویه چرخش
    angle = 360 / sides
    
    for i in range(sides):
        my_turtle.forward(side_length)
        my_turtle.left(angle) # چرخیدن به چپ بر اساس زاویه محاسبه شده

# تست: یک ۶ ضلعی منظم با طول اضلاع ۷۰ قدم بکش
draw_shape(6, 70)

turtle.done()

چالش بازی سنگ، کاغذ، قیچی در مقابل کامپیوتر

سؤال: برنامه‌ای بنویسید که بازی معروف سنگ، کاغذ، قیچی را شبیه‌سازی کند. کاربر انتخاب خود را بنویسد و کامپیوتر هم با استفاده از کتابخانه random یک انتخاب تصادفی داشته باشد، سپس برنده را اعلام کند.

شیوه حل کردن و توضیح منطق کد: گزینه‌ها را در یک لیست قرار می‌دهیم. انتخاب کامپیوتر را با random.choice() تعیین می‌کنیم. ورودی کاربر را با ساختارهای if و elif در برابر انتخاب کامپیوتر مقایسه می‌کنیم تا برنده، بازنده یا حالت مساوی مشخص شود.

import random

def play_game():
    options = ["سنگ", "کاغذ", "قیچی"]
    
    print("--- به بازی سنگ، کاغذ، قیچی خوش آمدید ---")
    user_choice = input("انتخاب شما (سنگ، کاغذ، قیچی): ").strip()
    
    if user_choice not in options:
        print("انتخاب نامعتبری داشتید!")
        return
        
    # انتخاب تصادفی کامپیوتر
    computer_choice = random.choice(options)
    print(f"انتخاب کامپیوتر: {computer_choice}")
    
    # بررسی منطق برنده شدن
    if user_choice == computer_choice:
        print("نتیجه بازی: مساوی شد!")
    elif (user_choice == "سنگ" and computer_choice == "قیچی") or \
         (user_choice == "کاغذ" and computer_choice == "سنگ") or \
         (user_choice == "قیچی" and computer_choice == "کاغذ"):
        print("تبریک! شما برنده شدید!")
    else:
        print("کامپیوتر برنده شد! دوباره تلاش کنید.")

# اجرای بازی
play_game()

چالش کشیدن یک دایره رنگارنگ جادویی

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

شیوه حل کردن و توضیح منطق کد: ما با استفاده از متد my_turtle.circle(80) می‌توانیم یک دایره به شعاع ۸۰ بکشیم. اگر این کار را داخل یک حلقه بگذاریم و بعد از کشیدن هر دایره، سرِ لاک‌پشت را مثلاً ۱۰ درجه بچرخانیم، دایره بعدی در زاویه جدیدی کشیده می‌شود و یک الگوی گل‌مانند بی‌نظیر ایجاد می‌شود.

import turtle

def draw_magic_circles():
    window = turtle.Screen()
    window.bgcolor("black") # رنگ پس‌زمینه را مشکی می‌کنیم تا نقاشی جذاب‌تر شود
    
    artist = turtle.Turtle()
    artist.speed(0) # سرعت حرکت لاک‌پشت را روی سریع‌ترین حالت ممکن می‌گذاریم
    artist.color("cyan")
    
    # کشیدن ۳۶ دایره در جهت‌های مختلف
    for i in range(36):
        artist.circle(80)   # کشیدن دایره
        artist.left(10)     # ۱۰ درجه تغییر مسیر برای دایره بعدی
        
    turtle.done()

# اجرای چالش هنری
draw_magic_circles()


جلسه نهم: آشنایی با پایتون در دنیای داده‌ها (NumPy ،Pandas و Matplotlib)

مفهوم تحلیل داده و چرا به ابزارهای جدید نیاز داریم؟

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

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

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

یادتان هست که در جلسه گذشته با ابزار pip آشنا شدیم؟ برای استفاده از این سه کتابخانه، ابتدا باید ترمینال یا CMD کامپیوتر خود را باز کنید و این دستور ساده را بنویسید تا هر سه یک‌جا نصب شوند:

pip install numpy pandas matplotlib


کتابخانه NumPy (محاسبات عددی سریع)

کتابخانه NumPy (مخفف Numerical Python) ابزاری است که به ما اجازه می‌دهد لیست‌هایی از اعداد بسازیم که با سرعت باورنکردنی (تا ۱۰۰ برابر سریع‌تر از لیست‌های معمولی پایتون) محاسبات ریاضی را انجام می‌دهند. به این لیست‌های پیشرفته، آرایه (Array) می‌گویند.

به صورت استاندارد، برنامه‌نویسان این کتابخانه را با نام مستعار np وارد برنامه می‌کنند.

ساخت یک آرایه عددی و محاسبات روی آن

در لیست‌های معمولی پایتون، اگر دو لیست را با علامت + جمع کنید، آن‌ها را به هم می‌چسباند. اما در NumPy، عملیات به صورت ریاضی و خانه به خانه انجام می‌شود:

import numpy as np

# ساخت دو آرایه عددی ساده
scores_term1 = np.array([15, 18, 12, 20])
scores_term2 = np.array([17, 16, 15, 19])

# جمع زدن نمرات دو ترم به صورت خانه به خانه و ریاضی
total_scores = scores_term1 + scores_term2
print("Total scores:", total_scores)  # خروجی: [32 34 27 39]

# ضرب کردن تمام نمرات در عدد ۲ به صورت همزمان
doubled_scores = scores_term1 * 2
print("Doubled scores:", doubled_scores)  # خروجی: [30 36 24 40]

ابزارهای ریاضی آماده در NumPy

نیازی نیست برای گرفتن میانگین یا پیدا کردن بالاترین نمره، حلقه بنویسید؛ NumPy متدهای آماده‌ای برای این کار دارد:

import numpy as np

prices = np.array([5000, 12000, 8500, 23000, 11000])

print("Average price:", np.mean(prices))  # محاسبه خودکار میانگین قیمت‌ها
print("Maximum price:", np.max(prices))   # پیدا کردن بیشترین قیمت
print("Minimum price:", np.min(prices))   # پیدا کردن کمترین قیمت


کتابخانه Pandas (مدیریت داده‌ها به شکل جدول)

اگر NumPy برای کار با خطی از اعداد است، کتابخانه Pandas برای کار با جدول‌ها است! پنداس به شما اجازه می‌دهد فایلی مثل اکسل را وارد پایتون کنید، سطرها و ستون‌های آن را نام‌گذاری کنید و دقیقاً مثل یک جدول واقعی با آن رفتار کنید.

در پنداس به این جدول‌ها، دیتافریم (DataFrame) می‌گویند. پنداس را معمولاً با نام مستعار pd وارد می‌کنند.

ساخت یک جدول (DataFrame) ساده در پایتون

بیایید با استفاده از یک دیکشنری، یک جدول از مشخصات چند کالا و موجودی انبار بسازیم:

import pandas as pd

# داده‌های اولیه ما که در یک دیکشنری ساختاردهی شده‌اند
data = {
    "product_name": ["Laptop", "Mouse", "Keyboard", "Monitor"],
    "price": [35000000, 450000, 1200000, 8500000],
    "stock": [5, 20, 15, 8]
}

# تبدیل کردن دیکشنری به یک جدول پنداس (دیتافریم)
df = pd.DataFrame(data)

# نمایش کل جدول در خروجی برنامه
print(df)

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

  product_name     price  stock
0       Laptop  35000000      5
1        Mouse    450000     20
2     Keyboard   1200000     15
3      Monitor   8500000      8

فیلتر کردن و استخراج اطلاعات از جدول

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

import pandas as pd

# نمایش اختصاصی ستون مربوط به نام کالاها
print(df["product_name"])

# فیلتر کردن محصولاتی که تعداد موجودی آن‌ها در انبار بیشتر از ۱۰ است
high_stock_items = df[df["stock"] > 10]
print(high_stock_items)


کتابخانه Matplotlib (رسم نمودار و تصویرسازی)

دیدن اعداد خشک و خالی در جدول همیشه جذاب نیست. انسان‌ها با دیدن نمودارها، وضعیت داده‌ها را خیلی بهتر درک می‌کنند. کتابخانه Matplotlib ابزار نقاشی و رسم نمودار در پایتون است. ما معمولاً از بخش pyplot آن با نام مستعار plt استفاده می‌کنیم.

رسم یک نمودار خطی ساده

import matplotlib.pyplot as plt

# داده‌های مربوط به روزهای هفته و میزان فروش روزانه
days = ["Sat", "Sun", "Mon", "Tue", "Wed"]
sales = [12, 19, 15, 25, 22]

# رسم یک نمودار خطی ساده
plt.plot(days, sales, marker='o', color='green')

# اضافه کردن عنوان اصلی و برچسب محورهای افقی و عمودی به نمودار
plt.title("Daily Sales Performance")
plt.xlabel("Days of the Week")
plt.ylabel("Number of Sales")

# نمایش دادن نهایی نمودار روی صفحه کامپیوتر
plt.show()


ورود یک دیتاست واقعی، فرم‌دهی و تحلیل داده‌ها

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

گام اول: خواندن فایل CSV

با استفاده از دستور pd.read_csv می‌توانیم فایل را مستقیماً به یک جدول تبدیل کنیم:

import pandas as pd
import matplotlib.pyplot as plt

# بارگذاری و خواندن فایل اکسل یا همان سی‌اس‌وی در پایتون
df = pd.read_csv("students_report.csv")

# نمایش فقط ۲ سطر اول به عنوان نمونه برای فهم ساختار فایل
print(df.head(2))

گام دوم: فرم‌دهی و تحلیل داده (ستون جدید و فیلتر کردن)

فرض کنید می‌خواهیم به نمره همه دانش‌آموزان ۲ نمره به عنوان ارفاق اضافه کنیم و کسانی که نمره نهایی آن‌ها بالای ۱۵ می‌شود را پیدا کنیم:

import pandas as pd

# اضافه کردن یک ستون جدید محاسباتی به نام نمره نهایی
df["final_score"] = df["actual_score"] + 2

# فیلتر کردن و پیدا کردن دانش‌آموزان برتر کلاس
top_students = df[df["final_score"] >= 15]
print("--- Top Students ---")
print(top_students[["name", "final_score"]])

گام سوم: رسم نمودار ستونی از نمرات دانش‌آموزان

import matplotlib.pyplot as plt

# رسم یک نمودار ستونی برای مقایسه بهتر نمرات نهایی کل کلاس
plt.bar(df["name"], df["final_score"], color="skyblue")

# زیباسازی و نام‌گذاری بخش‌های مختلف نمودار
plt.title("Class Final Scores Report")
plt.xlabel("Student Name")
plt.ylabel("Scores")

# نمایش نهایی پنجره نمودار
plt.show()


چالش‌ها و تمرین‌ها

برای تثبیت این مفاهیم، سه چالش پرکاربرد دنیای تحلیل داده را با هم حل می‌کنیم.

چالش محاسبه خودکار سود فروشگاه با NumPy

سؤال: آرایه‌ای از «قیمت خرید» ۵ کالا و آرایه دیگری از «قیمت فروش» همان ۵ کالا داده شده است. با استفاده از آرایه‌های NumPy، آرایه جدیدی بسازید که میزان سود هر کالا را نشان دهد. سپس مشخص کنید بیشترین سود چقدر بوده است؟

شیوه حل کردن و توضیح منطق کد: ابتدا لیست‌ها را به آرایه NumPy تبدیل می‌کنیم. از آنجا که آرایه‌ها محاسبات خانه به خانه را متوجه می‌شوند، با کم کردن آرایه خرید از آرایه فروش، آرایه سودها به دست می‌آید. در نهایت با متد np.max() بالاترین میزان سود را پیدا می‌کنیم.

import numpy as np

# وارد کردن لیست قیمت‌های خرید و فروش بر حسب هزار تومان
purchase = np.array([100, 250, 80, 400, 150])
sale = np.array([130, 290, 110, 450, 180])

# محاسبه سود هر کالا به صورت همزمان و خانه به خانه
profits = sale - purchase
print("Profits per item:", profits)  # خروجی: [30 40 30 50 30]

# پیدا کردن بالاترین میزان سود کسب شده
max_profit = np.max(profits)
print("Maximum profit earned:", max_profit)

چالش مدیریت اطلاعات کارمندان و فیلتر کردن حقوق با Pandas

سؤال: جدول اطلاعات کارمندان زیر را در پنداس بسازید. برنامه‌ای بنویسید که کارمندانی که در بخش "Sales" کار می‌کنند و حقوق آن‌ها بالای ۱۲ میلیون است را پیدا کرده و نام آن‌ها را چاپ کند.

name  | department | salary
Ali   | Sales      | 14000000
Sara  | Finance    | 16000000
Reza  | Sales      | 10000000
Mary  | Sales      | 15000000

شیوه حل کردن و توضیح منطق کد: ابتدا جدول را با pd.DataFrame تشکیل می‌دهیم. برای فیلتر کردن همزمان دو شرط، هر شرط را داخل پرانتز گذاشته و از علامت & (به معنی "و" منطقی در پنداس) استفاده می‌کنیم.

import pandas as pd

# ساخت یک دیکشنری اولیه برای اطلاعات کارمندان
employees_data = {
    "name": ["Ali", "Sara", "Reza", "Mary"],
    "department": ["Sales", "Finance", "Sales", "Sales"],
    "salary": [14000000, 16000000, 10000000, 15000000]
}

# تبدیل دیکشنری به دیتافریم پنداس
df = pd.DataFrame(employees_data)

# اعمال دو شرط همزمان: ۱. بخش حتما فروش باشد ۲. حقوق بزرگتر از ۱۲ میلیون باشد
filtered_df = df[(df["department"] == "Sales") & (df["salary"] > 12000000)]

# چاپ کردن خروجی نهایی نام افراد واجد شرایط
print("Qualified Employees:")
print(filtered_df["name"])

چالش تحلیل هوای پاک و تصویرسازی با نمودار

سؤال: فرض کنید جدولی از میانگین شاخص آلودگی هوای یک شهر در ۵ ماه اول سال دارید. می‌خواهید ماهی که بدترین آلودگی را داشته را با پنداس پیدا کنید و یک نمودار خطی از روند تغییرات آلودگی هوا رسم کنید.

month | aqi_index
Month1 | 65
Month2 | 80
Month3 | 110
Month4 | 145
Month5 | 130

¼شیوه حل کردن و توضیح منطق کد: ما ابتدا جدول را ساخته و با استفاده از متد max() بالاترین شاخص را پیدا می‌کنیم (شاخص AQI هرچه بالاتر باشد هوا آلوده‌تر است). سپس با کتابخانه Matplotlib یک نمودار خطی رسم می‌کنیم و با پارامترها شکل ظاهری آن را به صورت خط‌چین قرمز رنگ در می‌آوریم تا حالت هشدار را القا کند.

import pandas as pd
import matplotlib.pyplot as plt

# تعریف ساختار اطلاعات مربوط به ماه‌ها و شاخص کیفیت هوا
weather_data = {
    "month": ["Month1", "Month2", "Month3", "Month4", "Month5"],
    "aqi_index": [65, 80, 110, 145, 130]
}

# تبدیل داده‌ها به جدول
df = pd.DataFrame(weather_data)

# پیدا کردن بالاترین عدد شاخص آلودگی در کل ستون
worst_aqi = df["aqi_index"].max()

# پیدا کردن نام ماهی که این شاخص بالاترین متعلق به آن است
worst_month = df[df["aqi_index"] == worst_aqi]["month"].values[0]
print(f"Worst month: {worst_month} with AQI: {worst_aqi}")

# رسم نمودار تغییرات آلودگی به صورت خط‌چین قرمز با علامت ضربدر برای نقاط کلیدی
plt.plot(df["month"], df["aqi_index"], marker='X', linestyle='--', color='red')

# تنظیم ویژگی‌های ظاهری نمودار برای خوانایی بهتر
plt.title("Air Quality Index Trend")
plt.xlabel("Months")
plt.ylabel("AQI Score")
plt.grid(True)  # فعال کردن خطوط شطرنجی پس‌زمینه

# نمایش پنجره نمودار گرافیکی به کاربر
plt.show()


سلام! کاملاً حق با شماست. چطور یادم رفت؟ شی‌گرایی (OOP) و مفهوم «کلاس و شیء» یکی از ستون‌های اصلی پایتون است و قطعاً یک پروژه نهایی بدون وجود آن ناقص می‌ماند.

اضافه کردن شی‌گرایی به این سناریو اتفاقاً کار را بسیار قشنگ‌تر و منطقی‌تر می‌کند؛ چون هر «عادت» (Habit) به خودی خود یک شیء (Object) در دنیای واقعی است که ویژگی‌ها (نام، هدف، میزان پیشرفت) و رفتارهای (افزایش پیشرفت) خودش را دارد.

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


جلسه دهم: پروژه پایانی دوره پایتون: توسعه داشبورد هوشمند برای مدیریت عادت‌ها (Smart Habit Tracker)

شرح پروژه

در این پروژه نهایی، شما قرار است یک سیستم کاملاً واقعی و کاربردی برای زندگی روزمره طراحی کنید. هدف این پروژه، پیاده‌سازی یک ردیاب عادت‌ها و مدیریت کارهای روزانه (Habit Tracker) بر پایه معماری شی‌گرایی (OOP) تحت ترمینال است. این برنامه به یک فایل ذخیره‌سازی دائمی متصل بوده و در نهایت، داده‌های شما را به صورت یک نمودار گرافیکی و شیک آنالیز می‌کند.


اهداف آموزشی و مباحث مورد سنجش در این پروژه

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

  • شی‌گرایی (OOP): ساخت یک کلاس واحد برای مدل‌سازی هر عادت به همراه ویژگی‌ها و متدهای داخلی آن.
  • ساختارهای تکرار و شرطی: مدیریت منوی اصلی برنامه به صورت زنده و مداوم (while True و if-elif).
  • ساختار داده‌ها و لیست‌ها: مدیریت و نگهداری اشیاء ساخته شده درون یک لیست اصلی.
  • مدیریت فایل (File I/O): ذخیره دائمی اطلاعات در یک فایل CSV تا با بستن برنامه داده‌ها پاک نشوند.
  • مدیریت خطا (Exception Handling): مهار کردن ورودی‌های اشتباه کاربر (try-except) جهت جلوگیری از کرش کردن برنامه.
  • تحلیل داده و تصویرسازی (Pandas & Matplotlib): بارگذاری جدول مخازن و رسم نمودار ستونی مقایسه‌ای.

ساختار داده‌ها و نحوه ذخیره‌سازی (Data Architecture)

برنامه شما باید اطلاعات را درون یک فایل متنی به نام habits.csv ذخیره کند. هر عادت دارای ۳ ویژگی اصلی است که در کلاس تعریف می‌شوند:

  1. habit_name: نام عادت (مثلاً Reading یا Gym).
  2. target: هدف شما (چند بار باید در ماه این کار انجام شود، مثلاً 20 بار).
  3. done: میزان پیشرفت فعلی (تا این لحظه چند بار انجام شده است، مثلاً 12 بار).

شکل ذخیره‌سازی داده‌ها در فایل باید دقیقاً به صورت زیر (با ویرگول یا کاما جدا شده) باشد:

habit_name,target,done
Reading,20,12
Gym,15,8
Water,30,25


راهنمای گام‌به‌گام برای پیاده‌سازی پروژه (بخش‌های اصلی کد)

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

گام اول: طراحی کلاس عادت (The Habit Class)

  • یک کلاس به نام Habit بسازید.
  • متد سازنده این کلاس (__init__) باید نام عادت، هدف و میزان انجام شده (که پیش‌فرض صفر است) را دریافت و مقداردهی کند.
  • یک متد (رفتار) درون کلاس به نام add_progress بنویسید که هر زمان صدا زده شد، یک واحد به مقدار انجام شده (done) اضافه کند.

گام دوم: مدیریت و راه‌اندازی فایل (File Management)

  • توابعی بنویسید که در ابتدای برنامه، فایل habits.csv را بخواند، برای هر سطر آن یک شیء (Object) از روی کلاس Habit بسازد و تمام این اشیاء را درون یک لیست اصلی به نام habits_list ذخیره کند.
  • تابعی بنویسید که در صورت ایجاد تغییرات، لیست اشیاء را دوباره به فرمت کاما صوتی تبدیل کرده و در فایل CSV ذخیره کند.

گام سوم: منوی تعاملی و مدیریت عادت‌ها (Core Features)

  • یک حلقه اصلی بسازید که تا زمان انتخاب گزینه خروج توسط کاربر، گزینه‌های زیر را چاپ کند:
  1. نمایش وضعیت فعلی عادت‌ها (با پیمایش روی لیست اشیاء و چاپ ویژگی‌های هر شیء)
  2. اضافه کردن عادت جدید (ساخت یک شیء جدید از کلاس و آپدیدت فایل)
  3. ثبت پیشرفت روزانه (صدا زدن متد add_progress روی شیء انتخاب شده)
  4. مشاهده داشبورد گرافیکی
  5. خروج از برنامه
  • در بخش ورود اطلاعات هدف، حتماً از try-except استفاده کنید تا اگر کاربر متن وارد کرد، برنامه خطا ندهد.

گام چهارم: نمودار و داشبورد (Data Visualisation)

  • تابعی بنویسید که با استفاده از دستور pd.read_csv فایل دیتا را وارد پنداس کند.
  • با استفاده از Matplotlib یک نمودار ستونی (plt.bar) رسم کنید که در آن، سقف هدف هر عادت به همراه میزان پیشرفت واقعی کاربر با دو رنگ متفاوت در کنار هم مقایسه شوند.

نمونه کدهای راهنما (برای شروع کار)

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

import os
import pandas as pd
import matplotlib.pyplot as plt

FILE_NAME = "habits.csv"

# گام اول: طراحی قالب و کلاس شی‌گرا برای هر عادت
class Habit:
    def __init__(self, habit_name, target, done=0):
        self.habit_name = habit_name
        self.target = int(target)
        self.done = int(done)
        
    def add_progress(self):
        # متدی برای افزایش پیشرفت به میزان یک واحد
        self.done += 1

def load_habits():
    # خواندن فایل و تبدیل هر سطر به یک شیء از کلاس Habit و بازگرداندن یک لیست
    habits_list = []
    return habits_list

def save_habits(habits_list):
    # گرفتن لیست اشیاء و ذخیره ویژگی‌های آن‌ها درون فایل CSV
    pass

def main():
    # بارگذاری لیست اشیاء عادت در ابتدای برنامه
    my_habits = load_habits()
    
    while True:
        print("\n=== OOP SMART HABIT TRACKER ===")
        print("1. Show Status | 2. Add Habit | 3. Log Progress | 4. Dashboard | 5. Exit")
        choice = input("Select an option: ")
        
        if choice == "5":
            break

if __name__ == "__main__":
    main()

saleh askari
saleh askari

خیلی ممنونم بابت مطالعه این وبلاگ

EN