الدرس 30 من دورة Python للمبتدئين

حفظ وقراءة البيانات: اجعل المنتجات لا تضيع بعد إغلاق البرنامج

في الدرس السابق أضفنا تعديل المنتج أو سعره. الآن سنضيف خطوة مهمة جدًا: حفظ المنتجات في ملف JSON وقراءتها عند تشغيل البرنامج من جديد.

المستوى: مبتدئ جدًا النتيجة: products.json مدة التطبيق: 45 - 50 دقيقة
طريقة CIT للتعلم الهادئ: لا تحفظ json.dump و json.load فقط. افهم الفكرة: البرنامج يكتب المنتجات في ملف، ثم يقرأ نفس الملف عند التشغيل القادم.
مهم قبل البداية: سنضيف import json في أعلى الملف. هذا يعني أننا سنستخدم مكتبة جاهزة داخل Python للتعامل مع ملفات JSON.

ما النتيجة التي يجب أن تخرج بها؟

في نهاية الدرس سيظهر بجانب ملف البرنامج ملف جديد باسم:

products.json

وسيحفظ داخله المنتجات بهذا الشكل تقريبًا:

[
    {
        "name": "Keyboard",
        "price": 100.0
    },
    {
        "name": "Mouse",
        "price": 50.0
    }
]
json.dump تحفظ البيانات في ملف.
json.load تقرأ البيانات من ملف.
encoding يحافظ على النص العربي.
خريطة درس حفظ وقراءة البيانات من ملف في Python
مفهوم ملف JSON لحفظ المنتجات في Python
1

لماذا نحتاج حفظ البيانات؟

في النسخة السابقة، إذا أغلقت البرنامج ثم شغلته مرة أخرى، تبدأ قائمة المنتجات من الصفر.

هذا مقبول في التمرين، لكنه غير مناسب لبرنامج حقيقي. البرنامج الحقيقي يجب أن يحافظ على البيانات.

الفكرة ببساطة: الذاكرة المؤقتة تضيع عند إغلاق البرنامج، أما الملف فيبقى محفوظًا على الجهاز.
تحقق قبل الانتقال ✅ هل فهمت أن products داخل البرنامج مؤقتة، لكن products.json يبقى محفوظًا؟
2

أنشئ ملف الدرس الثلاثين

افتح مجلد الدورة داخل VS Code:

python_course

ثم أنشئ ملفًا جديدًا باسم:

lesson_30.py

افتح الملف داخل Editor.

توقف لحظة ✅ هل ظهر ملف lesson_30.py داخل Explorer؟ إذا نعم، انتقل للخطوة التالية.
3

أضف import json واسم ملف البيانات

في أعلى الملف، اكتب:

import json

DATA_FILE = "products.json"
TAX_RATE = 0.15

السطر import json يجعل Python يعرف أننا نريد استخدام أدوات JSON.

لماذا DATA_FILE؟ حتى لا نكتب اسم الملف products.json في أكثر من مكان. إذا أردنا تغيير الاسم لاحقًا نغيره من مكان واحد فقط.
سؤال سريع 🧠 أين نضع import json؟ في أعلى الملف قبل الدوال.
4

اكتب دالة حفظ المنتجات

دالة الحفظ تأخذ قائمة المنتجات وتكتبها في ملف:

def save_products(products):
    with open(
        DATA_FILE,
        "w",
        encoding="utf-8"
    ) as file:
        json.dump(
            products,
            file,
            ensure_ascii=False,
            indent=4
        )

معنى "w" أننا نفتح الملف للكتابة. وإذا لم يكن الملف موجودًا، ينشئه Python تلقائيًا.

مهم للعربية: استخدمنا encoding="utf-8" و ensure_ascii=False حتى لا تتحول النصوص العربية إلى رموز غير مفهومة.
تحقق قبل الانتقال ✅ هل فهمت أن save_products لا تطبع شيئًا؟ هي تحفظ البيانات فقط.
5

اكتب دالة قراءة المنتجات

الآن نحتاج دالة تقرأ المنتجات من الملف:

def load_products():
    try:
        with open(
            DATA_FILE,
            "r",
            encoding="utf-8"
        ) as file:
            products = json.load(file)
            return products
    except FileNotFoundError:
        return []

