باگگیری
- یک روش قدمبهقدم برای پیدا کردن باگ داشته باشی
- با
printوreprوtypeببینی داخل برنامه چه میگذرد - باگهای واقعی همین پروژه را بشناسی و درستشان کنی
- فرق «خطا» (برنامه میایستد) و «باگ منطقی» (اشتباه کار میکند) را بدانی
روش ششقدمی
- بازسازی: دقیق بنویس با چه کاری باگ دیده میشود. اگر نمیتوانی تکرارش کنی، نمیتوانی درستش کنی.
- پیام خطا را بخوان: آخرین خط (نوع و دلیل) و شمارهی خطی که به کد خودت اشاره میکند.
- حدس بزن: کدام متغیر احتمالاً مقدار اشتباه دارد؟
- نگاه کن: با
print(repr(x), type(x))درست قبل از خط مشکوک مقدار واقعی را ببین. - یک تغییر: فقط یک چیز را عوض کن و دوباره امتحان کن.
- تست: بعد از درست شدن، حالتهای دیگر را هم امتحان کن که چیز دیگری خراب نشده باشد.
خودت امتحان کن
اردک پلاستیکی: کد را خط به خط برای یک نفر (یا یک اردک!) توضیح بده. خیلی وقتها وسط توضیح خودت باگ را پیدا میکنی.
شکار باگ
هر کادر زیر یک باگ دارد. اول اجرا کن و ببین تست چه میگوید، بعد درستش کن. همهی اینها باگهاییاند که در برنامههای واقعی tkinter زیاد دیده میشوند.
باگ ۱: جمع متن
قیمت کل باید ۷۵۰۰ باشد، ولی چیز عجیبی درمیآید.
راهنمایی
print(repr(price)) را امتحان کن. '2500' * 3 متن را سه بار تکرار میکند؛ ولی '2500' * '3' اصلاً خطا میدهد.
دیدن راهحل (اول خودت تلاش کن)
price = '2500' qty = '3' total = float(price) * int(qty)
باگ ۲: همهی دکمهها گزارش میدهند
هر «دکمه» باید اسم کار خودش را برگرداند، ولی همه report برمیگردانند.
راهنمایی
lambda مقدار action را هنگام صدا زدن میخواند. با پارامتر پیشفرض مقدار همان لحظه را نگه دار.
دیدن راهحل (اول خودت تلاش کن)
actions = ['add', 'edit', 'report'] commands = [] for action in actions: commands.append(lambda a=action: a) results = [c() for c in commands]
باگ ۳: nan رد نمیشود
تابع باید nan را رد کند، ولی نمیکند.
دیدن راهحل (اول خودت تلاش کن)
def valid(buy, sell, qty): return all(0 <= x < float('inf') for x in (buy, sell, qty))
باگ ۴: موجودی کم نمیشود
بعد از فروش، موجودی در products باید ۱ شود ولی ۳ میماند.
راهنمایی
dict(...) یک کپی میسازد. ارجاع به همان دیکشنری لازم است.
دیدن راهحل (اول خودت تلاش کن)
products = {'pen': {'qty': 3}} product = products['pen'] product['qty'] -= 2
باگ ۵: کرش با فایل قدیمی
فایلهای قدیمی کلید sales ندارند و برنامه با KeyError بسته میشود.
دیدن راهحل (اول خودت تلاش کن)
data = {'products': {}} products = data.get('products', {}) sales = data.get('sales', [])
باگ ۶: جستوجو اثر ندارد
این شبیهسازی run است. بعد از جستوجوی pen جدول باید فقط pen داشته باشد، ولی همه را دارد.
دیدن راهحل (اول خودت تلاش کن)
products = ['pen', 'cup', 'pencil'] table = [] def refresh(query=''): table.clear() table.extend(p for p in products if query in p) def search(): refresh('pen') def run(action): action() if action is not search: refresh() run(search)
باگ ۷: None بهجای جعبه
این شبیهسازی Entry است. entry.get() خطای AttributeError: 'NoneType'... میدهد.
دیدن راهحل (اول خودت تلاش کن)
class Entry: def __init__(self): self.text = 'pen' def grid(self): return None def get(self): return self.text entry = Entry() entry.grid() name = entry.get()
باگ ۸: فروش صفر عدد
فروش صفر عدد نباید مجاز باشد، ولی هست.
دیدن راهحل (اول خودت تلاش کن)
def check_sell(qty, stock): if qty <= 0: raise ValueError('تعداد باید مثبت باشد') if qty > stock: raise ValueError('موجودی کافی نیست') return True
باگهای واقعی همین پروژه
| باگ | نشانه | درمان | خط |
|---|---|---|---|
| جستوجو بیاثر | دکمهی جستوجو همهی کالاها را نشان میداد | if action is not search: refresh() | خط ۸۷ پروژه |
nan و inf | قیمت nan ثبت و NaN در JSON ذخیره میشد | all(0 <= x < float('inf') ...) | خط ۳۱ پروژه |
| فایل غیر دیکشنری | برنامه با AttributeError باز نمیشد | گرفتن AttributeError | خط ۲۵ پروژه |
| مسیر نسبی | اجرا از پوشهی دیگر = دادهی خالی | مسیر از __file__ | خط ۶ پروژه |
بعد از هر باگی که پیدا میکنی، یک تست کوچک برایش بنویس (مثل تمرینهای این سایت) تا دوباره برنگردد.
آزمون این درس
برای گذراندن این درس حداقل ۸۰٪ جواب درست لازم است. هر چند بار خواستی میتوانی دوباره امتحان بدهی.