معالجة الأخطاء وتحسينها
أضف معالجة الحالات الاستثنائية والحالات النادرة إلى تطبيق الميزانية الخاص بنا.
بدأنا هذا المشروع بجمع المتطلبات من عميل افتراضي. ساعدنا نظام الذكاء الاصطناعي المساعد لدينا في إنشاء نواة وظيفية بسرعة، وقد عملت بسلاسة تامة في اختباراتنا. استقبل البرنامج الإيرادات وسجل النفقات كما أردنا تمامًا. لكن في الواقع، المستخدمون ليسوا دائمًا معصومين عن الخطأ. ماذا يحدث عندما يرتكبون خطأً؟
لنكتشف ذلك.
تحتوي الأداة أدناه على الكود الذي أنشأناه للتو. قم بتشغيله وفقًا للتعليمات. عندما ترى مطالبة " أدخل دخلك الشهري" ، اكتب " خمسمائة" بدلاً من إدخال الرقم 500.
income = 0.0
expenses = [] # Each item is a dict: {"category": str, "amount": float}
# Common categories to suggest to the user
allowed_categories = [
"Food", "Rent", "Transport", "Entertainment",
"Utilities", "Health", "Education", "Other"
]
def add_income(amount):
"""Set or update the monthly income (float > 0)."""
global income
income = float(amount)
def add_expense(category, amount):
"""Append an expense entry with category, amount (>0)."""
new_expense = {
"category": category,
"amount": float(amount)
}
expenses.append(new_expense)
def delete_expense(idx):
"""Delete an expense by zero-based"""
return expenses.pop(idx)
def total_spent(expenses_list=None):
"""Return the sum of all expense amounts from a list or the global store."""
if expenses_list is None:
expenses_list = expenses
return sum(entry["amount"] for entry in expenses_list)
def compute_category_totals(expenses_list=None):
"""Return a dict mapping category -> total amount from expenses."""
if expenses_list is None:
expenses_list = expenses
totals = {}
for entry in expenses_list:
category = entry["category"]
amount = entry["amount"]
totals[category] = totals.get(category, 0) + amount
return totals
def print_report(income_value=None, expenses_list=None):
"""Print a formatted budget report with category totals and status line."""
if income_value is None:
income_value = income
if expenses_list is None:
expenses_list = expenses
total = total_spent(expenses_list)
remaining_balance = income_value - total
category_totals = compute_category_totals(expenses_list)
print("\n--- Budget Report ---")
print(f"Income: ${income_value:.2f}")
print(f"Total Spent: ${total:.2f}")
print("\nCategory Spending:")
for category, amount in category_totals.items():
print(f" - {category}: ${amount:.2f}")
print("-" * 27)
print(f"Remaining Balance: ${remaining_balance:.2f}")
def show_menu():
"""Display simple command menu for the CLI."""
print("\n--- Budget Tracker Menu ---")
print("1. Set Monthly Income")
print("2. Add an Expense")
print("3. Delete an Expense")
print("4. Show Budget Report")
print("5. Reset All Data")
print("6. Exit")
# Helper function to list expenses
def list_expenses():
"""Display all expenses with their index."""
if not expenses:
print("No expenses recorded yet.")
return
print("\nCurrent Expenses:")
for i, expense in enumerate(expenses):
print(f"{i}: {expense['category']} - ${expense['amount']:.2f}")
def reset_data():
"""Clear income and expenses; return to a clean initial state."""
global income, expenses
income = 0.0
expenses.clear()
print("All budget data has been cleared.")
def run_cli():
"""Main loop: set income, add expenses, show report/export, reset, exit."""
global income
print("Welcome to Budget Tracker!")
income_input = input("Enter your monthly income: ")
add_income(income_input)
while True:
show_menu()
choice = input("Choose an option: ").strip()
if choice == "1":
amount = input("Enter new monthly income: ")
add_income(amount)
elif choice == "2":
# Display categories to the user
print("\nAvailable Categories:")
for i, cat in enumerate(allowed_categories):
print(f"{i}: {cat}")
# Prompt for category and amount
category_idx_str = input("Enter the number for the category: ")
category_idx = int(category_idx_str)
if 0 <= category_idx < len(allowed_categories):
category = allowed_categories[category_idx]
amount = input(f"Enter expense amount for {category}: ")
add_expense(category, amount)
print("Expense added.")
else:
print("Invalid category number. Please try again.")
elif choice == "3":
list_expenses() # Display list before prompting for deletion
if not expenses:
continue
idx_str = input("Enter the index of the expense to delete: ")
idx = int(idx_str)
if 0 <= idx < len(expenses):
deleted_expense = delete_expense(idx)
print(f"Deleted expense: {deleted_expense['category']} - ${deleted_expense['amount']:.2f}")
else:
print("Invalid index. No expense deleted.")
elif choice == "4":
print_report()
elif choice == "5":
reset_data()
elif choice == "6":
print("Goodbye!")
break
else:
print("Invalid choice. Please try again.")
if __name__ == "__main__":
run_cli()تعطل التطبيق، وامتلأت نافذة الأوامر بنصوص حمراء:ValueError: could not convert string to float: 'five hundred' .
ما الخطأ الذي حدث؟
كان الكود المُولّد بواسطة الذكاء الاصطناعي يتوقع قيمة رقمية. حاول تحويل النص الخاص بيfive hundred إلى رقم باستخدامfloat() الدالة. ولأن النص لم يكن رقميًا، فقد أثار البرنامج خطأ ValueError وتوقف عن العمل.
كان هدف الذكاء الاصطناعي هو توليد حلٍّ بناءً على السيناريو الأكثر شيوعًا ومثالية. يفترض هذا النوع من البرمجة أن المستخدم إدخال دائمًا رقمًا صحيحًا، لأن هذا ما يُفترض في تطبيقات تتبع الميزانية . يُعدّ هذا النوع من البرمجة ممتازًا لإنشاء نموذج أولي سريع، ولكنه هشّ، إذ يفتقر إلى طبقات الحماية التي تجعل التطبيق الاحترافي متينًا.
هذا مثال كلاسيكي على حالة استثنائية، وهي حالة لا تُعدّ استخدامًا نموذجيًا ولكنها قد تحدث بسهولة. في هذه الحالة، كانت الحالة الاستثنائية مجرد خطأ إملائي بسيط من المستخدم. نحتاج أيضًا إلى معالجة الاستثناءات، أي الأخطاء المحددة، مثلValueError ، وهو Python يثيره بايثون عندما لا يستطيع تنفيذ عملية ما.
الحل: بناء مشروع محصن ضد الرصاص
لحل هذه المشكلة، نحتاج إلى التخطيط للأخطاء المحتملة وجعل البرنامج يتعامل معها بسلاسة. وهذا يعني إضافة عمليات فحص للحالات الشاذة ومعالجة الاستثناءات. إحدى الطرق هي استخدامtry-except كتلة تستمع للأخطاء مثلValueError ويمنع البرنامج من التعطل. فبدلاً من السماح للتطبيق بالتعطل، فإنexcept ستكتشف الكتلة الخطأ، وتطبع رسالة توضيحية مثل: هذا رقم غير صالح. يرجى المحاولة مرة أخرى، وتسمح للبرنامج بمواصلة التشغيل.
لقد أنشأتَ تطبيقًا فعالًا لتتبع الميزانية، ولكن بصفتك مطورًا جيدًا، فإن خطوتك التالية هي جعله أكثر مرونة. هدفنا هو جعل التطبيق محصنًا ضد أي إدخال غير متوقعة أو خبيثة من المستخدم. التطبيق الرائع لا يعمل فقط عندما تسير الأمور على ما يرام، بل يتعامل مع الأخطاء بسلاسة عند حدوثها.
ما سنقوم بإصلاحه
في هذا الدرس، سنقوم بتطبيق معالجة قوية للأخطاء لمعالجة المشكلات الشائعة:
تحليل الأرقام: سنستخدم كتل try-except لالتقاط الأخطاء
ValueErrorعندما يُدخل المستخدم نصًا بدلاً من رقم، مما يمنع التطبيق من التعطل.التحقق: سنضيف عمليات تحقق للتأكد من أن مبالغ الدخل والمصروفات هي أرقام موجبة أكبر من الصفر.
التصنيفات: سنتعامل مع إدخال غير الحساسة لحالة الأحرف للتصنيفات وسنوجه المستخدم نحو الخيارات الصحيحة.
حذف المصاريف: سنقوم بالتحقق من صحة إدخال المستخدم للتأكد من أنه عدد صحيح صالح يتوافق مع فهرس المصاريف الحالي.
واجهة المستخدم: سنقوم بتحسين CLI لتوفير رسائل واضحة وغنية بالمعلومات لكل إجراء، سواء كان ناجحًا أم فاشلاً.
لنبدأ بتحسين الوظائف الأساسية للتطبيق لجعلها أكثر مرونة.
نبضات القلب خالية من الأخطاء
سنبدأ بالوظائف الأكثر أهمية:add_income() وadd_expense() هذه هي العناصر الأساسية لتطبيقنا، لذا فإن جعلها مرنة هو أولويتنا القصوى. كانت الدوال الأصلية تفترض أن المستخدم سيدخل دائمًا رقمًا صحيحًا. سنضيف الآن كتل try-except للتعامل مع إدخال غير الصحيحة.
income = 0.0
# A list of expense dictionaries, e.g. [{"category": str, "amount": float}, ...]
expenses = []
allowed_categories = {
"Food", "Rent", "Transport", "Entertainment",
"Utilities", "Health", "Education", "Other"
}
_allowed_map = {c.lower(): c for c in allowed_categories}
def add_income(amount):
"""Set or update the monthly income (must be a positive number). Return True/False."""
global income
try:
val = float(amount)
except (TypeError, ValueError):
print("Invalid income. Please enter a number like 1200 or 1200.50.")
return False
if val <= 0.0:
print("Income must be greater than 0.")
return False
income = val
print("Income set to ${0:.2f}".format(income))
return True
def add_expense(category, amount):
"""Record an expense if category is allowed and amount > 0. Return True/False."""
global expenses
if category is None:
print("Please provide a category.")
return False
key = str(category).strip().lower()
if key not in _allowed_map:
print("Invalid category. Choose from: " + ", ".join(allowed_categories))
return False
canon_cat = _allowed_map[key]
try:
val = float(amount)
except (TypeError, ValueError):
print("Invalid amount. Please enter a number like 12 or 12.50.")
return False
if val <= 0.0:
print("Amount must be greater than 0.")
return False
entry = {"category": canon_cat, "amount": val}
expenses.append(entry)
print("Expense added: {0} - ${1:.2f}".format(canon_cat, val))
return True
def run_cli():
while True:
print("Welcome to Budget Tracker — Capstone Project")
print("1. Add income")
print("2. Add expense")
print("3. Exit")
choice = input("Choose an option (1-3): ").strip() # Trim any extra spaces
if choice == "1":
amount = input("Enter your monthly income: $")
add_income(amount)
elif choice == "2":
print("\nNow, let's add an expense.")
category = input("Enter the expense category (" + ", ".join(allowed_categories) + "): ")
amount = input("Enter the expense amount: $")
add_expense(category, amount)
elif choice == "3":
print("Exiting Budget Tracker.")
break
else:
print("Invalid choice. Please select a valid option.")
if __name__ == "__main__":
run_cli()في الكود أعلاه:
_allowed_map:ال
_allowed_mapهو جدول بحث يضمن تخزين جميع الفئات بشكل متسق. يقوم بتحويل إدخال المستخدم مثل "طعام" أو "FoOd" إلى "طعام" موحد، مما يمنع التكرارات ويضمن دقة التقارير.
أضف وظيفة الدخل:
تستخدم هذه الدالة الآن كتلة try-except لمنع تعطل التطبيق في حال إدخال المستخدم قيمة غير رقمية (مثل abc). كما تتحقق من أن مبلغ الدخل عدد موجب. إذا فشل إدخال في أي من الفحصين، تعرض الدالة رسالة خطأ واضحة ولا تُغير دخل المستخدم، بل تُعيد القيمة.
Falseللإشارة إلى الفشل.
إضافة وظيفة المصروفات:
تقوم هذه الوظيفة بإجراء فحصين حاسمين قبل إضافة مصروف جديد:
التحقق من صحة الفئة : يتحقق من إدخال المستخدم مقابل
_allowed_mapإذا لم يتم التعرف على الفئة، فإن الوظيفة ترفضها وتسرد الخيارات المتاحة، مما يمنع إضافة بيانات غير صالحة.التحقق من صحة المبلغ : مشابه لـ
add_income()، فهو يستخدم كتلة try-except لالتقاط إدخال غير الرقمية والتأكد من أن المبلغ هو رقم موجب.
لا تتم إضافة المصروفات إلى أداة التتبع بنجاح إلا عند التحقق من صحة كل من الفئة والمبلغ، مما يضمن بيانات نظيفة وموثوقة.
لماذا يُفيد هذا؟: كلا الدالتين لا تتعطلان الآن عند إدخال خاطئة؛ بل تُعيدان قيمة صحيحة.
Falseوالحفاظ على الحالة دون تغيير.
شحن القطع المتبقية
الآن، وبعد أن وضعنا الأساس، حان دورك بناء ما تبقى. يمكنك استخدام برنامج AI Copilot لإنشاء هيكل أساسي لبقية عملية التنفيذ.
delete_expensetotal_spentcompute_category_totalsprint_reportshow_menureset_datarun_cli
اختبر الكود الخاص بك
الخطوة التالية هي اختبار الكود الذي قمت بإنشائه. أضفه إلى الأداة أدناه وشاهد كيف يعمل.
income = 0.0
# A list of expense dictionaries, e.g. [{"category": str, "amount": float}, ...]
expenses = []
allowed_categories = {
"Food", "Rent", "Transport", "Entertainment",
"Utilities", "Health", "Education", "Other"
}
_allowed_map = {c.lower(): c for c in allowed_categories}
def add_income(amount):
"""Set or update the monthly income (must be a positive number). Return True/False."""
global income
try:
val = float(amount)
except (TypeError, ValueError):
print("Invalid income. Please enter a number like 1200 or 1200.50.")
return False
if val <= 0.0:
print("Income must be greater than 0.")
return False
income = val
print("Income set to ${0:.2f}".format(income))
return True
def add_expense(category, amount):
"""Record an expense if category is allowed and amount > 0. Return True/False."""
global expenses
if category is None:
print("Please provide a category.")
return False
key = str(category).strip().lower()
if key not in _allowed_map:
print("Invalid category. Choose from: " + ", ".join(allowed_categories))
return False
canon_cat = _allowed_map[key]
try:
val = float(amount)
except (TypeError, ValueError):
print("Invalid amount. Please enter a number like 12 or 12.50.")
return False
if val <= 0.0:
print("Amount must be greater than 0.")
return False
entry = {"category": canon_cat, "amount": val}
expenses.append(entry)
print("Expense added: {0} - ${1:.2f}".format(canon_cat, val))
return True
# Add your code here
def run_cli():
while True:
print("Welcome to Budget Tracker — Capstone Project")
print("1. Add income")
print("2. Add expense")
print("3. Exit")
choice = input("Choose an option (1-3): ").strip() # Trim any extra spaces
if choice == "1":
amount = input("Enter your monthly income: $")
add_income(amount)
elif choice == "2":
print("\nNow, let's add an expense.")
category = input("Enter the expense category (" + ", ".join(allowed_categories) + "): ")
amount = input("Enter the expense amount: $")
add_expense(category, amount)
elif choice == "3":
print("Exiting Budget Tracker.")
break
else:
print("Invalid choice. Please select a valid option.")
if __name__ == "__main__":
run_cli()إذا واجهتك مشكلة، فانقر فوق زر"إظهار الحل الكامل".
لقد نجحت في تحويل نموذج أولي بسيط إلى تطبيق قوي يعمل عبر سطر الأوامر. يتعامل تطبيق "متتبع الميزانية" الآن بسلاسة مع إدخال المستخدم غير المتوقعة، مما يوفر تجربة مستخدم مستقرة وموثوقة.