إذا كان الملف موجودًا، تقرأ الدالة المنتجات وترجعها. وإذا لم يكن موجودًا، ترجع قائمة فارغة.

لماذا FileNotFoundError؟ أول مرة تشغل البرنامج لن يكون ملف products.json موجودًا. بدل أن ينهار البرنامج، نبدأ بقائمة فارغة.
سؤال سريع 🧠 لماذا نرجع [] إذا لم يوجد الملف؟ لأن البرنامج يبدأ بدون منتجات محفوظة.
6

اكتب دالة حساب الإجمالي من المنتجات

بما أننا سنقرأ products من ملف، نحتاج إعادة حساب total من المنتجات نفسها:

def calculate_total(products):
    total = 0

    for product in products:
        total = total + product["price"]

    return total

هذه الدالة تمر على المنتجات وتجمع أسعارها.

لماذا هذا أفضل؟ لأننا لا نحتاج حفظ total في الملف. نحفظ المنتجات فقط، ثم نحسب total منها عند الحاجة.
تحقق قبل الانتقال ✅ هل فهمت أن total يمكن حسابه من products في أي وقت؟
7

متى نحفظ البيانات؟

يجب حفظ المنتجات بعد أي عملية تغيّر القائمة:

تدفق قراءة وحفظ المنتجات في Python
بعد الإضافة لأن القائمة زادت منتجًا جديدًا.
بعد الحذف لأن القائمة نقصت منتجًا.
بعد التعديل لأن اسم المنتج أو سعره تغيّر.
سؤال سريع 🧠 هل نحتاج حفظ البيانات بعد عرض التقرير؟ لا، لأن التقرير لا يغير المنتجات.
8

رتّب الكود النهائي على أجزاء

الجزء الأول: الاستيراد والإعدادات:

import json

DATA_FILE = "products.json"
TAX_RATE = 0.15

الجزء الثاني: دوال الحفظ والقراءة:

أولًا: دالة الحفظ تفتح الملف للكتابة:

def save_products(products):
    with open(
        DATA_FILE,
        "w",
        encoding="utf-8"
    ) as file:

ثانيًا: نكتب المنتجات داخل الملف بصيغة JSON:

        json.dump(
            products,
            file,
            ensure_ascii=False,
            indent=4
        )

ثالثًا: دالة القراءة تفتح الملف للقراءة:

def load_products():
    try:
        with open(
            DATA_FILE,
            "r",
            encoding="utf-8"
        ) as file:

رابعًا: نقرأ المنتجات أو نرجع قائمة فارغة إذا لم يوجد الملف:

            products = json.load(file)
            return products
    except FileNotFoundError:
        return []

الجزء الثالث: دوال الحساب:

def calculate_total(products):
    total = 0

    for product in products:
        total = total + product["price"]

    return total

def calculate_tax(total):
    return total * TAX_RATE

def calculate_final_total(total, tax):
    return total + tax

الجزء الرابع: إنشاء المنتج والقائمة:

def create_product(product_name, price):
    product = {
        "name": product_name,
        "price": price
    }

    return product

def print_menu():
    print()
    print("----- قائمة البرنامج -----")
    print("1. إضافة منتج")
    print("2. عرض التقرير")
    print("3. حذف منتج")
    print("4. تعديل منتج")
    print("5. خروج")

الجزء الخامس: دالة إضافة منتج:

أولًا: نبدأ دالة إضافة المنتج ونقرأ الاسم والسعر:

def add_product(products):
    name_prompt = "اسم المنتج: "
    price_prompt = "سعر المنتج: "

    product_name = input(name_prompt).strip()
    price_text = input(price_prompt).strip()

ثانيًا: نحاول تحويل السعر إلى رقم:

    try:
        price = float(price_text)
    except ValueError:
        print("السعر غير صحيح، اكتب رقمًا فقط")
        return 0

ثالثًا: نمنع السعر السالب:

    if price < 0:
        print("السعر لا يمكن أن يكون سالبًا")
        return 0

رابعًا: ننشئ المنتج ونضيفه إلى القائمة:

    product = create_product(
        product_name,
        price
    )

    products.append(product)

خامسًا: نحفظ المنتجات ثم نرجع السعر:

    save_products(products)

    print("تمت إضافة المنتج وحفظه بنجاح")
    return price

الجزء السادس: عرض المنتجات:

