أساسيات

الوحدات والحزم في Python: import وpip وvenv والمكتبات بكلمات بسيطة

آخر تحديث: · Skillaria.Top

الوحدة في Python ملف .py، والحزمة مجلد يضم وحدات، والمكتبة مجموعة جاهزة من الحزم، ويُستدعى كل ذلك بالأمر import. تُثبَّت المكتبات الخارجية بـ pip أو uv داخل بيئة افتراضية venv حتى لا تتعارض مشاريع مختلفة، ومنذ Python 3.15 تتيح الكلمة lazy تأجيل تحميل الوحدات الثقيلة حتى أول استخدام.

الوحدة في Python ملف فيه كود، والأمر import وسيلة لأخذ الدوال والأصناف منه. تُجمع الوحدات في حزم، والحزم في مكتبات، ولكي لا تتداخل مكتبات المشاريع المختلفة تُثبَّت في بيئة افتراضية اسمها venv. وفيما يلي شرح السلسلة كاملة: ماذا يحدث عند الاستيراد، وكيف تُبنى الحزمة، وكيف تعمل pip وvenv في Python، وما هو الاستيراد الكسول الذي ظهر في الإصدار 3.15.

الوحدة والحزمة والمكتبة: الفرق بينها

المفهومما هومثال
الوحدة (module)ملف واحد .py؛ اسم الوحدة هو اسم الملف بلا امتدادcategories.py ← import categories
الحزمة (package)مجلد يضم وحدات وملف __init__.pyexpenses/report.py ← from expenses.report import build
المكتبة (library)مجموعة جاهزة من الوحدات والحزم لمهمة معينةrequests، pandas، والمكتبة القياسية pathlib
التوزيعة (حزمة PyPI)أرشيف يثبّته pippip install requests

تُخلط كلمات «مكتبة» و«حزمة» و«وحدة» في الكلام المعتاد، وهذا طبيعي. المهم أن نعرف أن import يحمّل دائماً وحدة أو حزمة، وأن pip يثبّت توزيعة من PyPI تحتوي على حزم.

كيف يعمل import في Python

توجد أشكال متعددة للاستيراد، وكلها تظهر في الكود الحقيقي:

import math                    # الوحدة كاملة، والاستخدام math.sqrt
from math import sqrt, pi      # أسماء محددة
import datetime as dt          # اسم مستعار
from collections import Counter as C

print(math.sqrt(16), sqrt(9), pi)
print(dt.date.today())

الأسماء المستعارة شائعة للمكتبات الكبيرة: import numpy as np وimport pandas as pd. أما الصيغة from module import * فيُفضَّل تجنبها، لأنها تملأ فضاء الأسماء وتخفي مصدر الاسم.

ماذا يفعل import فعلاً

  1. يبحث عن الوحدة في ذاكرة التخزين المؤقت sys.modules. إذا كانت محمّلة من قبل يعيدها فقط.
  2. إن لم تكن محمّلة يبحث عن الملف في قائمة المجلدات sys.path: مجلد السكريبت الذي يعمل، ثم المكتبة القياسية، ثم site-packages الخاص بالبيئة.
  3. ينفّذ الملف الذي وجده كاملاً من الأعلى إلى الأسفل.
  4. يحفظ كائن الوحدة في الذاكرة المؤقتة ويربطه بالاسم.

وتترتب على ذلك نتيجتان عمليتان. الاستيراد المكرر للوحدة نفسها يكاد يكون بلا كلفة، لأن الكود لا يُنفَّذ مرة ثانية. وكل الكود في المستوى الأعلى للوحدة، بما فيه print والحسابات الثقيلة، يعمل عند الاستيراد.

import sys
for p in sys.path:
    print(p)
print("math" in sys.modules)

if __name__ == "__main__"

بما أن الاستيراد ينفّذ الملف، نحتاج طريقة للتفريق بين «شُغّلت كسكريبت» و«استُوردت». لذلك توجد المتغيّر __name__: عند تشغيل الملف يساوي "__main__"، وعند الاستيراد يساوي اسم الوحدة.

# categories.py
def guess(title: str) -> str:
    return "طعام" if "مقهى" in title.lower() else "متنوع"

if __name__ == "__main__":
    print(guess("مقهى قرب البيت"))   # يُنفَّذ فقط عند python categories.py

وللحزمة بديل: ملف __main__.py داخلها، والتشغيل بالأمر python -m expenses.

الوحدات القياسية في Python

يأتي Python «مع البطاريات»: مئات الوحدات المدمجة متاحة فوراً دون تثبيت. أكثرها استخداماً:

الوحدةلماذا تُستخدم
os، sys، pathlib، shutilالنظام والمسارات والملفات
json، csv، pickleصيغ البيانات
datetime، time، calendarالتاريخ والوقت
math، random، statistics، fractionsالأرقام
re، stringمعالجة النصوص
collections، itertools، functoolsبنى البيانات والمكررات
logging، argparse، subprocessالسجلات ووسائط سطر الأوامر وتشغيل البرامج
asyncio، threadingالبرمجة غير المتزامنة والخيوط

