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

URL: https://skillaria.top/ar/guides/python/modules-python

الوحدة في 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__.py` | `expenses/report.py` ← `from expenses.report import build` |

| المكتبة (library) | مجموعة جاهزة من الوحدات والحزم لمهمة معينة | `requests`، `pandas`، والمكتبة القياسية `pathlib` |

| التوزيعة (حزمة PyPI) | أرشيف يثبّته pip | `pip 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 والـ API | `requests`، `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 فقط، فلا تتداخل المشاريع ويسهل إعادة إنشاء البيئة. وقد شُرحت هذه الموضوعات عملياً في دورة بايثون، حيث يتحول متتبّع المصروفات إلى حزمة لها اعتماديات، وتتسارع بدايتها بالاستيراد الكسول.

## FAQ

**هل يجب إنشاء 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 إلى جهاز آخر؟**

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


---
Source: Skillaria.Top (https://skillaria.top)
