Python Learningاز صفر تا نوشتن پروژه از حفظ
درس ۱۵ · استادی

نوشتن از حفظ

حدود ۶۰ دقیقه۱۶ تمرین۵ سؤال آزمون
بعد از این درس می‌توانی:
  • نقشه‌ی ذهنی ۸ بخش برنامه را داشته باشی
  • تکه‌های سخت را با تمرین جای خالی و مرتب‌سازی حفظ کنی
  • با برنامه‌ی تکرار فاصله‌دار، حفظ را ماندگار کنی
  • کل برنامه را در کمتر از ۳۰ دقیقه بدون نگاه بنویسی

حفظ کردن کد با فهمیدن شروع می‌شود

کد را مثل شعر حفظ نکن. اگر بدانی چرا هر خط آنجاست، خط بعدی خودش یادت می‌آید. روش این درس چهار قدم دارد:

۱. نقشه: ترتیب بخش‌ها را بدون کد بلد باش.

۲. اسکلت: اول فقط امضای تابع‌ها و متدها را بنویس، بعد داخلشان را پر کن.

۳. یادآوری فعال: بدون نگاه بنویس، بعد مقایسه کن. خواندن دوباره حفظ نمی‌کند؛ به یاد آوردن حفظ می‌کند.

۴. تکرار فاصله‌دار: تمرین را با فاصله‌های بیشتر و بیشتر تکرار کن.

۱. نقشه‌ی برنامه

#بخشجمله‌ی یادآور
۱ورودی‌ها و FILE«چهار import و مسیر فایل کنار برنامه»
۲__init__، save، load«خالی بساز، از فایل بخوان؛ هر تغییر را بنویس»
۳add، edit، delete«چک کن، تغییر بده، ذخیره کن»
۴sell، report«سه چک، کم کن، ثبت کن؛ دو sum»
۵shop، root، فرم«شیء، پنجره، چهار برچسب و جعبه»
۶values، run، شش تابع دکمه«بخوان و تبدیل کن؛ امن اجرا کن؛ هر دکمه یک خط»
۷refresh، choose«خالی کن و پر کن؛ ردیف را به فرم ببر»
۸دکمه‌ها، جدول، mainloop«قاب و lambda، چهار ستون، bind، وزن، refresh، mainloop»
مرتب کن

ترتیب بخش‌ها

این‌ها خط‌های کلیدی هر بخش هستند. به ترتیبی که در فایل می‌آیند مرتبشان کن.

  1. FILE = os.path.join(...)
  2. root.mainloop()
  3. def run(action):
  4. def refresh(query=''):
  5. table = ttk.Treeview(...)
  6. def sell(self, name, qty):
  7. def add(self, name, buy, sell, qty):
  8. class Shop:
  9. root = tk.Tk()

۲. اسکلت

تمرین

اسکلت کلاس

فقط اسکلت را بنویس: کلاس Shop با ۸ متد (__init__، save، load، add، edit، delete، sell، report) با پارامترهای درست و بدنه‌ی pass.

Ctrl + Enter
دیدن راه‌حل (اول خودت تلاش کن)
class Shop:
    def __init__(self):
        pass

    def save(self):
        pass

    def load(self):
        pass

    def add(self, name, buy, sell, qty):
        pass

    def edit(self, name, buy, sell, qty):
        pass

    def delete(self, name):
        pass

    def sell(self, name, qty):
        pass

    def report(self):
        pass

۳. خط‌های سخت

این خط‌ها طولانی‌اند یا آرگومان‌های زیاد دارند. چند بار پرشان کن تا بدون فکر بنویسی‌شان.

جای خالی

مسیر فایل

خط ۶:

FILE = os.path.(os.path.(os.path.()), 'shop_data.json')
جای خالی

ذخیره

بدنه‌ی save:

with open(FILE, '', encoding='utf-8') as f:
    json.dump({'products': self., 'sales': self.}, f, =False, =2)
جای خالی

چک عدد

خط ۳۱:

if not (0 <= x < float('') for x in (buy, sell, qty)):
جای خالی

رکورد فروش

خط ۵۹:

self.sales.({'name': name, 'qty': qty, 'total': qty * product['']})
جای خالی

برچسب فرم

خط ۷۲:

ttk.Label(root, text=label).grid(row=, column=, padx=8, pady=5, sticky='')
جای خالی

run

بدنه‌ی run:

try:
    action()
    if action  search: refresh()
except (, ) as exc:
    messagebox.('خطا', str(exc))
except  as exc:
    messagebox.showerror('خطای فایل', str(exc))
جای خالی

پیام گزارش

خط ۱۱۱:

messagebox.('گزارش فروش', f'تعداد فروخته‌شده: {count}\nدرآمد کل فروش: {income:}')
جای خالی

ردیف جدول

خط ۱۱۸:

table.insert(, '', values=(name, p['buy'], p['sell'], p['qty']))
جای خالی

دکمه‌ها