القائمة الكاملة لوحدات المكتبة القياسية موجودة في sys.stdlib_module_names (منذ Python 3.10). ولمعرفة ما تحتويه وحدة استخدم dir(math) وhelp(math).

حزمة خاصة بك: بنية المشروع

عندما تكثر الوحدات تُجمع في حزمة. والترتيب الشائع هو تخطيط src:

expenses-project/
├── pyproject.toml
└── src/
    └── expenses/
        ├── __init__.py
        ├── cli.py
        ├── parsing.py
        ├── categories.py
        └── report.py

تستورد الوحدات داخل الحزمة بعضها بعضاً إما باسم الحزمة الكامل (from expenses.categories import guess) أو نسبياً (from .categories import guess). والاستيراد النسبي يعمل داخل الحزمة فقط، لا في ملف يُشغَّل مباشرة كسكريبت.

pip في Python: تثبيت المكتبات

المكتبات الخارجية موجودة في الفهرس PyPI، وتُثبَّت بالأداة pip التي تأتي مع Python. والأضمن استدعاؤها عبر المفسّر نفسه، فتصل المكتبة إلى Python الذي تشغّل به الكود.

python -m pip install requests          # تثبيت
python -m pip install "pandas>=2.3"     # مع تحديد الإصدار
python -m pip install --upgrade requests
python -m pip uninstall requests        # حذف
python -m pip list                      # قائمة الحزم المثبتة
python -m pip show requests             # الإصدار والاعتماديات
python -m pip freeze > requirements.txt
python -m pip install -r requirements.txt

على Windows يُستخدم غالباً المشغّل py بدلاً من python. وإذا لم يوجد pip فثبّته بالوحدة المدمجة: python -m ensurepip --upgrade.

مكتبات Python الشائعة

عدد الحزم في PyPI مئات الآلاف، لكن أغلب المشاريع تستخدم قائمة قصيرة واحدة. ابدأ بها بدل البحث عن الغريب.

المهمةالمكتبات
طلبات HTTP والـ APIrequests، httpx
تحليل البيانات والجداولpandas، numpy
الرسوم البيانيةmatplotlib
خدمات الويبfastapi، django، flask
الاختباراتpytest
الصورpillow (يُستورد باسم PIL)

انتبه إلى أن اسم التثبيت واسم الاستيراد لا يتطابقان دائماً. تُثبَّت pillow بالأمر pip install pillow ويُكتب from PIL import Image، وتُثبَّت opencv-python ويُستورد الاسم cv2. وإذا فشل import مباشرة بعد التثبيت فراجع في وثائق الحزمة الاسم الذي تُستورد به.

تثبيت المكتبات على Linux

في Debian وUbuntu الحديثتين ينتهي الأمر pip install في Python النظام بالخطأ externally-managed-environment (PEP 668). وهذه حماية: Python النظام يتبع مدير حزم النظام، ولا ينبغي لـ pip أن يكسره. والحل الصحيح ليس --break-system-packages، بل بيئة افتراضية.

venv في Python: البيئة الافتراضية

البيئة الافتراضية مجلد مستقل فيه نسخة من المفسّر (رابط إليه) ومجلد site-packages خاص به. المكتبات المثبتة فيها لا يراها إلا هذا المشروع. فيمكن لمشروع أن يستخدم Django 4، ولمشروع مجاور Django 5، دون تعارض.

وحدة venv مدمجة في Python ولا تحتاج إلى تثبيت منفصل (وفي Debian/Ubuntu قد تحتاج إلى الحزمة النظامية python3-venv).

# إنشاء بيئة في المجلد .venv
python -m venv .venv

# التفعيل: Linux وmacOS
source .venv/bin/activate
# التفعيل: Windows PowerShell
.venv\Scripts\Activate.ps1

# الآن يثبّت pip الحزم داخل .venv
python -m pip install requests
python -c "import sys; print(sys.prefix)"

# الخروج من البيئة
deactivate

بعد التفعيل يظهر (.venv) في بداية سطر الطرفية. لا يُرفع مجلد البيئة إلى git (أضف .venv/ إلى .gitignore) ولا يُنقل بين الأجهزة، بل تُحفظ قائمة الاعتماديات وتُعاد إنشاء البيئة منها. وإذا منع PowerShell تشغيل سكريبت التفعيل فاسمح به للمستخدم: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.

uv وpyproject.toml

المشاريع الحديثة تصف الاعتماديات في pyproject.toml. والمدير uv المستخدم في الدورة يجمع إنشاء البيئة والتثبيت معاً: uv add pandas تنزّل الحزمة، وتسجلها في pyproject.toml، وتثبّت الإصدارات الدقيقة في uv.lock. ويكفي لزميلك أن ينسخ المشروع وينفّذ uv sync ليحصل على البيئة نفسها تماماً.

[project]
name = "expenses"
version = "0.1.0"
requires-python = ">=3.15"
dependencies = [
    "pandas>=2.3",
]

[project.scripts]
expenses = "expenses.cli:main"

الاستيراد الكسول في Python 3.15 (PEP 810)

