از یک فایل بلند به پروژهی مرتب
هر فایل .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 را مرتب چاپ میکند.