خط ۱۳۴:

ttk.Button(frame, text=label, command=lambda : run(a)).grid(row=i  4, column=i  4, padx=4, pady=4)
جای خالی

جدول

خط ۱۳۷ و ۱۴۲:

table = ttk.(root, columns=columns, show='', height=13)
table.('<<>>', choose)

۴. مرتب کردن متدها

مرتب کن

load

متد load:

  1. try:
  2. data = json.load(f)
  3. self.products, self.sales = {}, []
  4. self.products = data.get('products', {})
  5. if os.path.exists(FILE):
  6. except (OSError, json.JSONDecodeError, AttributeError):
  7. self.sales = data.get('sales', [])
  8. def load(self):
  9. with open(FILE, 'r', encoding='utf-8') as f:
مرتب کن

sell

متد sell:

  1. def sell(self, name, qty):
  2. raise ValueError('کالا پیدا نشد')
  3. raise ValueError('موجودی کافی نیست')
  4. if qty <= 0:
  5. raise ValueError('تعداد باید مثبت باشد')
  6. self.sales.append({'name': name, 'qty': qty, 'total': qty * product['sell']})
  7. product = self.products[name]
  8. self.save()
  9. product['qty'] -= qty
  10. if qty > product['qty']:
  11. if name not in self.products:
مرتب کن

choose

تابع choose:

  1. selection = table.selection()
  2. fields[key].insert(0, value)
  3. if not selection:
  4. name, buy, sell_price, qty = table.item(selection[0], 'values')
  5. fields[key].delete(0, tk.END)
  6. def choose(event):
  7. return
  8. for key, value in [('name', name), ('buy', buy), ('sell', sell_price), ('qty', qty)]:

۵. نوشتن کامل از حفظ

تمرین

کلاس Shop از حفظ (تکرار کن)

این همان تمرین آخر درس ۱۳ است. هر بار که به این درس برمی‌گردی، کادر را با «از اول» خالی کن و دوباره بنویس.

Ctrl + Enter
دیدن راه‌حل (اول خودت تلاش کن)
import json
import os

FILE = 'shop_data.json'  # در VS Code: مسیر کنار برنامه با __file__

class Shop:
    def __init__(self):
        self.products = {}
        self.sales = []
        self.load()

    def save(self):
        with open(FILE, 'w', encoding='utf-8') as f:
            json.dump({'products': self.products, 'sales': self.sales}, f, ensure_ascii=False, indent=2)

    def load(self):
        if os.path.exists(FILE):
            try:
                with open(FILE, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                self.products = data.get('products', {})
                self.sales = data.get('sales', [])
            except (OSError, json.JSONDecodeError, AttributeError):
                self.products, self.sales = {}, []

    def add(self, name, buy, sell, qty):
        if not name or name in self.products:
            raise ValueError('نام خالی یا کالای تکراری است')
        if not all(0 <= x < float('inf') for x in (buy, sell, qty)):
            raise ValueError('عدد منفی یا نامعتبر مجاز نیست')
        self.products[name] = {'buy': buy, 'sell': sell, 'qty': qty}
        self.save()

    def edit(self, name, buy, sell, qty):
        if name not in self.products:
            raise ValueError('کالا پیدا نشد')
        if not all(0 <= x < float('inf') for x in (buy, sell, qty)):
            raise ValueError('عدد منفی یا نامعتبر مجاز نیست')
        self.products[name] = {'buy': buy, 'sell': sell, 'qty': qty}
        self.save()

    def delete(self, name):
        if name not in self.products:
            raise ValueError('کالا پیدا نشد')
        del self.products[name]
        self.save()

    def sell(self, name, qty):
        if name not in self.products:
            raise ValueError('کالا پیدا نشد')
        if qty <= 0:
            raise ValueError('تعداد باید مثبت باشد')
        product = self.products[name]
        if qty > product['qty']:
            raise ValueError('موجودی کافی نیست')
        product['qty'] -= qty
        self.sales.append({'name': name, 'qty': qty, 'total': qty * product['sell']})
        self.save()

    def report(self):
        return sum(s['qty'] for s in self.sales), sum(s['total'] for s in self.sales)

برنامه‌ی تکرار

هر روز یک بار کل برنامه را در یک فایل خالی در VS Code بنویس، اجرا کن و با چک‌لیست درس قبل تست کن. زمانت را یادداشت کن. بعد از ۵ روز، فاصله‌ها را بیشتر کن.

آزمون این درس

برای گذراندن این درس حداقل ۸۰٪ جواب درست لازم است. هر چند بار خواستی می‌توانی دوباره امتحان بدهی.

۱. بهترین روش حفظ کردن کد کدام است؟
۲. ترتیب بخش‌ها در فایل کدام است؟
۳. «اسکلت» یعنی
۴. چرا فاصله‌ی تکرارها را بیشتر می‌کنیم؟
۵. سه آرگومان بعد از دیکشنری در json.dump چیست؟