۱۰
مدیریت خطا و Exception ⚠️
یاد میگیریم چطور خطاها رو مدیریت کنیم و برنامههای ضدضربه بنویسیم!
چرا مدیریت خطا مهمه؟
تو برنامهنویسی، خطاها اجتنابناپذیرن. بیا ببینیم چه خطاهایی ممکنه رخ بده:
انواع خطاهای رایج
# خطای تقسیم بر صفر
result = 10 / 0 # ZeroDivisionError
# خطای دسترسی به ایندکس نامعتبر
my_list = [1, 2, 3]
print(my_list[10]) # IndexError
# خطای تبدیل نوع داده
number = int("سلام") # ValueError
# خطای دسترسی به کلید ناموجود
my_dict = {"نام": "علی"}
print(my_dict["سن"]) # KeyError
# خطای باز کردن فایل ناموجود
with open("فایل_ناموجود.txt", "r") as file: # FileNotFoundError
content = file.read()
# خطای تعریف نشده متغیر
print(undefined_variable) # NameError
🚨 بدون مدیریت خطا، برنامه کرش میکنه و کاربر تجربه بدی داره!
try-except: نجاتدهنده برنامهها
با try-except میتونیم خطاها رو بگیریم و مدیریت کنیم:
ساختار پایه try-except
# مثال ساده
try:
number = int(input("یک عدد وارد کنید: "))
result = 100 / number
print(f"نتیجه: {result}")
except ValueError:
print("❌ لطفاً یک عدد معتبر وارد کنید!")
except ZeroDivisionError:
print("❌ نمیتوان بر صفر تقسیم کرد!")
print("برنامه ادامه پیدا کرد! ✅")
# گرفتن چند نوع خطا با یک except
try:
# کدی که ممکنه خطا بده
data = eval(input("عبارت ریاضی وارد کنید: "))
print(f"نتیجه: {data}")
except (ValueError, SyntaxError, NameError) as error:
print(f"خطا در ورودی: {error}")
# گرفتن همه خطاها
try:
risky_operation()
except Exception as e:
print(f"خطای غیرمنتظره: {e}")
💡 نکته: همیشه خطاهای خاص رو قبل از Exception عمومی بنویس!
else و finally: کنترل کامل جریان
else و finally کنترل بیشتری روی جریان برنامه میدن:
try-except-else-finally
def safe_divide(a, b):
try:
result = a / b
except ZeroDivisionError:
print("❌ تقسیم بر صفر امکانپذیر نیست!")
return None
except TypeError:
print("❌ نوع داده نامعتبر!")
return None
else:
# فقط اگر خطایی رخ نداده باشد اجرا میشود
print("✅ تقسیم با موفقیت انجام شد")
return result
finally:
# همیشه اجرا میشود (خطا یا بدون خطا)
print("🔄 عملیات تقسیم تمام شد")
# تست تابع
print("=== تست ۱: تقسیم عادی ===")
result1 = safe_divide(10, 2)
print(f"نتیجه: {result1}")
print("\n=== تست ۲: تقسیم بر صفر ===")
result2 = safe_divide(10, 0)
print(f"نتیجه: {result2}")
print("\n=== تست ۳: نوع داده اشتباه ===")
result3 = safe_divide("10", "abc")
print(f"نتیجه: {result3}")
📝 یادداشت:
- else: فقط اگر خطایی رخ نداده باشد اجرا میشود
- finally: همیشه اجرا میشود (برای تمیزکاری منابع)
ایجاد Exception های سفارشی
میتونیم خطاهای سفارشی خودمون رو تعریف کنیم:
Exception های سفارشی
# تعریف Exception سفارشی
class AgeError(Exception):
"""خطای سن نامعتبر"""
def __init__(self, age, message="سن وارد شده نامعتبر است"):
self.age = age
self.message = message
super().__init__(self.message)
class PasswordError(Exception):
"""خطای رمز عبور ضعیف"""
pass
# استفاده از Exception های سفارشی
def validate_user(name, age, password):
# بررسی سن
if age < 0 or age > 150:
raise AgeError(age, f"سن {age} غیرمنطقی است!")
# بررسی رمز عبور
if len(password) < 8:
raise PasswordError("رمز عبور باید حداقل ۸ کاراکتر باشد!")
if not any(c.isdigit() for c in password):
raise PasswordError("رمز عبور باید حداقل یک عدد داشته باشد!")
return f"کاربر {name} با موفقیت ثبت شد! ✅"
# تست تابع
users_data = [
("علی", 25, "mypassword123"),
("سارا", -5, "weakpass"),
("محمد", 30, "short"),
("فاطمه", 200, "verylongpassword123")
]
for name, age, password in users_data:
try:
result = validate_user(name, age, password)
print(result)
except AgeError as e:
print(f"❌ خطای سن برای {name}: {e}")
except PasswordError as e:
print(f"❌ خطای رمز عبور برای {name}: {e}")
except Exception as e:
print(f"❌ خطای غیرمنتظره: {e}")
مثال کاربردی: مدیریت فایل با خطایابی
بیا یه برنامه کاربردی برای مدیریت فایل بنویسیم:
مدیر فایل هوشمند
import json
import os
from datetime import datetime
class FileManager:
def __init__(self):
self.log_file = "file_operations.log"
def log_operation(self, operation, status, details=""):
"""ثبت عملیات در فایل لاگ"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] {operation}: {status} - {details}\n"
try:
with open(self.log_file, "a", encoding="utf-8") as log:
log.write(log_entry)
except Exception as e:
print(f"خطا در ثبت لاگ: {e}")
def read_json_file(self, filename):
"""خواندن فایل JSON با مدیریت خطا"""
try:
# بررسی وجود فایل
if not os.path.exists(filename):
raise FileNotFoundError(f"فایل {filename} یافت نشد")
# بررسی اندازه فایل
file_size = os.path.getsize(filename)
if file_size == 0:
raise ValueError("فایل خالی است")
# خواندن فایل
with open(filename, "r", encoding="utf-8") as file:
data = json.load(file)
self.log_operation("READ", "SUCCESS", f"فایل {filename} خوانده شد")
return data
except FileNotFoundError as e:
self.log_operation("READ", "ERROR", str(e))
print(f"❌ {e}")
return None
except json.JSONDecodeError as e:
self.log_operation("READ", "ERROR", f"فرمت JSON نامعتبر: {e}")
print(f"❌ فرمت فایل نامعتبر: {e}")
return None
except ValueError as e:
self.log_operation("READ", "ERROR", str(e))
print(f"❌ {e}")
return None
except Exception as e:
self.log_operation("READ", "ERROR", f"خطای غیرمنتظره: {e}")
print(f"❌ خطای غیرمنتظره: {e}")
return None
def write_json_file(self, filename, data):
"""نوشتن فایل JSON با مدیریت خطا"""
try:
# بررسی نوع داده
if not isinstance(data, (dict, list)):
raise TypeError("داده باید از نوع dict یا list باشد")
# ایجاد backup اگر فایل وجود داشته باشد
if os.path.exists(filename):
backup_name = f"{filename}.backup"
os.rename(filename, backup_name)
print(f"📁 فایل قبلی به {backup_name} منتقل شد")
# نوشتن فایل جدید
with open(filename, "w", encoding="utf-8") as file:
json.dump(data, file, ensure_ascii=False, indent=2)
self.log_operation("WRITE", "SUCCESS", f"فایل {filename} نوشته شد")
print(f"✅ فایل {filename} با موفقیت ذخیره شد")
return True
except TypeError as e:
self.log_operation("WRITE", "ERROR", str(e))
print(f"❌ {e}")
return False
except PermissionError:
self.log_operation("WRITE", "ERROR", "عدم دسترسی برای نوشتن")
print("❌ دسترسی برای نوشتن فایل وجود ندارد")
return False
except Exception as e:
self.log_operation("WRITE", "ERROR", f"خطای غیرمنتظره: {e}")
print(f"❌ خطای غیرمنتظره: {e}")
return False
# تست FileManager
fm = FileManager()
# تست نوشتن
test_data = {
"نام": "علی احمدی",
"سن": 28,
"شهر": "تهران",
"مهارتها": ["پایتون", "جاوااسکریپت", "React"]
}
fm.write_json_file("user_data.json", test_data)
# تست خواندن
loaded_data = fm.read_json_file("user_data.json")
if loaded_data:
print(f"داده خوانده شده: {loaded_data}")
# تست خواندن فایل ناموجود
fm.read_json_file("nonexistent.json")
تمرین عملی: ماشین حساب ضدضربه
یک ماشین حساب بنویس که تمام خطاهای ممکن رو مدیریت کنه:
- ورودیهای نامعتبر (غیرعددی)
- تقسیم بر صفر
- عملگرهای نامعتبر
- محدوده عددی (اعداد خیلی بزرگ)
ماشین حساب ضدضربه
class SafeCalculator:
def __init__(self):
self.max_number = 10**10 # حداکثر عدد مجاز
def validate_number(self, num_str):
"""اعتبارسنجی عدد ورودی"""
try:
num = float(num_str)
if abs(num) > self.max_number:
raise OverflowError(f"عدد بیش از حد مجاز ({self.max_number})")
return num
except ValueError:
raise ValueError(f"'{num_str}' یک عدد معتبر نیست")
def calculate(self, num1_str, operator, num2_str):
"""انجام محاسبه با مدیریت خطا"""
try:
# اعتبارسنجی اعداد
num1 = self.validate_number(num1_str)
num2 = self.validate_number(num2_str)
# انجام عملیات
if operator == "+":
result = num1 + num2
elif operator == "-":
result = num1 - num2
elif operator == "*":
result = num1 * num2
elif operator == "/":
if num2 == 0:
raise ZeroDivisionError("تقسیم بر صفر امکانپذیر نیست")
result = num1 / num2
elif operator == "**":
if num1 == 0 and num2 < 0:
raise ValueError("صفر به توان منفی تعریف نشده")
result = num1 ** num2
else:
raise ValueError(f"عملگر '{operator}' پشتیبانی نمیشود")
# بررسی محدوده نتیجه
if abs(result) > self.max_number:
raise OverflowError("نتیجه بیش از حد مجاز")
return result
except (ValueError, ZeroDivisionError, OverflowError) as e:
return f"❌ خطا: {e}"
except Exception as e:
return f"❌ خطای غیرمنتظره: {e}"
# تست ماشین حساب
calc = SafeCalculator()
test_cases = [
("10", "+", "5"),
("20", "/", "0"),
("abc", "*", "5"),
("2", "**", "100"),
("10", "&", "5"),
("0", "**", "-1")
]
for num1, op, num2 in test_cases:
result = calc.calculate(num1, op, num2)
print(f"{num1} {op} {num2} = {result}")
چالش پیشرفته: سیستم لاگین امن
یک سیستم لاگین بنویس که:
- تلاشهای ناموفق رو محدود کنه
- کاربر رو بعد از ۳ تلاش اشتباه قفل کنه
- تمام فعالیتها رو لاگ کنه
- خطاهای مختلف رو مدیریت کنه
سیستم لاگین امن
import hashlib
import json
from datetime import datetime, timedelta
class SecureLoginSystem:
def __init__(self):
self.users_file = "users.json"
self.log_file = "login_log.txt"
self.max_attempts = 3
self.lockout_duration = 30 # دقیقه
self.load_users()
def hash_password(self, password):
"""هش کردن رمز عبور"""
return hashlib.sha256(password.encode()).hexdigest()
def log_activity(self, username, activity, status):
"""ثبت فعالیت در لاگ"""
try:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] {username}: {activity} - {status}\n"
with open(self.log_file, "a", encoding="utf-8") as log:
log.write(log_entry)
except Exception as e:
print(f"خطا در ثبت لاگ: {e}")
def load_users(self):
"""بارگذاری کاربران از فایل"""
try:
with open(self.users_file, "r", encoding="utf-8") as file:
self.users = json.load(file)
except FileNotFoundError:
self.users = {}
self.save_users()
except json.JSONDecodeError:
print("❌ فایل کاربران خراب است")
self.users = {}
def save_users(self):
"""ذخیره کاربران در فایل"""
try:
with open(self.users_file, "w", encoding="utf-8") as file:
json.dump(self.users, file, ensure_ascii=False, indent=2)
except Exception as e:
print(f"خطا در ذخیره کاربران: {e}")
def register_user(self, username, password):
"""ثبتنام کاربر جدید"""
try:
if not username or not password:
raise ValueError("نام کاربری و رمز عبور نمیتواند خالی باشد")
if len(password) < 6:
raise ValueError("رمز عبور باید حداقل ۶ کاراکتر باشد")
if username in self.users:
raise ValueError("این نام کاربری قبلاً ثبت شده")
self.users[username] = {
"password": self.hash_password(password),
"failed_attempts": 0,
"locked_until": None,
"created_at": datetime.now().isoformat()
}
self.save_users()
self.log_activity(username, "REGISTER", "SUCCESS")
return True, "کاربر با موفقیت ثبت شد"
except ValueError as e:
self.log_activity(username, "REGISTER", f"FAILED: {e}")
return False, str(e)
except Exception as e:
self.log_activity(username, "REGISTER", f"ERROR: {e}")
return False, f"خطای غیرمنتظره: {e}"
def is_user_locked(self, username):
"""بررسی قفل بودن کاربر"""
if username not in self.users:
return False
locked_until = self.users[username].get("locked_until")
if not locked_until:
return False
unlock_time = datetime.fromisoformat(locked_until)
if datetime.now() > unlock_time:
# زمان قفل تمام شده
self.users[username]["locked_until"] = None
self.users[username]["failed_attempts"] = 0
self.save_users()
return False
return True
def login(self, username, password):
"""ورود کاربر"""
try:
if not username or not password:
raise ValueError("نام کاربری و رمز عبور الزامی است")
if username not in self.users:
raise ValueError("نام کاربری یافت نشد")
# بررسی قفل بودن
if self.is_user_locked(username):
locked_until = self.users[username]["locked_until"]
unlock_time = datetime.fromisoformat(locked_until)
remaining = unlock_time - datetime.now()
minutes = int(remaining.total_seconds() / 60)
raise ValueError(f"حساب قفل است. {minutes} دقیقه صبر کنید")
# بررسی رمز عبور
hashed_password = self.hash_password(password)
if self.users[username]["password"] != hashed_password:
# افزایش تعداد تلاشهای ناموفق
self.users[username]["failed_attempts"] += 1
if self.users[username]["failed_attempts"] >= self.max_attempts:
# قفل کردن حساب
lock_until = datetime.now() + timedelta(minutes=self.lockout_duration)
self.users[username]["locked_until"] = lock_until.isoformat()
self.save_users()
self.log_activity(username, "LOGIN", "LOCKED")
raise ValueError(f"حساب به دلیل {self.max_attempts} تلاش ناموفق قفل شد")
self.save_users()
attempts_left = self.max_attempts - self.users[username]["failed_attempts"]
self.log_activity(username, "LOGIN", "FAILED")
raise ValueError(f"رمز عبور اشتباه. {attempts_left} تلاش باقی مانده")
# ورود موفق
self.users[username]["failed_attempts"] = 0
self.users[username]["last_login"] = datetime.now().isoformat()
self.save_users()
self.log_activity(username, "LOGIN", "SUCCESS")
return True, "ورود موفقیتآمیز"
except ValueError as e:
return False, str(e)
except Exception as e:
self.log_activity(username, "LOGIN", f"ERROR: {e}")
return False, f"خطای غیرمنتظره: {e}"
# تست سیستم لاگین
auth = SecureLoginSystem()
# ثبتنام
print("=== ثبتنام ===")
success, msg = auth.register_user("ali123", "mypassword")
print(f"ثبتنام: {msg}")
# تلاشهای ورود
print("\n=== تلاشهای ورود ===")
test_logins = [
("ali123", "wrongpass"),
("ali123", "wrongpass"),
("ali123", "wrongpass"),
("ali123", "mypassword")
]
for username, password in test_logins:
success, msg = auth.login(username, password)
print(f"ورود {username}: {msg}")