def print_products(products):
    if len(products) == 0:
        print("لا توجد منتجات حتى الآن")
        return False

    print("----- المنتجات الحالية -----")

    for index in range(len(products)):
        product = products[index]
        name = product["name"]
        price = product["price"]
        product_number = index + 1

        print(
            f"{product_number}. {name}: "
            f"{price:.2f} ريال"
        )

    return True

الجزء السابع: حذف المنتج مع الحفظ:

أولًا: نبدأ دالة الحذف ونتأكد أن هناك منتجات:

def delete_product(products):
    if print_products(products) == False:
        return 0

ثانيًا: نقرأ رقم المنتج ونحوله إلى رقم:

    delete_prompt = "اكتب رقم المنتج المراد حذفه: "
    delete_text = input(delete_prompt).strip()

    try:
        delete_number = int(delete_text)
    except ValueError:
        print("رقم المنتج غير صحيح")
        return 0

ثالثًا: نحول رقم المستخدم إلى index ونتحقق منه:

    index = delete_number - 1

    if index < 0 or index >= len(products):
        print("رقم المنتج غير موجود")
        return 0

رابعًا: نحذف المنتج ونحفظ التغيير:

    deleted_product = products.pop(index)
    save_products(products)

خامسًا: نطبع اسم المنتج المحذوف ونرجع سعره:

    deleted_name = deleted_product["name"]
    print(f"تم حذف المنتج وحفظ التغيير: {deleted_name}")

    return deleted_product["price"]

الجزء الثامن: تعديل المنتج مع الحفظ:

أولًا: نبدأ دالة التعديل ونتأكد أن هناك منتجات:

def edit_product(products):
    if print_products(products) == False:
        return 0

ثانيًا: نقرأ رقم المنتج ونحوله إلى رقم:

    edit_prompt = "اكتب رقم المنتج المراد تعديله: "
    edit_text = input(edit_prompt).strip()

    try:
        edit_number = int(edit_text)
    except ValueError:
        print("رقم المنتج غير صحيح")
        return 0

ثالثًا: نحول رقم المستخدم إلى index ونتحقق منه:

    index = edit_number - 1

    if index < 0 or index >= len(products):
        print("رقم المنتج غير موجود")
        return 0

رابعًا: نصل إلى المنتج ونحفظ السعر القديم:

    product = products[index]
    old_price = product["price"]

أكمل دالة التعديل:

    new_name = input(
        "اكتب الاسم الجديد أو Enter للإبقاء عليه: "
    ).strip()

    if new_name != "":
        product["name"] = new_name

    new_price_text = input(
        "اكتب السعر الجديد أو Enter للإبقاء عليه: "
    ).strip()

    if new_price_text == "":
        save_products(products)
        print("تم تعديل المنتج وحفظه")
        return 0

افحص السعر الجديد ثم احفظ:

    try:
        new_price = float(new_price_text)
    except ValueError:
        print("السعر غير صحيح")
        return 0

    if new_price < 0:
        print("السعر لا يمكن أن يكون سالبًا")
        return 0

    product["price"] = new_price
    price_difference = new_price - old_price

    save_products(products)

    print("تم تعديل المنتج وحفظه")
    return price_difference

الجزء التاسع: دالة التقرير:

def print_report(products, total, tax, final_total):
    if len(products) == 0:
        print("لا توجد منتجات حتى الآن")
        return

    print("----- تقرير المنتجات -----")

    for product in products:
        name = product["name"]
        price = product["price"]

        print(f"{name}: {price:.2f} ريال")

    print(f"عدد المنتجات: {len(products)}")
    print(f"الإجمالي قبل الضريبة: {total:.2f} ريال")
    print(f"الضريبة: {tax:.2f} ريال")
    print(f"الإجمالي النهائي: {final_total:.2f} ريال")

الجزء العاشر: main تقرأ المنتجات عند بداية التشغيل:

def main():
    products = load_products()
    total = calculate_total(products)
    menu_prompt = "اختر رقم الأمر: "

أوامر الإضافة والحذف والتعديل داخل main:

    while True:
        print_menu()
        choice = input(menu_prompt).strip()

        if choice == "1":
            added_price = add_product(products)
            total = total + added_price

        elif choice == "3":
            deleted_price = delete_product(products)
            total = total - deleted_price

        elif choice == "4":
            price_difference = edit_product(
                products
            )
            total = total + price_difference

