ساخت برنامه مدیریت کتابخانه با پایتون؛ پروژه کامل با JSON و سیستم امانت

🗓 آخرین بهروزرسانی:
ساخت برنامه مدیریت کتابخانه با پایتون یکی از پروژههای بسیار مناسب برای عبور از تمرینهای ساده و رسیدن به یک برنامه کاربردی چندبخشی است. در این پروژه باید اطلاعات کتابها را نگه داریم، کتاب جدید ثبت کنیم، وضعیت موجود یا امانتدادهشده را کنترل کنیم، نام امانتگیرنده و تاریخ امانت را ذخیره کنیم، کتاب را جستوجو یا حذف کنیم و در پایان همه دادهها را داخل فایل JSON نگه داریم تا بعد از بستهشدن برنامه از بین نروند.
این مقاله چهاردهمین پروژه فاز اول دسته تمرین و پروژههای پایتون است. اگر مقالههای قبلی مثل مدیریت نمرات دانشآموزان با پایتون یا مدیریت هزینههای شخصی با پایتون را انجام داده باشید، با ایده ذخیره دادههای ساختاریافته و ساخت منوی تعاملی آشنا هستید. در این پروژه یک مرحله جلوتر میرویم و مفهوم وضعیت یا State را هم به دادههای هر کتاب اضافه میکنیم.
در این پروژه چه چیزی میسازیم؟
- ثبت کتاب جدید با عنوان، نویسنده و ISBN
- تولید شناسه یکتا برای هر کتاب
- نمایش همه کتابها
- نمایش فقط کتابهای موجود
- نمایش کتابهای امانتدادهشده
- جستوجو بر اساس عنوان، نویسنده یا ISBN
- ثبت امانت کتاب
- ذخیره نام امانتگیرنده
- ثبت خودکار تاریخ امانت
- ثبت بازگشت کتاب
- جلوگیری از امانتدادن دوباره یک کتاب
- جلوگیری از بازگرداندن کتابی که امانت نیست
- حذف کتاب از سیستم
- گزارش تعداد کل، موجود و امانتدادهشده
- ذخیره دائمی اطلاعات در JSON
- مدیریت فایل JSON خراب یا نامعتبر
چرا پروژه مدیریت کتابخانه برای یادگیری پایتون مفید است؟
این پروژه فقط CRUD ساده نیست. در هر رکورد کتاب چند نوع داده داریم: متن برای عنوان و نویسنده، عدد برای شناسه، Boolean برای وضعیت موجود بودن و تاریخ برای زمان امانت. علاوه بر این، عملیات مختلف باید از قوانین مشخصی پیروی کنند. مثلاً کتابی که قبلاً امانت داده شده نباید دوباره امانت داده شود و کتاب موجود نباید عملیات بازگشت را بپذیرد.
این مدل دقیقاً همان چیزی است که در برنامههای واقعی میبینیم: داده + قانون کسبوکار. یادگیری نحوه جدا کردن این دو بخش، برای پروژههای بزرگتر مثل فروشگاه، مدیریت سفارش، سامانه آموزش و سیستم رزرو بسیار مهم است.
ساختار داده هر کتاب
هر کتاب را به شکل یک Dictionary نگه میداریم:
{
"id": 1,
"title": "Clean Code",
"author": "Robert C. Martin",
"isbn": "9780132350884",
"available": true,
"borrower": "",
"borrowed_at": ""
}زمانی که کتاب امانت داده میشود، مقدار available برابر false میشود و نام امانتگیرنده و تاریخ امانت نیز ثبت خواهند شد.
{
"id": 1,
"title": "Clean Code",
"author": "Robert C. Martin",
"isbn": "9780132350884",
"available": false,
"borrower": "علی رضایی",
"borrowed_at": "2026-08-11"
}مرحله اول: ساخت فایل پروژه
یک فایل پایتون با نام زیر بسازید:
library_manager.pyفایل اطلاعات کتابخانه نیز بهصورت خودکار با نام زیر ایجاد خواهد شد:
library.jsonمرحله دوم: وارد کردن ماژولها
import json
from pathlib import Path
from datetime import dateماژول json برای ذخیره و بارگذاری اطلاعات، Path برای کار تمیزتر با مسیر فایل و date برای ثبت تاریخ امانت استفاده میشود. برای یادگیری عمیقتر این بخشها میتوانید آموزش کار با فایلهای JSON در پایتون و تاریخ و زمان در پایتون با datetime را مطالعه کنید.
مرحله سوم: تعریف فایل داده
DATA_FILE = Path("library.json")قرار دادن مسیر فایل در یک ثابت باعث میشود اگر بعداً بخواهیم نام فایل یا محل ذخیرهسازی را تغییر دهیم، فقط همین خط را ویرایش کنیم.
مرحله چهارم: بارگذاری اطلاعات کتابخانه
def load_books():
if not DATA_FILE.exists():
return []
try:
with DATA_FILE.open(
"r",
encoding="utf-8",
) as file:
data = json.load(file)
except (
json.JSONDecodeError,
OSError,
):
print(
"فایل کتابخانه قابل خواندن نیست. "
"برنامه با لیست خالی شروع شد."
)
return []
return data if isinstance(data, list) else []اگر فایل هنوز ساخته نشده باشد، یک لیست خالی برمیگردانیم. اگر فایل وجود داشته باشد اما JSON آن خراب یا ناقص باشد، برنامه بهجای Crash شدن، پیام خطا نمایش میدهد و با داده خالی ادامه میدهد.
مرحله پنجم: ذخیره اطلاعات
def save_books(books):
with DATA_FILE.open(
"w",
encoding="utf-8",
) as file:
json.dump(
books,
file,
ensure_ascii=False,
indent=2,
)گزینه ensure_ascii=False باعث میشود عنوانها و نامهای فارسی داخل فایل JSON بهصورت خوانا ذخیره شوند.
مرحله ششم: تولید شناسه یکتا
برای هر کتاب یک ID لازم داریم تا عملیات امانت، بازگشت و حذف دقیقاً روی همان کتاب انجام شود.
def next_book_id(books):
return max(
(
book.get("id", 0)
for book in books
),
default=0,
) + 1استفاده از len(books) + 1 روش مناسبی نیست، چون اگر یک کتاب حذف شود احتمال تکرار ID وجود دارد. در اینجا بزرگترین شناسه فعلی را پیدا میکنیم و یک واحد به آن اضافه میکنیم.
مرحله هفتم: ثبت کتاب جدید
def add_book(books):
title = input(
"عنوان کتاب: "
).strip()
if not title:
print(
"عنوان کتاب نمیتواند خالی باشد."
)
return
author = input(
"نام نویسنده: "
).strip()
if not author:
print(
"نام نویسنده نمیتواند خالی باشد."
)
return
isbn = input(
"ISBN (اختیاری): "
).strip()
book = {
"id": next_book_id(books),
"title": title,
"author": author,
"isbn": isbn,
"available": True,
"borrower": "",
"borrowed_at": "",
}
books.append(book)
save_books(books)
print(
f"کتاب با شناسه {book['id']} ثبت شد."
)کتاب تازهثبتشده بهصورت پیشفرض موجود است؛ بنابراین available برابر True قرار میگیرد.
مرحله هشتم: نمایش وضعیت یک کتاب
def book_status(book):
if book.get("available", True):
return "موجود"
return (
"امانت | "
f"{book.get('borrower', '-')} | "
f"{book.get('borrowed_at', '-')}"
)این تابع فقط مسئول تولید متن وضعیت است. جدا کردن این منطق باعث میشود بتوانیم همان وضعیت را در فهرست کامل، نتایج جستوجو و گزارشها دوباره استفاده کنیم.
مرحله نهم: نمایش همه کتابها
def show_books(books):
if not books:
print(
"هنوز کتابی ثبت نشده است."
)
return
print("\n=== فهرست کتابها ===")
for book in books:
print(
f"#{book.get('id')} | "
f"{book.get('title')} | "
f"{book.get('author')} | "
f"{book_status(book)}"
)مرحله دهم: پیدا کردن کتاب با ID
def find_book_by_id(
books,
book_id,
):
for book in books:
if book.get("id") == book_id:
return book
return Noneاین تابع در عملیات امانت، بازگشت و حذف استفاده خواهد شد و جلوی تکرار کد را میگیرد.
مرحله یازدهم: جستوجوی کتاب
جستوجو را روی سه فیلد عنوان، نویسنده و ISBN انجام میدهیم. از casefold() استفاده میکنیم تا جستوجوی متن انگلیسی نسبت به حروف بزرگ و کوچک حساس نباشد.
def search_books(books):
query = input(
"عنوان، نویسنده یا ISBN: "
).strip().casefold()
if not query:
print(
"عبارت جستوجو خالی است."
)
return
results = [
book
for book in books
if (
query
in book.get(
"title",
"",
).casefold()
or query
in book.get(
"author",
"",
).casefold()
or query
in book.get(
"isbn",
"",
).casefold()
)
]
if not results:
print("کتابی پیدا نشد.")
return
show_books(results)مرحله دوازدهم: امانت دادن کتاب
در این عملیات سه شرط مهم داریم: ID باید عدد باشد، کتاب باید وجود داشته باشد و وضعیت آن باید موجود باشد.
def borrow_book(books):
value = input(
"شناسه کتاب: "
).strip()
try:
book_id = int(value)
except ValueError:
print(
"شناسه باید عدد صحیح باشد."
)
return
book = find_book_by_id(
books,
book_id,
)
if book is None:
print("کتاب پیدا نشد.")
return
if not book.get(
"available",
True,
):
print(
"این کتاب قبلاً امانت داده شده است."
)
return
borrower = input(
"نام امانتگیرنده: "
).strip()
if not borrower:
print(
"نام امانتگیرنده نمیتواند خالی باشد."
)
return
book["available"] = False
book["borrower"] = borrower
book["borrowed_at"] = (
date.today().isoformat()
)
save_books(books)
print(
"امانت کتاب با موفقیت ثبت شد."
)مرحله سیزدهم: بازگشت کتاب
def return_book(books):
value = input(
"شناسه کتاب: "
).strip()
try:
book_id = int(value)
except ValueError:
print(
"شناسه باید عدد صحیح باشد."
)
return
book = find_book_by_id(
books,
book_id,
)
if book is None:
print("کتاب پیدا نشد.")
return
if book.get(
"available",
True,
):
print(
"این کتاب در حال حاضر موجود است."
)
return
book["available"] = True
book["borrower"] = ""
book["borrowed_at"] = ""
save_books(books)
print(
"بازگشت کتاب ثبت شد."
)بعد از بازگشت، اطلاعات امانتگیرنده و تاریخ قبلی پاک میشوند و کتاب دوباره در وضعیت موجود قرار میگیرد.
مرحله چهاردهم: نمایش کتابهای موجود و امانتشده
def show_available_books(books):
available = [
book
for book in books
if book.get("available", True)
]
if not available:
print(
"در حال حاضر کتاب موجودی نداریم."
)
return
show_books(available)
def show_borrowed_books(books):
borrowed = [
book
for book in books
if not book.get(
"available",
True,
)
]
if not borrowed:
print(
"هیچ کتابی در امانت نیست."
)
return
show_books(borrowed)مرحله پانزدهم: حذف کتاب
بهتر است کتابی که در امانت است مستقیماً حذف نشود؛ چون با حذف آن اطلاعات امانت هم از بین میرود.
def delete_book(books):
value = input(
"شناسه کتاب برای حذف: "
).strip()
try:
book_id = int(value)
except ValueError:
print(
"شناسه باید عدد صحیح باشد."
)
return
book = find_book_by_id(
books,
book_id,
)
if book is None:
print("کتاب پیدا نشد.")
return
if not book.get(
"available",
True,
):
print(
"کتاب امانتدادهشده را "
"ابتدا برگردانید."
)
return
confirm = input(
f"حذف {book['title']}؟ (بله/خیر): "
).strip().lower()
if confirm not in {
"بله",
"yes",
"y",
}:
print("حذف لغو شد.")
return
books.remove(book)
save_books(books)
print("کتاب حذف شد.")مرحله شانزدهم: گزارش کتابخانه
def library_report(books):
total = len(books)
available = sum(
1
for book in books
if book.get("available", True)
)
borrowed = total - available
print("\n=== گزارش کتابخانه ===")
print(f"کل کتابها: {total}")
print(f"موجود: {available}")
print(f"در امانت: {borrowed}")این گزارش ساده است، اما مفهوم Aggregation را تمرین میدهد؛ یعنی بهجای مشاهده رکوردها بهصورت جداگانه، وضعیت کلی دادهها را محاسبه میکنیم.
مرحله هفدهم: ساخت منوی برنامه
def show_menu():
print("\n=== مدیریت کتابخانه ===")
print("1) ثبت کتاب")
print("2) نمایش همه کتابها")
print("3) جستوجوی کتاب")
print("4) امانت کتاب")
print("5) بازگشت کتاب")
print("6) کتابهای موجود")
print("7) کتابهای امانتشده")
print("8) گزارش کتابخانه")
print("9) حذف کتاب")
print("0) خروج")کد کامل برنامه مدیریت کتابخانه با پایتون
import json
from pathlib import Path
from datetime import date
DATA_FILE = Path("library.json")
def load_books():
if not DATA_FILE.exists():
return []
try:
with DATA_FILE.open(
"r",
encoding="utf-8",
) as file:
data = json.load(file)
except (
json.JSONDecodeError,
OSError,
):
print(
"فایل کتابخانه قابل خواندن نیست."
)
return []
return data if isinstance(data, list) else []
def save_books(books):
with DATA_FILE.open(
"w",
encoding="utf-8",
) as file:
json.dump(
books,
file,
ensure_ascii=False,
indent=2,
)
def next_book_id(books):
return max(
(
book.get("id", 0)
for book in books
),
default=0,
) + 1
def book_status(book):
if book.get("available", True):
return "موجود"
return (
"امانت | "
f"{book.get('borrower', '-')} | "
f"{book.get('borrowed_at', '-')}"
)
def show_books(books):
if not books:
print("کتابی برای نمایش وجود ندارد.")
return
print("\n=== فهرست کتابها ===")
for book in books:
print(
f"#{book.get('id')} | "
f"{book.get('title')} | "
f"{book.get('author')} | "
f"{book_status(book)}"
)
def find_book_by_id(
books,
book_id,
):
for book in books:
if book.get("id") == book_id:
return book
return None
def add_book(books):
title = input("عنوان کتاب: ").strip()
if not title:
print("عنوان نمیتواند خالی باشد.")
return
author = input("نام نویسنده: ").strip()
if not author:
print("نام نویسنده نمیتواند خالی باشد.")
return
isbn = input("ISBN (اختیاری): ").strip()
book = {
"id": next_book_id(books),
"title": title,
"author": author,
"isbn": isbn,
"available": True,
"borrower": "",
"borrowed_at": "",
}
books.append(book)
save_books(books)
print(
f"کتاب با شناسه {book['id']} ثبت شد."
)
def search_books(books):
query = input(
"عنوان، نویسنده یا ISBN: "
).strip().casefold()
if not query:
print("عبارت جستوجو خالی است.")
return
results = [
book
for book in books
if (
query
in book.get(
"title",
"",
).casefold()
or query
in book.get(
"author",
"",
).casefold()
or query
in book.get(
"isbn",
"",
).casefold()
)
]
if not results:
print("کتابی پیدا نشد.")
return
show_books(results)
def borrow_book(books):
value = input("شناسه کتاب: ").strip()
try:
book_id = int(value)
except ValueError:
print("شناسه باید عدد صحیح باشد.")
return
book = find_book_by_id(books, book_id)
if book is None:
print("کتاب پیدا نشد.")
return
if not book.get("available", True):
print(
"این کتاب قبلاً امانت داده شده است."
)
return
borrower = input(
"نام امانتگیرنده: "
).strip()
if not borrower:
print(
"نام امانتگیرنده نمیتواند خالی باشد."
)
return
book["available"] = False
book["borrower"] = borrower
book["borrowed_at"] = (
date.today().isoformat()
)
save_books(books)
print("امانت کتاب ثبت شد.")
def return_book(books):
value = input("شناسه کتاب: ").strip()
try:
book_id = int(value)
except ValueError:
print("شناسه باید عدد صحیح باشد.")
return
book = find_book_by_id(books, book_id)
if book is None:
print("کتاب پیدا نشد.")
return
if book.get("available", True):
print(
"این کتاب در حال حاضر موجود است."
)
return
book["available"] = True
book["borrower"] = ""
book["borrowed_at"] = ""
save_books(books)
print("بازگشت کتاب ثبت شد.")
def show_available_books(books):
rows = [
book
for book in books
if book.get("available", True)
]
if not rows:
print("کتاب موجودی نداریم.")
return
show_books(rows)
def show_borrowed_books(books):
rows = [
book
for book in books
if not book.get("available", True)
]
if not rows:
print("هیچ کتابی در امانت نیست.")
return
show_books(rows)
def library_report(books):
total = len(books)
available = sum(
1
for book in books
if book.get("available", True)
)
borrowed = total - available
print("\n=== گزارش کتابخانه ===")
print(f"کل کتابها: {total}")
print(f"موجود: {available}")
print(f"در امانت: {borrowed}")
def delete_book(books):
value = input(
"شناسه کتاب برای حذف: "
).strip()
try:
book_id = int(value)
except ValueError:
print("شناسه باید عدد صحیح باشد.")
return
book = find_book_by_id(books, book_id)
if book is None:
print("کتاب پیدا نشد.")
return
if not book.get("available", True):
print(
"کتاب امانتدادهشده را "
"ابتدا برگردانید."
)
return
confirm = input(
f"حذف {book['title']}؟ (بله/خیر): "
).strip().lower()
if confirm not in {
"بله",
"yes",
"y",
}:
print("حذف لغو شد.")
return
books.remove(book)
save_books(books)
print("کتاب حذف شد.")
def show_menu():
print("\n=== مدیریت کتابخانه ===")
print("1) ثبت کتاب")
print("2) نمایش همه کتابها")
print("3) جستوجوی کتاب")
print("4) امانت کتاب")
print("5) بازگشت کتاب")
print("6) کتابهای موجود")
print("7) کتابهای امانتشده")
print("8) گزارش کتابخانه")
print("9) حذف کتاب")
print("0) خروج")
def main():
books = load_books()
while True:
show_menu()
choice = input(
"انتخاب شما: "
).strip()
if choice == "1":
add_book(books)
elif choice == "2":
show_books(books)
elif choice == "3":
search_books(books)
elif choice == "4":
borrow_book(books)
elif choice == "5":
return_book(books)
elif choice == "6":
show_available_books(books)
elif choice == "7":
show_borrowed_books(books)
elif choice == "8":
library_report(books)
elif choice == "9":
delete_book(books)
elif choice == "0":
print("برنامه پایان یافت.")
break
else:
print("گزینه نامعتبر است.")
if __name__ == "__main__":
main()نمونه اجرای برنامه
=== مدیریت کتابخانه ===
1) ثبت کتاب
2) نمایش همه کتابها
3) جستوجوی کتاب
4) امانت کتاب
5) بازگشت کتاب
6) کتابهای موجود
7) کتابهای امانتشده
8) گزارش کتابخانه
9) حذف کتاب
0) خروج
انتخاب شما: 1
عنوان کتاب: Clean Code
نام نویسنده: Robert C. Martin
ISBN (اختیاری): 9780132350884
کتاب با شناسه 1 ثبت شد.نمونه ثبت امانت
شناسه کتاب: 1
نام امانتگیرنده: علی رضایی
امانت کتاب ثبت شد.اگر بعد از آن فهرست را نمایش دهید، وضعیت کتاب به شکل زیر خواهد بود:
#1 | Clean Code | Robert C. Martin | امانت | علی رضایی | 2026-08-11کد پروژه چگونه بررسی شد؟
قبل از انتشار، ساختار اصلی کد از نظر Syntax بررسی شد و سناریوهای پایه نیز تست شدند: ثبت کتاب جدید، تولید ID، امانت کتاب، جلوگیری از امانت مجدد، بازگشت کتاب، جستوجوی عنوان و محاسبه گزارش موجودی. این تستها نشان میدهند منطق اصلی پروژه قبل از افزودن قابلیتهای پیشرفتهتر درست کار میکند.
اشتباههای رایج در پروژه مدیریت کتابخانه
استفاده از عنوان بهعنوان شناسه
ممکن است چند نسخه از یک کتاب یا چند کتاب با عنوان مشابه وجود داشته باشند. استفاده از ID عددی باعث میشود هر رکورد مستقل و قابل مدیریت باشد.
نداشتن وضعیت موجودی
اگر فقط نام امانتگیرنده را ذخیره کنیم، بررسی وضعیت کتاب سختتر میشود. یک Boolean واضح مثل available منطق برنامه را خواناتر میکند.
امانت دوباره کتاب
قبل از تغییر وضعیت باید بررسی کنیم کتاب در حال حاضر موجود باشد. همین کنترل ساده از ناسازگاری داده جلوگیری میکند.
حذف کتاب در حال امانت
حذف کتابی که هنوز نزد امانتگیرنده است باعث از بین رفتن سابقه مهم میشود. در این پروژه ابتدا بازگشت کتاب اجباری است.
ذخیره نکردن بعد از عملیات
بعد از ثبت، امانت، بازگشت یا حذف باید فایل JSON دوباره نوشته شود؛ در غیر این صورت تغییرات فقط در حافظه باقی میمانند.
چطور پروژه را حرفهایتر کنیم؟
- تاریخ سررسید: هنگام امانت یک Due Date مثلاً هفت یا چهارده روز بعد تعیین کنید.
- کتابهای دیرکرد: کتابهایی را که از تاریخ بازگشتشان گذشته نمایش دهید.
- تاریخچه امانت: بهجای پاک کردن اطلاعات امانت بعد از بازگشت، آن را در یک لیست History نگه دارید.
- مدیریت اعضا: اعضا را با ID، نام و اطلاعات تماس در فایل جداگانه نگه دارید.
- تعداد نسخه: یک عنوان بتواند چند نسخه فیزیکی داشته باشد.
- جریمه دیرکرد: بر اساس تعداد روز تأخیر مبلغ جریمه محاسبه کنید.
- ویرایش کتاب: امکان اصلاح عنوان، نویسنده یا ISBN را اضافه کنید.
- مرتبسازی: فهرست را بر اساس عنوان، نویسنده یا وضعیت مرتب کنید.
- خروجی CSV: گزارش موجودی را برای Excel خروجی بگیرید.
- SQLite: در نسخه پیشرفته JSON را با دیتابیس جایگزین کنید.
- OOP: کلاسهای Book، Member و Library بسازید.
- رابط گرافیکی: نسخه Tkinter با جدول کتابها و فرم امانت طراحی کنید.
تمرین پیشنهادی: اضافه کردن تاریخ سررسید
در زمان امانت، علاوه بر borrowed_at یک فیلد جدید به نام due_date ذخیره کنید. میتوانید با timedelta تاریخ هفت روز بعد را محاسبه کنید:
from datetime import date, timedelta
borrowed_at = date.today()
due_date = borrowed_at + timedelta(days=7)سپس در گزارش امانتها مشخص کنید کدام کتابها از تاریخ سررسید عبور کردهاند.
تمرین پیشرفته: تبدیل به OOP
بعد از اینکه نسخه تابعمحور پروژه را کامل اجرا کردید، آن را به طراحی شیگرا تبدیل کنید. کلاس Book میتواند اطلاعات یک کتاب را مدیریت کند و کلاس Library مسئول ثبت، جستوجو، امانت، بازگشت و ذخیره دادهها باشد. پروژه مدیریت مخاطبان با JSON و OOP نمونه مناسبی برای دیدن ساختار یک پروژه شیگرا و چندعملیاتی است.
این پروژه چه مفاهیمی را تمرین میدهد؟
- List و Dictionary
- Boolean و مدیریت State
- JSON
- Pathlib
- date و datetime
- خواندن و نوشتن فایل
- توابع و مقدار بازگشتی
- حلقه for و while
- شرطها
- Exception Handling
- List Comprehension
- Generator Expression
- جستوجو و فیلتر داده
- اعتبارسنجی ورودی
- مدیریت قوانین کسبوکار
- گزارشگیری و Aggregation
- ماندگاری اطلاعات
جمعبندی
در این آموزش یک برنامه کامل مدیریت کتابخانه با پایتون ساختیم که میتواند کتاب جدید ثبت کند، اطلاعات را جستوجو کند، وضعیت موجود یا امانت را مدیریت کند، نام امانتگیرنده و تاریخ را ذخیره کند، بازگشت کتاب را ثبت کند، گزارش موجودی بسازد و همه اطلاعات را داخل فایل JSON نگه دارد.
مهمترین نکته این پروژه، مدیریت صحیح State است. کتاب فقط یک مجموعه متن نیست؛ در هر لحظه یک وضعیت مشخص دارد و عملیات بعدی بر اساس همان وضعیت مجاز یا غیرمجاز میشوند. این مفهوم پایه بسیاری از برنامههای واقعی است. بعد از تکمیل نسخه فعلی، افزودن تاریخ سررسید و تاریخچه امانت بهترین مسیر برای توسعه پروژه خواهد بود.
📌 همچنین بخوانید: تمرین پایتون با جواب، ساخت بازی با پایتون و Pygame و ساخت ربات با پایتون
برای ذخیره پیشرفت در همه دستگاهها و دریافت گواهی پایان مسیر، رایگان ثبتنام کن یا وارد شو.