إذا ظهر لك الخطأ ImportError: cannot import name فهذا يعني غالبًا أن Python وصلت إلى الـModule الذي طلبته، لكنها لم تجد الاسم المطلوب متاحًا في اللحظة التي حاولت استيراده فيها. السبب قد يكون اسمًا غير موجود، أو تعريفًا لم يُنفّذ بعد، أو Circular Import بين ملفين، أو تعارض اسم ملفك مع مكتبة أخرى.
في هذا الدرس من سلسلة مشكلة وحل 12 على بايثون العرب سنركز على تشخيص cannot import name والاستيراد الدائري عمليًا، بدل إعادة شرح مشكلة No module named وتثبيت الحزم.
{getToc} $title={محتوى المقال}
{alertInfo}
الخلاصة السريعة: إذا كانت Python لا تجد الـModule أصلًا فهذه نية ModuleNotFoundError. أما إذا وجدت الـModule لكن الاسم المطلوب غير متاح، فابدأ بفحص الاسم نفسه ثم ابحث عن Circular Import أو تعارض أسماء الملفات.
أولًا: لا تخلط بين cannot import name وNo module named
التمييز بين الرسالتين يوفر عليك كثيرًا من الحلول الخاطئة. لا تبدأ بـ pip install تلقائيًا لمجرد أن الخطأ متعلق بالاستيراد.
| الرسالة | المعنى العملي | أين تبدأ التشخيص؟ |
|---|---|---|
ModuleNotFoundError: No module named 'x' |
Python لم تجد الـModule المطلوب أصلًا. | التثبيت، البيئة، المسار واسم الحزمة. |
ImportError: cannot import name 'y' from 'x' |
Python وصلت إلى x لكن الاسم y غير متاح منها. |
وجود الاسم، Circular Import، الإصدار، أو تعارض أسماء الملفات. |
إذا كانت رسالتك هي No module named فانتقل إلى مقال
حل ModuleNotFoundError: No module named في Python؛ لأنه هو الصفحة المخصصة للتثبيت وبيئات Python.
أما تثبيت الحزم باستخدام pip فهو مسار مناسب عندما تثبت أن الـModule غير موجود في البيئة الحالية؛ وليس خطوة تلقائية لمجرد ظهور كلمة ImportError.
ماذا يعني ImportError: cannot import name؟
انظر إلى هذا المثال. لدينا ملف tools.py يحتوي على دالة باسم say_hello، لكننا نحاول استيراد اسم آخر غير موجود.
# tools.py
def say_hello():
return "Hello"# main.py
from tools import welcomeImportError: cannot import name 'welcome' from 'tools'
هنا المشكلة ليست أن ملف tools.py مفقود. Python وجدته، لكن لا يوجد داخله اسم متاح باسم welcome.
1. تحقق أن الاسم موجود فعلًا داخل الـModule
أبسط سبب للخطأ هو أن اسم الدالة أو الكلاس أو المتغير غير موجود، أو أن هناك اختلافًا في الكتابة وحالة الأحرف.
# tools.py
def say_hello():
return "Hello"
# main.py
from tools import say_hello
print(say_hello())
راجع الاسم حرفًا بحرف. فمثلًا User وuser اسمان مختلفان. وإذا كنت تستورد من مكتبة خارجية، تأكد أيضًا أن الاسم ما يزال موجودًا في الإصدار المثبت لديك؛ فقد تنتقل بعض الأسماء أو تتغير بين الإصدارات.
طريقة سريعة للتأكد من الملف والاسم
import tools
print(tools.__file__)
print(hasattr(tools, "say_hello"))
print(hasattr(tools, "welcome"))
يعرض __file__ الملف الذي تم استيراده فعليًا، بينما يساعدك hasattr() على معرفة هل الاسم موجود داخل الـModule أم لا. هذه الخطوة مهمة جدًا قبل افتراض أن المشكلة في pip.
2. ما معنى partially initialized module؟
إذا ظهرت الرسالة بهذا الشكل:
ImportError: cannot import name 'get_user' from partially initialized module 'users'
(most likely due to a circular import)
فعبارة partially initialized module تعني أن Python بدأت تنفيذ أحد الملفات، ثم انتقلت لاستيراد ملف آخر، ثم عاد الملف الثاني وطلب اسمًا من الملف الأول قبل أن يكمل الأول تنفيذ تعريفاته.
هذه هي الصورة الكلاسيكية لـ Circular Import.
3. مثال واضح على Circular Import بين ملفين
افترض أن لدينا ملفين: users.py وreports.py.
# users.py
from reports import build_report
def get_user():
return "Ali"# reports.py
from users import get_user
def build_report():
return f"Report for {get_user()}"
عند استيراد users.py تبدأ Python بتنفيذه من الأعلى، فتصل إلى استيراد reports.py. أثناء تنفيذ reports.py يطلب الملف get_user من users.py، لكن تعريف get_user لم يُنفّذ بعد لأن Python ما تزال في منتصف تحميل users.py.
{alertWarning} وجود الملفين على القرص لا يعني أن كل الأسماء داخلهما أصبحت جاهزة. في Circular Import قد يكون الـModule موجودًا في الذاكرة لكنه ما يزال جزئي التهيئة.
4. أفضل حل: اكسر دائرة الاعتماد بإعادة تنظيم الكود
إذا كان الملفان يحتاجان منطقًا مشتركًا، فالحل الأنظف غالبًا هو نقل الجزء المشترك إلى ملف ثالث مستقل بدل أن يستورد كل ملف الآخر.
project/
├── users.py
├── reports.py
└── shared.py# shared.py
def get_user():
return "Ali"# reports.py
from shared import get_user
def build_report():
return f"Report for {get_user()}"# users.py
from reports import build_report
print(build_report())
الآن لم يعد هناك اعتماد دائري: reports.py يعتمد على shared.py، وusers.py يعتمد على reports.py.
5. حل تكتيكي: نقل import إلى داخل الدالة
أحيانًا لا تحتاج إعادة هيكلة كبيرة، ويمكن تأخير الاستيراد حتى وقت استدعاء الدالة. هذا الأسلوب مفيد عندما يكون الاعتماد محدودًا وواضحًا.
# reports.py
def build_report():
from users import get_user
return f"Report for {get_user()}"
بهذه الطريقة لا يحدث الاستيراد أثناء التحميل الأولي للملف، بل عند تنفيذ الدالة لاحقًا بعد اكتمال تهيئة الـModules.
{alertInfo} لا تجعل نقل كل imports إلى داخل الدوال عادة عامة. الأصل أن تكون imports واضحة في أعلى الملف، واستخدم الاستيراد المحلي عندما يكون هناك سبب فعلي مثل كسر Circular Import.
6. أحيانًا يساعد import module بدل from module import name
في بعض دوائر الاستيراد يمكن أن يكون استيراد الـModule نفسه أقل حساسية من سحب اسم منه أثناء مرحلة التهيئة:
import users
def build_report():
return f"Report for {users.get_user()}"
لكن هذا ليس حلًا سحريًا لكل Circular Import. إذا كان التصميم نفسه يجعل ملفين يعتمدان على بعضهما بقوة، فإعادة الهيكلة تبقى الحل الأفضل.
7. تعارض اسم ملفك مع مكتبة: Module Shadowing
قد تكون كتبت الاستيراد بصورة صحيحة، لكن Python تستورد ملفًا محليًا بدل المكتبة التي تقصدها. يحدث ذلك مثلًا إذا كان لديك ملف باسم:
random.py
json.py
requests.py
datetime.py
ثم كتبت import random أو from json import dumps. وجود ملف محلي بالاسم نفسه قد يجعل Python تختاره بدل المكتبة الأصلية، فتظهر رسائل ImportError أو AttributeError غير متوقعة.
كيف تتأكد من الملف الذي استوردته Python؟
import random
print(random.__file__)
إذا ظهر المسار داخل مجلد مشروعك مثل project/random.py بدل مسار مكتبة Python، فقد وجدت السبب. غيّر اسم الملف إلى اسم فريد مثل random_demo.py، ثم احذف مجلد __pycache__ إذا بقيت آثار قديمة.
8. ماذا عن relative imports والاستيراد بين ملفات المشروع؟
إذا كانت رسالتك مثل attempted relative import with no known parent package أو كانت المشكلة أن Python لا ترى ملفًا داخل Package، فهذه نية مختلفة عن cannot import name.
لشرح هيكل الـPackage و__init__.py وrelative imports وتشغيل الوحدة باستخدام python -m راجع:
إنشاء Module وPackage في Python والاستيراد بين الملفات.
9. لا تستخدم pip install كحل تلقائي لـ cannot import name
إذا كان الخطأ هو ImportError: cannot import name، فإعادة تثبيت الحزمة قد لا تغيّر شيئًا؛ لأن Python قد تكون وجدتها أصلًا. استخدم pip أساسًا عندما تكون المشكلة أن الحزمة غير موجودة أو عندما تثبت أن الإصدار نفسه غير مناسب.
قبل أي إعادة تثبيت، اسأل: هل Python لم تجد الـModule؟ أم وجدته ولم تجد الاسم؟ الفرق بين السؤالين هو الذي يحدد اتجاه التشخيص.
10. Checklist لتشخيص from x import y
- اقرأ الاسم بعد
cannot import name: هذا هوy. - اقرأ اسم الـModule بعد
from: هذا هوx. - جرّب
import xوحدها. إذا فشلت، فالمشكلة أوسع من الاسمy. - اطبع
x.__file__للتأكد أنك تستورد الملف الصحيح. - تحقق من
hasattr(x, "y")أو راجع تعريف الاسم داخل الملف. - إذا ظهرت
partially initialized module، ابحث عن ملفين يستورد كل منهما الآخر. - افحص أسماء ملفات المشروع بحثًا عن أسماء تطابق مكتبات قياسية أو خارجية.
- إذا كان الاسم تابعًا لمكتبة خارجية، راجع الإصدار والتوثيق الحالي لها.
11. جدول سريع: الرسالة والسبب الأقرب
| الرسالة أو العلامة | السبب الأقرب | الخطوة التالية |
|---|---|---|
cannot import name 'x' from 'module' |
الاسم غير موجود أو تغيّر أو لم يصبح متاحًا بعد. | راجع الاسم وmodule.__file__ ووجود x. |
partially initialized module |
Circular Import غالبًا. | تتبع الاعتماد بين الملفات واكسر الحلقة. |
| يتم استيراد ملف من مجلد المشروع بدل المكتبة. | Module Shadowing. | غيّر اسم الملف وافحص __file__. |
No module named |
الـModule غير موجود في مسار الاستيراد. | انتقل لتشخيص ModuleNotFoundError والتثبيت/البيئة. |
attempted relative import... |
طريقة تشغيل أو بنية Package. | راجع relative imports وتشغيل python -m. |
مثال عملي: من Circular Import إلى بنية سليمة
لنفترض أن ملف orders.py يحتاج اسم العميل، بينما customers.py يحتاج تنسيق الطلب. إذا جعلت الملفين يستوردان بعضهما في أعلى الملف، فأنت تبني حلقة اعتماد.
بدل ذلك، انقل البيانات أو الوظائف المشتركة إلى ملف محايد:
shop/
├── orders.py
├── customers.py
└── common.py# common.py
def customer_label(name):
return name.strip().title()
# customers.py
from common import customer_label
def get_customer():
return customer_label("ali")
# orders.py
from customers import get_customer
def build_order():
return f"Order for {get_customer()}"
print(build_order())
هذه البنية تجعل اتجاه الاعتماد واضحًا بدل دائرة: orders → customers → common.
أخطاء شائعة عند محاولة حل ImportError
- استخدام
pip installمباشرة رغم أن الرسالةcannot import name. - تغيير مسارات
sys.pathعشوائيًا قبل فهم هيكل المشروع. - نقل import إلى داخل دالة في عشرات الأماكن بدل إصلاح التصميم الدائري.
- نسيان أن اسم ملف محلي قد يطابق اسم مكتبة.
- افتراض أن وجود الدالة في الملف يعني أنها أصبحت متاحة قبل اكتمال تنفيذ الملف.
- نسخ حل من إصدار مكتبة مختلف دون التأكد أن الاسم ما يزال موجودًا.
أسئلة شائعة
ما سبب ImportError: cannot import name في Python؟
يظهر عندما تصل Python إلى الـModule، لكنها لا تجد الاسم المطلوب متاحًا. قد يكون الاسم غير موجود أو مكتوبًا خطأ، أو قد يكون هناك Circular Import، أو تعارض في أسماء الملفات، أو اختلاف بين إصدار المكتبة والكود المستخدم.
ما معنى cannot import name from partially initialized module؟
تعني أن Python بدأت تحميل الـModule لكنها لم تكمل تهيئته بعد، ثم حاول ملف آخر استيراد اسم لم يتم تعريفه بعد. السبب الشائع هو Circular Import بين ملفين أو أكثر.
كيف أحل Circular Import في Python؟
الأفضل إعادة تنظيم الاعتمادات ونقل الكود المشترك إلى Module ثالث مستقل. ويمكن في حالات محددة تأخير import داخل دالة أو استيراد الـModule نفسه بدل سحب الاسم أثناء التهيئة.
هل pip install يحل cannot import name؟
ليس عادة. pip install مناسب عندما تكون الحزمة غير مثبتة أو تحتاج إصدارًا مختلفًا. أما cannot import name فيعني غالبًا أن الـModule موجود بالفعل لكن الاسم المطلوب غير متاح.
هل اسم الملف يمكن أن يسبب ImportError؟
نعم. تسمية ملفك random.py أو json.py أو باسم مكتبة خارجية قد تجعل Python تستورد ملفك المحلي بدل المكتبة المقصودة.
مصادر وروابط مفيدة
- حل ModuleNotFoundError: No module named في Python
- إنشاء Module وPackage في Python والاستيراد بين الملفات
- سلسلة مشكلة وحل في بايثون
- Python Programming FAQ - Circular Imports
- Python Documentation - The import system
الخلاصة
عندما ترى ImportError: cannot import name لا تتعامل معه كأنه ModuleNotFoundError. ابدأ بالتأكد من الاسم والملف الذي تم استيراده فعليًا، ثم ابحث عن Circular Import إذا ظهرت عبارة partially initialized module.
في المشاريع متعددة الملفات، الحل الأفضل غالبًا ليس إضافة المزيد من أوامر import، بل جعل اتجاه الاعتمادات أوضح: انقل المنطق المشترك إلى Module مستقل، وتجنب أن يستورد ملفان بعضهما في أعلى الملف.
{alertSuccess} القاعدة الذهبية:No module namedيعني غالبًا أن Python لم تجد الـModule؛ أماcannot import nameفيعني أن عليك فحص الاسم، ترتيب التهيئة، Circular Import، والملف الذي تم استيراده فعلًا.