في هذا المشروع ستبني برنامج جهات اتصال ببايثون يعمل من سطر الأوامر، ويتيح لك إضافة جهات الاتصال وعرضها والبحث عنها وتعديلها وحذفها. هذه العمليات تمثل أساس CRUD: الإنشاء والقراءة والتحديث والحذف.
ولكي لا تختفي البيانات بعد إغلاق البرنامج، سنحفظها داخل ملف contacts.json باستخدام مكتبة json المدمجة، ثم نحمّلها تلقائيًا عند التشغيل التالي. سنعالج أيضًا الملف المفقود أو الفارغ أو غير الصالح حتى لا يتوقف البرنامج فجأة.
الشرح مناسب للمبتدئ ويعتمد على List وDictionary والدوال وwhile وtry/except وpathlib. وهو الدرس 19 من سلسلة بايثون بالمثال، والهدف فيه تطبيق هذه المفاهيم داخل مشروع واحد قابل للتشغيل والحفظ.
{alertInfo} الفكرة ببساطة: سنبني Contact Book كاملًا بعمليات الإضافة والعرض والبحث والتعديل والحذف، ثم نحفظ كل تغيير في JSON حتى تبقى البيانات بعد إغلاق البرنامج.
{getToc} $title={محتوى المقال}
مشروع جهات اتصال ببايثون باستخدام JSON: ماذا سنبني؟
سننشئ برنامج دفتر جهات اتصال يوفر الخيارات التالية:
- إضافة جهة اتصال جديدة.
- عرض جميع جهات الاتصال.
- البحث عن جهة اتصال بالاسم.
- تعديل رقم الهاتف أو البريد الإلكتروني.
- حذف جهة اتصال بعد التأكيد.
- حفظ البيانات تلقائيًا داخل ملف
JSON. - تحميل البيانات السابقة عند تشغيل البرنامج.
- التعامل مع ملف مفقود أو فارغ أو غير صالح.
عمليًا، يغطي المشروع دورة CRUD كاملة: Create للإضافة، وRead للعرض والبحث، وUpdate للتعديل، وDelete للحذف. أما ملف JSON فيوفر Persistence حتى تبقى البيانات بين مرات التشغيل.
سيعمل البرنامج من خلال قائمة نصية تظهر في Terminal أو CMD، ويختار المستخدم العملية بكتابة رقم من 1 إلى 6.
المهارات التي ستتدرب عليها
| المهارة | كيف سنستخدمها؟ |
|---|---|
List | تخزين جميع جهات الاتصال داخل قائمة واحدة. |
Dictionary | تخزين الاسم ورقم الهاتف والبريد لكل شخص. |
| الدوال | تقسيم الإضافة والبحث والحذف والحفظ إلى وظائف مستقلة. |
if وwhile | التحقق من المدخلات وتشغيل القائمة الرئيسية باستمرار. |
ملفات JSON | حفظ البيانات وقراءتها بين مرات تشغيل البرنامج. |
try وexcept | منع توقف البرنامج عند وجود مشكلة في ملف البيانات. |
pathlib | تحديد مسار ملف جهات الاتصال بطريقة واضحة. |
هيكل ملفات المشروع
أنشئ مجلدًا جديدًا باسم contact_book، ثم أنشئ داخله ملف بايثون باسم contact_book.py. وإذا أردت فهم تنظيم الملفات بمستوى أوسع، راجع تنظيم ملفات مشروع بايثون.
contact_book/
│
├── contact_book.py
└── contacts.json
لا تحتاج إلى إنشاء ملف contacts.json يدويًا. سيقوم البرنامج بإنشائه عند حفظ أول جهة اتصال. وضعناه في المخطط فقط حتى تفهم شكل المشروع بعد تشغيله.
كيف سننظم بيانات جهات الاتصال؟
كل جهة اتصال تحتوي على أكثر من معلومة، لذلك القاموس Dictionary مناسب لتمثيلها. وإذا كان مفهوم القواميس جديدًا عليك، راجع شرح Dictionary في بايثون.
contact = {
"name": "Ali",
"phone": "+967 777 123 456",
"email": "ali@example.com"
}
المفاتيح في القاموس هي name للاسم، وphone لرقم الهاتف، وemail للبريد الإلكتروني.
ولأن البرنامج سيحتوي على أكثر من جهة اتصال، سنضع القواميس داخل قائمة:
contacts = [
{
"name": "Ali",
"phone": "+967 777 123 456",
"email": "ali@example.com"
},
{
"name": "Sara",
"phone": "+967 733 987 654",
"email": "sara@example.com"
}
]
بهذا التنظيم يمكنك المرور على جميع جهات الاتصال باستخدام حلقة، أو البحث داخلها، أو تحويلها بسهولة إلى ملف JSON.
النسخة الأولى: إضافة جهة اتصال إلى قائمة
سنبدأ بنسخة بسيطة لا تحفظ البيانات في ملف بعد. أنشئ قائمة فارغة، ثم اطلب من المستخدم إدخال البيانات.
contacts = []
name = input("أدخل الاسم: ").strip()
phone = input("أدخل رقم الهاتف: ").strip()
email = input("أدخل البريد الإلكتروني: ").strip()
contact = {
"name": name,
"phone": phone,
"email": email
}
contacts.append(contact)
print(contacts)
استخدمنا strip() لإزالة المسافات الزائدة من بداية ونهاية الإدخال، ثم أنشأنا قاموسًا وأضفناه إلى القائمة باستخدام append().
تحويل الإضافة إلى دالة مستقلة
من الأفضل وضع منطق الإضافة داخل دالة، حتى نستطيع استدعاءه من القائمة الرئيسية كلما اختار المستخدم إضافة جهة اتصال.
def add_contact(contacts):
name = input("أدخل الاسم: ").strip()
phone = input("أدخل رقم الهاتف: ").strip()
email = input("أدخل البريد الإلكتروني: ").strip()
contact = {
"name": name,
"phone": phone,
"email": email
}
contacts.append(contact)
print("تمت إضافة جهة الاتصال بنجاح.")
الدالة تستقبل القائمة نفسها، ثم تضيف القاموس الجديد إليها. لأن القائمة قابلة للتعديل، ستبقى جهة الاتصال مضافة بعد انتهاء الدالة.
التحقق من الاسم ورقم الهاتف
لا نريد قبول اسم فارغ أو رقم هاتف مكوّن من حروف. نبدأ بدالة تتحقق من رقم الهاتف بعد إزالة المسافات والشرطات وعلامة + في البداية.
def is_valid_phone(phone):
cleaned_phone = phone.replace(" ", "").replace("-", "")
if cleaned_phone.startswith("+"):
cleaned_phone = cleaned_phone[1:]
return cleaned_phone.isdigit() and len(cleaned_phone) >= 7
هذا تحقق مبسط للتدريب، وليس نظامًا عالميًا لجميع أرقام الهواتف؛ لأن أطوال وصيغ الأرقام تختلف بين الدول.
التحقق من البريد الإلكتروني
سنجعل البريد اختياريًا. إذا تركه المستخدم فارغًا نقبله، وإذا كتب قيمة نتحقق بصورة مبسطة من وجود @ ونقطة داخل اسم النطاق.
def is_valid_email(email):
if not email:
return True
if "@" not in email:
return False
domain = email.split("@", 1)[1]
return "." in domain
منع تكرار جهة الاتصال بالاسم
ننشئ دالة تبحث عن الاسم وتعيد موقعه داخل القائمة. نستخدم casefold() حتى لا يجعل اختلاف الحروف الكبيرة والصغيرة الاسم يبدو جديدًا.
def find_contact_index(contacts, name):
normalized_name = name.casefold()
for index, contact in enumerate(contacts):
if contact["name"].casefold() == normalized_name:
return index
return None
دالة الإضافة بعد التحقق من البيانات
def add_contact(contacts):
name = input("أدخل الاسم: ").strip()
if not name:
print("الاسم لا يمكن أن يكون فارغًا.")
return
if find_contact_index(contacts, name) is not None:
print("توجد جهة اتصال بهذا الاسم مسبقًا.")
return
phone = input("أدخل رقم الهاتف: ").strip()
if not is_valid_phone(phone):
print("رقم الهاتف غير صالح.")
return
email = input(
"أدخل البريد الإلكتروني أو اتركه فارغًا: "
).strip()
if not is_valid_email(email):
print("البريد الإلكتروني غير صالح.")
return
contacts.append({
"name": name,
"phone": phone,
"email": email
})
print("تمت إضافة جهة الاتصال بنجاح.")
عرض جميع جهات الاتصال
نستخدم حلقة for مع enumerate() لعرض كل جهة اتصال برقم متسلسل.
def list_contacts(contacts):
if not contacts:
print("لا توجد جهات اتصال محفوظة.")
return
print("\n--- جهات الاتصال ---")
for number, contact in enumerate(contacts, start=1):
email = contact["email"] or "غير مضاف"
print(f"\n{number}. الاسم: {contact['name']}")
print(f" الهاتف: {contact['phone']}")
print(f" البريد: {email}")
البحث عن جهة اتصال بالاسم
سنسمح بالبحث باستخدام جزء من الاسم، وليس الاسم كاملًا فقط. نستخدم قائمة مشتقة لجمع النتائج، ثم نعيد استخدام دالة العرض.
def search_contacts(contacts):
query = input("أدخل الاسم أو جزءًا منه: ").strip()
if not query:
print("اكتب اسمًا للبحث.")
return
normalized_query = query.casefold()
matches = [
contact
for contact in contacts
if normalized_query in contact["name"].casefold()
]
if not matches:
print("لم يتم العثور على جهة اتصال مطابقة.")
return
list_contacts(matches)
تعديل جهة اتصال موجودة
نطلب الاسم الكامل، ثم نصل إلى القاموس المطلوب. إذا ضغط المستخدم Enter دون كتابة قيمة جديدة، نحتفظ بالقيمة الحالية.
def update_contact(contacts):
name = input("أدخل الاسم الكامل لجهة الاتصال: ").strip()
index = find_contact_index(contacts, name)
if index is None:
print("جهة الاتصال غير موجودة.")
return
contact = contacts[index]
new_phone = input(
f"رقم الهاتف الجديد [{contact['phone']}]: "
).strip()
new_email = input(
f"البريد الجديد [{contact['email'] or 'غير مضاف'}]: "
).strip()
if new_phone:
if not is_valid_phone(new_phone):
print("رقم الهاتف الجديد غير صالح.")
return
contact["phone"] = new_phone
if new_email:
if not is_valid_email(new_email):
print("البريد الإلكتروني الجديد غير صالح.")
return
contact["email"] = new_email
print("تم تعديل جهة الاتصال بنجاح.")
{alertInfo} في هذه النسخة، ترك البريد فارغًا أثناء التعديل يعني الاحتفاظ بالبريد الحالي، وليس حذفه. يمكنك لاحقًا إضافة خيار مستقل لمسح البريد.
حذف جهة اتصال بعد التأكيد
def delete_contact(contacts):
name = input("أدخل الاسم الكامل لجهة الاتصال: ").strip()
index = find_contact_index(contacts, name)
if index is None:
print("جهة الاتصال غير موجودة.")
return
contact = contacts[index]
confirmation = input(
f"هل تريد حذف {contact['name']}؟ (y/n): "
).strip().lower()
if confirmation == "y":
contacts.pop(index)
print("تم حذف جهة الاتصال.")
else:
print("تم إلغاء الحذف.")
لماذا تختفي البيانات عند إغلاق البرنامج؟
حتى الآن تُحفظ البيانات داخل الذاكرة فقط. عندما ينتهي تشغيل البرنامج، تختفي القائمة وجميع القواميس الموجودة فيها.
لحل المشكلة نستخدم ملف JSON، لأنه مناسب للقوائم والقواميس، وسهل القراءة، ومكتبته مدمجة مع بايثون. ولشرح الصيغة والدوال الأساسية بتفصيل منفصل، راجع شرح مكتبة JSON في بايثون.
تحديد مسار ملف البيانات باستخدام pathlib
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
DATA_FILE = BASE_DIR / "contacts.json"
بهذا يصبح ملف contacts.json بجانب contact_book.py حتى لو شغّلت البرنامج من مجلد آخر في Terminal. أما Path("contacts.json") وحدها فتعتمد على مجلد التشغيل الحالي، وقد تجعل الملف يظهر في مكان مختلف عما تتوقع.
حفظ جهات الاتصال باستخدام json.dump
import json
def save_contacts(contacts):
try:
with DATA_FILE.open("w", encoding="utf-8") as file:
json.dump(
contacts,
file,
ensure_ascii=False,
indent=4
)
except OSError as error:
print(f"تعذر حفظ البيانات: {error}")
يضمن encoding="utf-8" دعم النص العربي، ويجعل ensure_ascii=False العربية ظاهرة طبيعيًا، بينما ينظم indent=4 الملف.
قد يصبح محتوى الملف بعد إضافة جهتي اتصال بهذا الشكل:
[
{
"name": "Ali",
"phone": "+967 777 123 456",
"email": "ali@example.com"
},
{
"name": "Sara",
"phone": "+967 733 987 654",
"email": ""
}
]
قراءة جهات الاتصال عند تشغيل البرنامج
إذا لم يكن الملف موجودًا نعيد قائمة فارغة. وإذا كان فارغًا أو تالفًا، نعالج JSONDecodeError بدل توقف البرنامج. وإذا أردت فهم هذا الخطأ تحديدًا، راجع حل خطأ JSONDecodeError في بايثون.
def load_contacts():
if not DATA_FILE.exists():
return []
try:
with DATA_FILE.open("r", encoding="utf-8") as file:
contacts = json.load(file)
if not isinstance(contacts, list):
print("ملف البيانات لا يحتوي على قائمة صالحة.")
return []
return contacts
except json.JSONDecodeError:
print("ملف contacts.json فارغ أو غير صالح.")
return []
except OSError as error:
print(f"تعذر قراءة البيانات: {error}")
return []
متى نحفظ البيانات؟
سنحفظ مباشرة بعد كل إضافة أو تعديل أو حذف، حتى لا تضيع التغييرات إذا أُغلقت النافذة فجأة.
# بعد الإضافة
contacts.append(contact)
save_contacts(contacts)
# بعد التعديل
contact["phone"] = new_phone
save_contacts(contacts)
# بعد الحذف
contacts.pop(index)
save_contacts(contacts)
إنشاء القائمة الرئيسية
def show_menu():
print("\n=== دفتر جهات الاتصال ===")
print("1. إضافة جهة اتصال")
print("2. عرض جميع جهات الاتصال")
print("3. البحث عن جهة اتصال")
print("4. تعديل جهة اتصال")
print("5. حذف جهة اتصال")
print("6. خروج")
تشغيل البرنامج داخل حلقة while
def main():
contacts = load_contacts()
while True:
show_menu()
choice = input("اختر رقم العملية: ").strip()
if choice == "1":
add_contact(contacts)
elif choice == "2":
list_contacts(contacts)
elif choice == "3":
search_contacts(contacts)
elif choice == "4":
update_contact(contacts)
elif choice == "5":
delete_contact(contacts)
elif choice == "6":
print("تم إغلاق دفتر جهات الاتصال.")
break
else:
print("الخيار غير صحيح. اختر رقمًا من 1 إلى 6.")
تشغيل main بالطريقة الصحيحة
if __name__ == "__main__":
main()
الكود الكامل لمشروع جهات الاتصال ببايثون مع JSON
ضع الكود التالي كاملًا داخل ملف contact_book.py:
import json
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
DATA_FILE = BASE_DIR / "contacts.json"
def load_contacts():
if not DATA_FILE.exists():
return []
try:
with DATA_FILE.open("r", encoding="utf-8") as file:
contacts = json.load(file)
if not isinstance(contacts, list):
print("ملف البيانات لا يحتوي على قائمة صالحة.")
return []
return contacts
except json.JSONDecodeError:
print("ملف contacts.json فارغ أو غير صالح.")
return []
except OSError as error:
print(f"تعذر قراءة البيانات: {error}")
return []
def save_contacts(contacts):
try:
with DATA_FILE.open("w", encoding="utf-8") as file:
json.dump(
contacts,
file,
ensure_ascii=False,
indent=4
)
except OSError as error:
print(f"تعذر حفظ البيانات: {error}")
def is_valid_phone(phone):
cleaned_phone = phone.replace(" ", "").replace("-", "")
if cleaned_phone.startswith("+"):
cleaned_phone = cleaned_phone[1:]
return cleaned_phone.isdigit() and len(cleaned_phone) >= 7
def is_valid_email(email):
if not email:
return True
if "@" not in email:
return False
domain = email.split("@", 1)[1]
return "." in domain
def find_contact_index(contacts, name):
normalized_name = name.casefold()
for index, contact in enumerate(contacts):
if contact["name"].casefold() == normalized_name:
return index
return None
def add_contact(contacts):
name = input("أدخل الاسم: ").strip()
if not name:
print("الاسم لا يمكن أن يكون فارغًا.")
return
if find_contact_index(contacts, name) is not None:
print("توجد جهة اتصال بهذا الاسم مسبقًا.")
return
phone = input("أدخل رقم الهاتف: ").strip()
if not is_valid_phone(phone):
print("رقم الهاتف غير صالح.")
return
email = input(
"أدخل البريد الإلكتروني أو اتركه فارغًا: "
).strip()
if not is_valid_email(email):
print("البريد الإلكتروني غير صالح.")
return
contacts.append({
"name": name,
"phone": phone,
"email": email
})
save_contacts(contacts)
print("تمت إضافة جهة الاتصال بنجاح.")
def list_contacts(contacts):
if not contacts:
print("لا توجد جهات اتصال محفوظة.")
return
print("\n--- جهات الاتصال ---")
for number, contact in enumerate(contacts, start=1):
email = contact["email"] or "غير مضاف"
print(f"\n{number}. الاسم: {contact['name']}")
print(f" الهاتف: {contact['phone']}")
print(f" البريد: {email}")
def search_contacts(contacts):
query = input("أدخل الاسم أو جزءًا منه: ").strip()
if not query:
print("اكتب اسمًا للبحث.")
return
normalized_query = query.casefold()
matches = [
contact
for contact in contacts
if normalized_query in contact["name"].casefold()
]
if not matches:
print("لم يتم العثور على جهة اتصال مطابقة.")
return
list_contacts(matches)
def update_contact(contacts):
name = input("أدخل الاسم الكامل لجهة الاتصال: ").strip()
index = find_contact_index(contacts, name)
if index is None:
print("جهة الاتصال غير موجودة.")
return
contact = contacts[index]
new_phone = input(
f"رقم الهاتف الجديد [{contact['phone']}]: "
).strip()
new_email = input(
f"البريد الجديد [{contact['email'] or 'غير مضاف'}]: "
).strip()
if new_phone:
if not is_valid_phone(new_phone):
print("رقم الهاتف الجديد غير صالح.")
return
contact["phone"] = new_phone
if new_email:
if not is_valid_email(new_email):
print("البريد الإلكتروني الجديد غير صالح.")
return
contact["email"] = new_email
save_contacts(contacts)
print("تم تعديل جهة الاتصال بنجاح.")
def delete_contact(contacts):
name = input("أدخل الاسم الكامل لجهة الاتصال: ").strip()
index = find_contact_index(contacts, name)
if index is None:
print("جهة الاتصال غير موجودة.")
return
contact = contacts[index]
confirmation = input(
f"هل تريد حذف {contact['name']}؟ (y/n): "
).strip().lower()
if confirmation == "y":
contacts.pop(index)
save_contacts(contacts)
print("تم حذف جهة الاتصال.")
else:
print("تم إلغاء الحذف.")
def show_menu():
print("\n=== دفتر جهات الاتصال ===")
print("1. إضافة جهة اتصال")
print("2. عرض جميع جهات الاتصال")
print("3. البحث عن جهة اتصال")
print("4. تعديل جهة اتصال")
print("5. حذف جهة اتصال")
print("6. خروج")
def main():
contacts = load_contacts()
while True:
show_menu()
choice = input("اختر رقم العملية: ").strip()
if choice == "1":
add_contact(contacts)
elif choice == "2":
list_contacts(contacts)
elif choice == "3":
search_contacts(contacts)
elif choice == "4":
update_contact(contacts)
elif choice == "5":
delete_contact(contacts)
elif choice == "6":
print("تم إغلاق دفتر جهات الاتصال.")
break
else:
print("الخيار غير صحيح. اختر رقمًا من 1 إلى 6.")
if __name__ == "__main__":
main()
طريقة تشغيل البرنامج
افتح Terminal داخل مجلد المشروع، ثم شغّل الملف.
py contact_book.pyأو:
python contact_book.pyفي macOS أو Linux قد تستخدم:
python3 contact_book.py
مثال تشغيل
=== دفتر جهات الاتصال ===
1. إضافة جهة اتصال
2. عرض جميع جهات الاتصال
3. البحث عن جهة اتصال
4. تعديل جهة اتصال
5. حذف جهة اتصال
6. خروج
اختر رقم العملية: 1
أدخل الاسم: Ali
أدخل رقم الهاتف: +967 777 123 456
أدخل البريد الإلكتروني أو اتركه فارغًا: ali@example.com
تمت إضافة جهة الاتصال بنجاح.
بعد الإضافة سيظهر ملف contacts.json داخل مجلد المشروع. أغلق البرنامج وشغّله مرة أخرى، ثم اختر عرض جهات الاتصال؛ ستجد أن البيانات ما زالت موجودة.
كيف يعمل البرنامج خطوة بخطوة؟
- يبدأ التنفيذ من الشرط
if __name__ == "__main__". - تستدعي
main()الدالةload_contacts(). - تُقرأ البيانات القديمة من ملف JSON أو تبدأ قائمة فارغة.
- تعرض حلقة
whileالقائمة الرئيسية. - يُنفذ الخيار الذي يحدده المستخدم.
- بعد الإضافة أو التعديل أو الحذف تُحفظ القائمة مباشرة.
- تتكرر القائمة حتى يختار المستخدم الخروج.
أخطاء شائعة عند بناء دفتر جهات الاتصال
1. إعادة إنشاء القائمة داخل كل دورة
لا تكتب contacts = [] داخل حلقة القائمة؛ لأن ذلك يمسح البيانات في كل دورة. حمّل القائمة مرة واحدة قبل الحلقة.
2. نسيان الحفظ بعد التعديل
تعديل القاموس في الذاكرة لا يكفي. استدعِ save_contacts() بعد الإضافة والتعديل والحذف.
3. الخلط بين dump وdumps
json.dump() تكتب داخل ملف، بينما json.dumps() تعيد نص JSON داخل البرنامج.
4. نسيان ensure_ascii=False
استخدمه مع encoding="utf-8" حتى يظهر النص العربي طبيعيًا داخل ملف JSON.
5. محاولة قراءة ملف JSON فارغ
الملف الفارغ ليس JSON صالحًا، لذلك عالج JSONDecodeError وأعد قائمة فارغة.
6. الحذف دون تأكيد
اطلب من المستخدم تأكيدًا واضحًا قبل استخدام pop() حتى لا يحذف اسمًا بالخطأ.
7. اعتبار التحقق المبسط نهائيًا
فحص الهاتف والبريد في المثال مناسب للتعلم، لكنه ليس بديلًا عن قواعد أو مكتبات متخصصة في تطبيق حقيقي.
8. فتح ملف JSON بوضع append لإضافة جهة اتصال
لا تضف قاموسًا جديدًا مباشرة إلى نهاية contacts.json باستخدام وضع "a"؛ لأن الملف في هذا المشروع يمثل قائمة JSON واحدة. كتابة كائن JSON آخر في نهاية الملف قد تجعل المحتوى غير صالح للقراءة بـ json.load(). الطريقة الصحيحة هنا هي: حمّل القائمة، عدّلها في الذاكرة باستخدام الإضافة أو التعديل أو الحذف، ثم أعد حفظ القائمة كاملة باستخدام json.dump().
إذا أردت مشروعًا أبسط قبل هذا المستوى، جرّب أولًا إنشاء برنامج قائمة مهام بسيطة في بايثون، ثم عد إلى هذا المشروع لإضافة الحفظ الدائم وعمليات CRUD الكاملة.
تحسينات يمكنك إضافتها لاحقًا
- إضافة الشركة والعنوان والملاحظات.
- البحث باستخدام رقم الهاتف.
- ترتيب الأسماء أبجديًا قبل العرض.
- منع تكرار رقم الهاتف.
- تصدير البيانات إلى CSV.
- إنشاء نسخة احتياطية من ملف JSON.
- تقسيم البرنامج إلى أكثر من ملف.
- كتابة اختبارات باستخدام
pytest. - تحويل البرنامج إلى واجهة رسومية باستخدام
Tkinter.
تمرين عملي
أضف المفتاح category لكل جهة اتصال، واجعل المستخدم يختار بين: العائلة، العمل، الأصدقاء، وأخرى. بعد ذلك أضف خيارًا يعرض جهات الاتصال حسب الفئة.
category = input(
"أدخل الفئة: العائلة، العمل، الأصدقاء، أخرى: "
).strip()
contact = {
"name": name,
"phone": phone,
"email": email,
"category": category
}
مصادر خارجية رسمية للتوسع
- توثيق مكتبة json الرسمي في بايثون
- توثيق مكتبة pathlib الرسمي
- هياكل البيانات في توثيق بايثون الرسمي
- الأخطاء والاستثناءات في بايثون
الخلاصة
أنشأنا برنامج دفتر جهات اتصال كاملًا باستخدام بايثون. خزّنا كل شخص داخل قاموس يحتوي على الاسم والهاتف والبريد، ثم وضعنا القواميس داخل قائمة واحدة.
تعلمنا الإضافة والعرض والبحث والتعديل والحذف، وقسمنا البرنامج إلى دوال واضحة. ثم استخدمنا مكتبة json لحفظ القائمة داخل ملف، وpathlib للمسار، وtry وexcept للتعامل مع مشكلات القراءة والكتابة.
{alertSuccess} القاعدة المهمة: البيانات الموجودة داخل المتغيرات تختفي عند انتهاء البرنامج. إذا أردت الاحتفاظ بها، احفظها في ملف أو قاعدة بيانات، ثم اقرأها عند التشغيل التالي.
أسئلة شائعة
كيف أنشئ برنامج جهات اتصال ببايثون؟
خزّن كل جهة اتصال داخل قاموس، وضع القواميس داخل قائمة، ثم أنشئ دوالًا للإضافة والعرض والبحث والتعديل والحذف.
هل مشروع جهات الاتصال هذا يطبق CRUD؟
نعم. الإضافة تمثل Create، والعرض والبحث يمثلان Read، والتعديل يمثل Update، والحذف يمثل Delete. ويضيف ملف JSON حفظ البيانات بين مرات التشغيل.
لماذا استخدمنا JSON؟
لأنه مناسب للقوائم والقواميس، وسهل القراءة والكتابة باستخدام مكتبة json المدمجة مع بايثون.
هل أحتاج إلى تثبيت مكتبة json؟
لا. يكفي أن تكتب import json.
ما الفرق بين json.dump وjson.load؟
json.dump() تكتب بيانات بايثون داخل ملف، بينما json.load() تقرأ الملف وتحوله إلى بيانات بايثون.
كيف أمنع تكرار جهة الاتصال؟
ابحث عن الاسم أو رقم الهاتف قبل الإضافة. في المثال استخدمنا find_contact_index() لمنع تكرار الاسم.
هل يمكن البحث بجزء من الاسم؟
نعم، استخدم casefold() لتوحيد الحروف، ثم العامل in للتحقق من وجود النص داخل الاسم.
هل يمكن تحويل البرنامج إلى واجهة رسومية؟
نعم، يمكنك استخدام Tkinter لاحقًا مع الاحتفاظ بدوال الحفظ والقراءة نفسها تقريبًا.
هل JSON مناسب لعدد كبير من جهات الاتصال؟
هو مناسب للمشروع التعليمي والبيانات الصغيرة. عند نمو التطبيق، تكون قاعدة بيانات مثل SQLite أنسب للبحث والتعديل وإدارة البيانات.



