فصل ۷: ماژول‌ها، کتابخانه‌ی استاندارد و شیءگرایی

import، __name__ == "__main__" و ساخت پکیج

از یک فایل بلند به پروژه‌ی مرتب

هر فایل .py یک ماژول است و هر پوشه‌ای از ماژول‌ها (معمولاً با فایل __init__.py) یک پکیج. وقتی برنامه از چند صد خط گذشت، آن را بر اساس مسئولیت به ماژول‌ها تقسیم کنید: مدل داده، ذخیره‌سازی، گزارش، رابط کاربری.

شکل‌های import

import json                          # کل ماژول؛ استفاده: json.dumps
from pathlib import Path             # فقط یک نام
from collections import Counter, defaultdict
import datetime as dt                # نام مستعار
from decimal import Decimal as D     # کوتاه، اما با احتیاط

from module import * را ننویسید: معلوم نیست چه نام‌هایی وارد شده و ممکن است نام‌های شما را بی‌صدا بازنویسی کند. ترتیب استاندارد import ها در بالای فایل: اول کتابخانه‌ی استاندارد، بعد کتابخانه‌های نصب‌شده، بعد ماژول‌های خود پروژه؛ هر گروه با یک خط خالی جدا. Ruff این ترتیب را خودکار مرتب می‌کند.

ساختار یک پکیج

carpet_orders/            ← ریشه‌ی پروژه (از این‌جا اجرا می‌کنید)
├── .venv/
├── requirements.txt
└── orders/               ← پکیج
    ├── __init__.py
    ├── models.py
    ├── storage.py
    ├── reports.py
    └── cli.py
# orders/storage.py
from .models import Order            # import نسبی: «از همین پکیج»

# orders/cli.py
from orders.storage import load_orders   # import مطلق؛ همیشه واضح‌تر

__name__ == "__main__"

هر ماژول متغیری به نام __name__ دارد. وقتی فایل مستقیم اجرا شود مقدارش "__main__" است و وقتی import شود، نام خود ماژول. این الگو اجازه می‌دهد یک فایل هم قابل import باشد و هم قابل اجرا:

# orders/reports.py
def monthly_total(orders):
    return sum(o["price"] for o in orders)

def main():
    sample = [{"price": 57_000_000}, {"price": 98_000_000}]
    print(f"{monthly_total(sample):,}")

if __name__ == "__main__":
    main()          # فقط هنگام اجرای مستقیم، نه هنگام import

بدون این شرط، هر بار که فایل دیگری from orders.reports import monthly_total بنویسد، کد نمونه هم اجرا می‌شود. تست‌ها (فصل ۸) هم دقیقاً به همین دلیل به این الگو نیاز دارند.

اجرای ماژول داخل پکیج

cd D:\projects\carpet_orders
python -m orders.cli

با -m پایتون پوشه‌ی جاری را در مسیر جست‌وجو قرار می‌دهد و import های پکیج درست کار می‌کنند. اگر به‌جایش python orders\cli.py بزنید، import نسبی خطای «attempted relative import with no known parent package» می‌دهد.

نکته‌هایی که کمتر کسی می‌داند

  • هر ماژول فقط یک بار در هر اجرا بارگذاری می‌شود و در sys.modules کش می‌شود؛ import دوم کد را دوباره اجرا نمی‌کند.
  • پایتون اول پوشه‌ی اسکریپت اجراشده را جست‌وجو می‌کند؛ برای همین فایلی به نام random.py یا csv.py در پروژه، کتابخانه‌ی استاندارد را «سایه» می‌زند. print(module.__file__) نشان می‌دهد کدام فایل import شده است.
  • import چرخه‌ای (A، B را import کند و B، A را) خطای «partially initialized module» می‌دهد؛ معمولاً یعنی مسئولیت‌ها درست تقسیم نشده‌اند و کد مشترک باید به ماژول سوم برود.
  • پوشه‌ی __pycache__ بایت‌کد کامپایل‌شده است؛ پاک کردنش بی‌خطر است و باید در .gitignore باشد.
  • python -m برای ابزارها هم کار می‌کند: python -m http.server 8000 پوشه‌ی جاری را در شبکه‌ی محلی به اشتراک می‌گذارد و python -m json.tool file.json فایل JSON را مرتب چاپ می‌کند.

برای ذخیره‌ی پیشرفت و شرکت در آزمون، وارد شوید — رایگان است.