في بعض الأحيان يصبح من الضروري فصل عدة حزم موجودة في نفس مساحة الاسم على طول مسارات مادية مختلفة. على سبيل المثال ، إذا كنت تريد أن تكون قادرًا على نقل تخطيطات مختلفة من الإضافات ، فإن لديك القدرة على إضافتها لاحقًا دون التحكم في موقعها ، وفي الوقت نفسه ، الوصول إليها من خلال مساحة اسم واحدة.
إن ورقة الغش هذه ، والتي هي أكثر ملاءمة للمبتدئين ، مخصصة لمساحات أسماء Python.
دعونا نلقي نظرة على كيفية القيام بذلك في إصدارات مختلفة من Python ، لأنه على الرغم من أن Python2 لم يعد مدعومًا قريبًا ، فإن الكثير منا الآن بين نارين من الحرائق ، وهذه مجرد واحدة من الفروق الدقيقة المهمة في عملية الانتقال.

النظر في هذا المثال:
نريد الحصول على بنية الحزمة:
namespace1 package1 module1 package2 module2
محتويات ملف Module1
print('package 1') var1 = 1
محتويات ملف Module2
print('package 2') var2 = 2
في الوقت نفسه ، يتم توزيع الحزم في بنية المجلد التالية:
path1 namespace1 package1 module1 path2 namespace1 package2 module2
لنفترض أنه تم بالفعل إضافة المسار 1 و path2 بالفعل إلى sys.path. نحن بحاجة إلى الوصول إلى module1 و module2:
from namespace1.package1 import module1 from namespace1.package2 import module2
ماذا يحدث في
Python 3.7 عند تنفيذ هذا الرمز؟ كل شيء يعمل بشكل رائع:
package 1 package 2
مع PEP-420 في Python 3.3 ، ظهر دعم لمساحات الأسماء الضمنية. بالإضافة إلى ذلك ، عند استيراد حزمة من py33 ، لن تحتاج إلى إنشاء ملفات __init__.py. وعند استيراد مساحة الاسم ، يكون هذا فقط ممنوعًا. إذا كان الملف __init__.py موجودًا في أحد الدلائل أو كلاهما يحمل الاسم name1 ، فسيحدث خطأ عند استيراد الحزمة الثانية.
ModuleNotFoundError: No module named 'namespace1.package2'
وبالتالي ، فإن وجود شخص من الداخل يحدد الحزمة بوضوح ، ولا يمكن دمج الحزم ، فهو كيان واحد. إذا كنت تبدأ مشروعًا جديدًا ، بغض النظر عن التطوير القديم ، وسيتم تثبيت الحزم باستخدام pip ، فأنت بحاجة إلى الالتزام بهذه الطريقة. ومع ذلك ، في بعض الأحيان نرث الكود القديم ، والذي يحتاج أيضًا إلى الحفاظ عليه ، على الأقل لفترة من الوقت ، أو نقله إلى إصدار جديد.
دعنا ننتقل إلى
بيثون 2.7 . مع هذا الإصدار بالفعل أكثر إثارة للاهتمام ، تحتاج أولاً إلى إضافة __init__.py إلى كل دليل لإنشاء حزم ، وإلا فإن المترجم ببساطة لا يتعرف على الحزمة في هذه المجموعة من الملفات. ثم اكتب إعلان مساحة اسم صريح في __init__ الملفات المتعلقة بمساحة الاسم 1 ، وإلا ، سيتم استيراد الحزمة الأولى فقط.
from pkgutil import extend_path __path__ = extend_path(__path__, __name__)
ماذا يحدث مع هذا؟ عندما يصل المترجم إلى الاستيراد الأول ، يتم البحث في الحزمة التي تحمل نفس الاسم في sys.path ، ويكون في path1 / namespace1 وينفذ المترجم path1 / namespace1 / __ init__.py. لا يتم إجراء مزيد من البحث. ومع ذلك ، فإن وظيفة extension_path نفسها تبحث بالفعل عبر sys.path ، وتبحث عن جميع الحزم بمساحة الاسم 1 والاسم الداخلي وتضيفها إلى المتغير __path__ الخاص بحزمة namespace1 ، والذي يتم استخدامه للبحث عن الحزم التابعة في مساحة الاسم هذه.
في الأدلة الرسمية ، يوصى بأن تكون الأحرف الأولى كما هي في كل مرة يتم فيها وضع مساحة الاسم 1. في الواقع ، يمكن أن تكون فارغة جميعها باستثناء الأولى ، والتي يتم العثور عليها أثناء البحث في sys.path ، والتي يجب أن تسمى pkgutil.extend_path ، لأنه لم يتم تنفيذ الباقي. ومع ذلك ، بالطبع ، من الأفضل أن تكون المكالمة في كل مكتب داخلي ، حتى لا تربط منطقك "في حالة" وألا تخمين الفريق الذي كان أول من ينفذ ، لأن ترتيب البحث يمكن أن يتغير. للسبب نفسه ، يجب ألا تضع أي ملفات منطقية أخرى في منطقة المتغيرات.
سيعمل هذا في الإصدارات المستقبلية ، ويمكن استخدام هذا الرمز لكتابة
رمز متوافق ، ولكن تذكر أنه يجب عليك الالتزام بالطريقة المختارة في كل حزمة موزعة. إذا وضعت في الإصدار 3 في علبة في بعض الحزم في مكالمة إلى pkgutil.extend_path وتركت بعضها دون علبة الوارد ، فلن ينجح ذلك.
بالإضافة إلى ذلك ، يعد هذا الخيار مناسبًا أيضًا للحالة عندما تخطط لتثبيت باستخدام تثبيت python setup.py.
طريقة أخرى ، والتي تعتبر الآن قديمة إلى حد ما ، ولكن لا يزال من الممكن العثور عليها كثيرًا حيث:
وحدة pkg_resources تأتي مع حزمة setuptools. المعنى هنا هو نفسه كما في pkgutil - من الضروري أن يحتوي كل ملف __init__ على نفس بيان مساحة الاسم في كل موقع من مساحة الاسم 1 ولا يوجد رمز آخر. في الوقت نفسه ، من الضروري تسجيل مساحة الاسم namespace_packages = ['namespace1'] في setup.py. يوجد وصف أكثر تفصيلاً لإنشاء الحزم خارج نطاق هذه المقالة.
بالإضافة إلى ذلك ، يمكنك العثور على مثل هذا الرمز في كثير من الأحيان
try: __import__('pkg_resources').declare_namespace(__name__) except: from pkgutil import extend_path __path__ = extend_path(__path__, __name__)
المنطق هنا بسيط - إذا لم يتم تثبيت setuptools ، فإننا نستخدم pkgutil ، والذي يتم تضمينه في المكتبة القياسية.
إذا قمت بتكوين مساحة اسم بإحدى هذه الطرق ، يمكنك الاتصال بأخرى من وحدة نمطية واحدة. على سبيل المثال ، قم بتغيير مساحة الاسم 1 / package2 / module2
import namespace1.package1.module1 print(var1)
ثم سنرى ما يحدث إذا قمنا بتسمية حزمة جديدة عن طريق الخطأ بالإضافة إلى حزمة موجودة ولفناها بنفس مساحة الاسم. على سبيل المثال ، سيكون هناك مجموعتان في أماكن مختلفة مع الاسم package1.
namespace1 package1 module1 package1 module2
في هذه الحالة ، سيتم استيراد أول واحد فقط ولن يكون هناك وصول إلى module2. لا يمكن الجمع بين الحزم.
from namespace1.package1 import module1 from namespace1.package1 import module2
خلاصة القول:- بالنسبة لـ Python الأقدم من 3.3 والتثبيت باستخدام نقطة ، فمن المستحسن أن تستخدم إعلان مساحة اسمية ضمني.
- في حالة دعم الإصدارين 2 و 3 ، بالإضافة إلى التثبيت مع تثبيت كل من pip و python setup.py ، يوصى بخيار pkgutil.
- يوصى بخيار pkg_resources إذا كنت بحاجة إلى دعم الحزم القديمة باستخدام هذه الطريقة ، أو كنت بحاجة إلى أن تكون الحزمة آمنة من نوع zip.
مصادر:
أمثلة يمكن العثور عليها
هنا .