أهم جديد في هذا الموضوع في الإصدار 3.15 هو الكلمة المفتاحية lazy. المكتبات الثقيلة تُحمَّل ببطء: في قياس الدورة على الصورة python:3.15-slim استغرق import pandas العادي نحو 415 ميلي ثانية، بينما استغرق lazy import pandas نحو 13 ميلي ثانية. وهذا فرق كبير لأداة سطر أوامر تُشغَّل من أجل --help.

lazy import pandas as pd
lazy from expenses.report import build_report

def main() -> None:
    ...
    df = pd.read_csv(path)   # التحميل الفعلي لـ pandas يحدث هنا

بدلاً من الوحدة يُوضع في الاسم كائن وكيل (types.LazyImportType)، ويحدث الاستيراد الحقيقي عند أول استخدام للاسم. وإذا لم يُستخدم الاسم أبداً فلا يُصرف أي وقت عليه.

قيود الاستيراد الكسول

  • مسموح فقط في المستوى الأعلى للوحدة: داخل دالة أو صنف أو try/except يعطي SyntaxError.
  • الصيغة lazy from x import * ممنوعة.
  • يظهر ModuleNotFoundError عند أول استخدام، لا عند بدء التشغيل.
  • الآثار الجانبية للوحدة (تسجيل الإضافات وما شابه) تتأجل زمنياً.

يمكن جعل كل الاستيرادات كسولة دفعة واحدة بالعلم -X lazy_imports=all أو بمتغير البيئة PYTHON_LAZY_IMPORTS=all، وتُحدَّد الاستثناءات بـ sys.set_lazy_imports_filter(). وللكود الذي يجب أن يعمل على الإصدارات القديمة يوجد المتغير __lazy_modules__ = ["pandas"] داخل الوحدة: يجعل الإصدار 3.15 الاستيرادات المذكورة كسولة، أما الإصدارات القديمة فتنفّذ import العادي. ولمعرفة الوقت الذي يضيعه بدء التشغيل استخدم python -X importtime -c "import pandas". وتستفيد البرامج التي تُشغَّل كثيراً وبسرعة، أما الخادم طويل التشغيل فلا يكسب من ذلك شيئاً يُذكر.

أخطاء شائعة

  • ModuleNotFoundError: No module named 'requests' — المكتبة ثُبِّتت في Python آخر، أو لم تُفعَّل البيئة. تحقق بـ python -m pip show requests بالمفسّر نفسه.
  • سمّيت ملفك json.py أو random.py أو csv.py — مجلد السكريبت يأتي أولاً في sys.path فيحجب الوحدة القياسية. يقترح Python 3.13 وما بعده في رسالة الخطأ «consider renaming». أعد تسمية الملف واحذف مجلد __pycache__.
  • استيراد دائري: الملف a.py يستورد b.py والعكس. انقل المشترك إلى وحدة ثالثة، أو استورد داخل الدالة.
  • تشغيل pip داخل المفسّر (>>> pip install) يعطي SyntaxError. أوامر pip تُنفَّذ في الطرفية.

الخلاصة

الوحدة ملف، والحزمة مجلد وحدات، والمكتبة مجموعة جاهزة منها. يبحث import عن الوحدة في الذاكرة المؤقتة وفي sys.path وينفّذها مرة واحدة. ثبّت المكتبات الخارجية بـ pip أو uv داخل بيئة venv فقط، فلا تتداخل المشاريع ويسهل إعادة إنشاء البيئة. وقد شُرحت هذه الموضوعات عملياً في دورة بايثون، حيث يتحول متتبّع المصروفات إلى حزمة لها اعتماديات، وتتسارع بدايتها بالاستيراد الكسول.

الأسئلة الشائعة

هل يجب إنشاء venv لكل مشروع؟

نعم، فهذه ممارسة جيدة: لكل مشروع إصدارات مكتبات خاصة به، والبيئة المنفصلة تمنع التعارضات ولا تلوّث Python النظام.

ما الفرق بين pip وuv؟

pip يثبّت الحزم فقط، أما uv فيُنشئ البيئة ويسجّل الاعتماديات في pyproject.toml ويثبّت الإصدارات الدقيقة في ملف القفل، وهو أسرع بوضوح.

كيف أرى قائمة المكتبات المثبتة في Python؟

نفّذ python -m pip list في البيئة المطلوبة. أما pip freeze فيعرض القائمة نفسها بصيغة requirements.txt.

أين تُخزَّن الوحدات المثبتة؟

في مجلد site-packages الخاص بالبيئة الحالية. ويمكن معرفة مسار وحدة بالخاصية __file__، مثلاً: import requests; print(requests.__file__).

هل يمكن نقل مجلد venv إلى جهاز آخر؟

لا، فالبيئة مرتبطة بمسارات المفسّر في الجهاز الأصلي. ينقل الناس قائمة الاعتماديات وينشئون البيئة من جديد.

تعلّم هذا الموضوع بالفيديو

يُشرح هذا الموضوع بالتفصيل مع التطبيق العملي في دورة «Python 3.15: от новичка до профи».

الانتقال إلى الدورة