عرض التقرير والخروج:

إذا اختار المستخدم عرض التقرير، نحسب الضريبة والإجمالي النهائي:

        elif choice == "2":
            tax = calculate_tax(total)
            final_total = calculate_final_total(
                total,
                tax
            )

ثم نطبع التقرير:

            print_report(
                products,
                total,
                tax,
                final_total
            )

وإذا اختار الخروج أو كتب اختيارًا خاطئًا:

        elif choice == "5":
            print("تم إنهاء البرنامج")
            break
        else:
            print("اختيار غير صحيح، حاول مرة أخرى")

وأخيرًا نشغل البرنامج:

main()
مهم جدًا: الأجزاء السابقة تُكتب في نفس ملف lesson_30.py بالترتيب. عند تشغيل البرنامج سيُنشئ ملف products.json بجانب ملف الدرس.
تحقق قبل الانتقال ✅ هل فهمت أن products تُقرأ عند البداية وتُحفظ بعد التغيير؟
9

شغّل البرنامج واختبر الحفظ

احفظ الملف:

Ctrl + S

ثم شغله:

python lesson_30.py

وإذا لم يعمل أمر python:

py lesson_30.py

أضف منتجًا:

اختر رقم الأمر: 1
اسم المنتج: Keyboard
سعر المنتج: 100
تمت إضافة المنتج وحفظه بنجاح

ستلاحظ ظهور ملف جديد:

products.json
تحقق قبل الانتقال ✅ هل ظهر ملف products.json داخل مجلد الدورة؟
10

أغلق البرنامج ثم شغله مرة أخرى

اخرج من البرنامج:

اختر رقم الأمر: 5
تم إنهاء البرنامج

ثم شغله مرة أخرى:

python lesson_30.py

اختر عرض التقرير:

اختر رقم الأمر: 2
----- تقرير المنتجات -----
Keyboard: 100.00 ريال
عدد المنتجات: 1
الإجمالي قبل الضريبة: 100.00 ريال
الضريبة: 15.00 ريال
الإجمالي النهائي: 115.00 ريال
ماذا تعلمت؟ المنتج لم يضِع بعد إغلاق البرنامج؛ لأننا قرأناه من products.json عند التشغيل الجديد.
توقف لحظة ✅ لا تعتمد الدرس قبل أن تغلق البرنامج وتفتحه مرة أخرى وتتأكد أن المنتج بقي محفوظًا.
11

تحدي صغير: افتح ملف products.json

افتح ملف products.json داخل VS Code، ولاحظ كيف تبدو البيانات:

[
    {
        "name": "Keyboard",
        "price": 100.0
    }
]
هدف التحدي: تفهم أن الملف ليس غامضًا. هو مجرد بيانات مكتوبة بطريقة منظمة يستطيع Python قراءتها.
تحدي التفكير 🧠 لماذا لا نحفظ total داخل الملف؟ لأن total يمكن حسابه من أسعار المنتجات في أي وقت.

اختبار نهاية الدرس

قبل الانتقال للدرس القادم، تأكد أنك فهمت هذه النقاط:

✅ فهمت لماذا نحتاج حفظ البيانات.
✅ استخدمت import json.
✅ كتبت save_products.
✅ كتبت load_products.
✅ استخدمت FileNotFoundError لأول تشغيل.
✅ استخدمت encoding="utf-8" لحماية العربية.
✅ حفظت بعد الإضافة والحذف والتعديل.
✅ شغلت البرنامج مرة ثانية ووجدت المنتجات محفوظة.
إذا فهمت هذه النقاط: فأنت الآن انتقلت من برنامج مؤقت إلى برنامج يحتفظ ببياناته مثل البرامج الحقيقية.

خلاصة الدرس

تعلمنا كيف نحفظ المنتجات في ملف products.json، وكيف نقرأها عند تشغيل البرنامج، وكيف نحسب total من المنتجات المحفوظة.

تذكر القاعدة:

اقرأ البيانات عند بداية البرنامج
احفظ البيانات بعد أي تغيير
احسب الإجمالي من المنتجات المحفوظة

في الدرس القادم سنحسن التقرير النهائي ليصبح أوضح وأكثر ترتيبًا، ونجهز المشروع لمرحلة الخاتمة.