لقد حدث أنني منذ عام 2012 أقوم بتطوير متصفح مفتوح المصدر ، كونه المبرمج الوحيد. في بيثون في حد ذاته. ليس المتصفح أسهل شيء ، والآن في الجزء الرئيسي من المشروع ، يوجد أكثر من 1000 وحدة وأكثر من 120،000 سطر من شفرة Python. في المجموع ، سيكون أكثر من مرة ونصف مع مشاريع الأقمار الصناعية.
في مرحلة ما ، تعبت من العبث بأرضيات الاستيراد في بداية كل ملف ، وقررت التعامل مع هذه المشكلة مرة واحدة وإلى الأبد. لذلك
ولدت مكتبة
smart_imports (
جيثب ،
بيبى ).
الفكرة بسيطة جدا. أي مشروع معقد يشكل في النهاية اتفاقه الخاص على تسمية كل شيء.
إذا تم تحويل هذه الاتفاقية إلى قواعد أكثر رسمية ، فيمكن استيراد أي كيان تلقائيًا بواسطة اسم المتغير المرتبط به.على سبيل المثال ، لن تحتاج إلى كتابة
import math
للوصول إلى
math.pi
- يمكننا أن نفهم
math.pi
أن
math
في هذه الحالة هي وحدة نمطية للمكتبة القياسية.
الواردات الذكية تدعم بيثون> = 3.5. المكتبة مغطاة بالكامل بالاختبارات
والتغطية> 95 ٪ . كنت أستخدمها بنفسي لمدة عام الآن.
لمزيد من التفاصيل ، أدعوك إلى Cat.
كيف يعمل بشكل عام؟
لذلك ، تعمل التعليمات البرمجية من صورة الرأس كما يلي:
- أثناء مكالمة إلى
smart_imports.all()
المكتبة AST الوحدة النمطية التي تم إجراء المكالمة منها ؛ - البحث عن متغيرات غير مهيأة ؛
- نقوم بتشغيل اسم كل متغير من خلال سلسلة من القواعد التي تحاول العثور على الوحدة النمطية (أو سمة الوحدة النمطية) اللازمة للاستيراد حسب الاسم. إذا وجدت قاعدة الكيان المطلوب ، فلن يتم التحقق من القواعد التالية.
- يتم تحميل الوحدات النمطية الموجودة وتهيئتها ووضعها في مساحة الاسم العالمية (أو يتم وضع السمات الضرورية لهذه الوحدات النمطية هناك).
يتم البحث عن متغيرات غير مهيأة في جميع أنحاء التعليمات البرمجية ، بما في ذلك بناء الجملة الجديد.
يتم تمكين الاستيراد التلقائي فقط لمكونات المشروع التي تستدعي
smart_imoprts.all()
بشكل صريح. بالإضافة إلى ذلك ، فإن استخدام الواردات الذكية لا يحظر استخدام الواردات التقليدية. يتيح لك ذلك تنفيذ المكتبة تدريجياً ، وكذلك حل التبعيات الدورية المعقدة.
سوف يلاحظ القارئ الدقيق أن وحدة AST مبنية مرتين:
- يبني CPython لأول مرة أثناء استيراد الوحدة النمطية ؛
- تقوم smart_imports في المرة الثانية بإنشائها أثناء استدعاء
smart_imports.all()
.
يمكن بناء AST حقًا مرة واحدة فقط (لهذا تحتاج إلى دمج في عملية استيراد الوحدات النمطية باستخدام خطاطيف الاستيراد المطبقة في
PEP-0302 ، لكن هذا الحل يبطئ عملية الاستيراد.
لماذا تعتقد ذلك؟بمقارنة أداء تطبيقين (مع وبدون الخطافات) ، توصلت إلى استنتاج مفاده أنه عند استيراد وحدة نمطية ، يقوم CPython بإنشاء AST في بنيات البيانات الداخلية (C-shh) الخاصة به. يعد تحويلها إلى هياكل بيانات Python أغلى من بناء شجرة من المصدر باستخدام وحدة
ast .
بالطبع ، يتم بناء AST لكل وحدة وتحليلها مرة واحدة فقط لكل إطلاق.
قواعد الاستيراد الافتراضية
يمكن استخدام المكتبة دون تكوين إضافي. بشكل افتراضي ، يقوم باستيراد الوحدات وفقًا للقواعد التالية:
- من خلال المصادفة الدقيقة للاسم ، فإنه يبحث عن الوحدة النمطية الموجودة بجانب الوحدة الحالية (في نفس الدليل).
- يتحقق الوحدات النمطية للمكتبة القياسية:
- حسب المطابقة التامة لاسم الحزم ذات المستوى الأعلى ؛
- للحزم والوحدات المتداخلة ، يتحقق من أسماء المركبات واستبدال النقاط بالشرطات السفلية. على سبيل المثال ، سيتم استيراد
os_path
في حالة os_path
متغير os_path
.
- عن طريق مطابقة الاسم تمامًا ، يبحث عن حزم الجهة الخارجية المثبتة. على سبيل المثال ، تطلب الحزمة المعروفة.
إنتاجية
لا تؤثر عمليات الاستيراد الذكية على أداء البرنامج ، ولكنها تزيد من الوقت الذي يستغرقه الإطلاق.
بسبب إعادة بناء AST ، يزيد وقت التشغيل الأول بحوالي 1.5-2 مرة. بالنسبة للمشاريع الصغيرة ، هذا ليس مهمًا. في المشروعات الكبيرة ، يعاني وقت البدء من بنية التبعية بين الوحدات النمطية بدلاً من وقت الاستيراد الخاص بوحدة نمطية معينة.
عندما تصبح الواردات الذكية شائعة ، أعيد كتابة العمل من AST إلى C - وهذا من شأنه أن يقلل بشكل كبير من تكاليف بدء التشغيل.
لتسريع عملية التحميل ، يمكن تخزين نتائج معالجة وحدات AST في نظام الملفات. يتم تمكين التخزين المؤقت في التكوين. بالطبع ، يتم تعطيل ذاكرة التخزين المؤقت عند تغيير المصدر.
يتأثر وقت بدء التشغيل بقائمة قواعد بحث الوحدة النمطية وتسلسلها. نظرًا لأن بعض القواعد تستخدم وظائف Python القياسية للبحث عن الوحدات النمطية. يمكنك استبعاد هذه النفقات من خلال الإشارة صراحةً إلى مراسلات الأسماء والوحدات النمطية باستخدام قاعدة "الأسماء المخصصة" (انظر أدناه).
ترتيب
التكوين الافتراضي قد تم وصفه مسبقًا. يجب أن تكون كافية للعمل مع المكتبة القياسية في المشاريع الصغيرة.
التكوين الافتراضي { "cache_dir": null, "rules": [{"type": "rule_local_modules"}, {"type": "rule_stdlib"}, {"type": "rule_predefined_names"}, {"type": "rule_global_modules"}] }
إذا لزم الأمر ، يمكن وضع تكوين أكثر تعقيدًا على نظام الملفات.
مثال على التكوين المعقد (من المستعرض).
أثناء استدعاء
smart_import.all()
تحدد المكتبة موضع وحدة الاتصال على نظام الملفات وتبدأ في البحث عن ملف
smart_imports.json
في الاتجاه من الدليل الحالي إلى الجذر. إذا تم العثور على مثل هذا الملف ، فإنه يعتبر التكوين للوحدة الحالية.
يمكنك استخدام العديد من التكوينات المختلفة (وضعها في دلائل مختلفة).
لا يوجد العديد من خيارات التكوين الآن:
{ // AST. // null — . "cache_dir": null|"string", // . "rules": [] }
قواعد الاستيراد
يحدد ترتيب تحديد القواعد في التكوين ترتيب تطبيقها. القاعدة الأولى التي عملت توقف البحث عن الواردات.
في أمثلة التكوينات ، غالبًا ما تظهر قاعدة rule_predefined_names ، من الضروري التعرف على الوظائف المدمجة (على سبيل المثال ،
print
) بشكل صحيح.
القاعدة 1: الأسماء المعرفة مسبقا
تسمح لك القاعدة بتجاهل الأسماء المحددة مسبقًا مثل
__file__
المدمجة مثل
print
.
المادة 2: الوحدات المحلية
للتحقق من وجود وحدة نمطية بالاسم المحدد بجوار الوحدة النمطية الحالية (في نفس الدليل). إذا كان هناك ، استيراده.
المادة 3: الوحدات العالمية
يحاول استيراد وحدة نمطية مباشرة بالاسم. على سبيل المثال ، وحدة
الطلبات .
المادة 4: الأسماء المخصصة
يتوافق مع اسم وحدة نمطية معينة أو السمة الخاصة بها. يشار إلى الامتثال في التكوين القاعدة.
المادة 5: الوحدات القياسية
يتم التحقق مما اذا كان الاسم وحدة نمطية للمكتبة القياسية. على سبيل المثال
الرياضيات أو
os.path التي تتحول إلى
os_path
.
يعمل بشكل أسرع من القاعدة الخاصة باستيراد الوحدات النمطية العالمية ، حيث يتحقق من وجود وحدة نمطية في قائمة مخزنة مؤقتًا. قوائم لكل نسخة من بيثون تأتي من هنا:
github.com/jackmaney/python-stdlib-listالقاعدة 6: الاستيراد بالبادئة
يقوم باستيراد وحدة نمطية بالاسم ، من الحزمة المقترنة بالبادئة الخاصة بها. إنه مناسب للاستخدام عندما يكون لديك العديد من الحزم المستخدمة في التعليمات البرمجية. على سبيل المثال ، يمكن الوصول إلى وحدات حزمة
utils
باستخدام البادئة
utils_
.
المادة 7: الوحدة النمطية من الحزمة الأصل
إذا كان لديك حزم فرعية بنفس الاسم في أجزاء مختلفة من المشروع (على سبيل المثال ،
tests
أو عمليات
migrations
) ، فيمكنك السماح لهم بالبحث عن الوحدات النمطية للاستيراد حسب الاسم في الحزم الأصلية.
المادة 8: الربط بحزمة أخرى
بالنسبة للوحدات النمطية من حزمة معينة ، فإنها تتيح البحث عن عمليات الاستيراد حسب الاسم في الحزم الأخرى (المحددة في التكوين). في حالتي ، كانت هذه القاعدة مفيدة للحالات التي لم أكن أريد فيها توسيع نطاق عمل القاعدة السابقة (الوحدة النمطية من الحزمة الأصل) إلى المشروع بأكمله.
مضيفا القواعد الخاصة بك
إضافة القاعدة الخاصة بك أمر بسيط للغاية:
- نرث من الفئة smart_imports.rules.BaseRule .
- نحن ندرك المنطق اللازم.
- تسجيل قاعدة باستخدام طريقة smart_imports.rules.register
- أضف القاعدة إلى التكوين.
- ؟؟؟
- الربح.
مثال يمكن العثور عليه في
تنفيذ القواعد الحالية.ربح
اختفت قوائم الاستيراد المتعددة الخطوط في بداية كل مصدر.
انخفض عدد الصفوف. قبل أن يتحول المتصفح إلى الواردات الذكية ، كان لديه 6688 سطرًا مسؤولاً عن الاستيراد. بعد الانتقال ، بقي 2084 (سطرين من الواردات الذكية لكل ملف + 130 واردات ، تم استدعاؤها بوضوح من وظائف وأماكن مماثلة).
وكانت مكافأة لطيفة توحيد الأسماء في المشروع. أصبح الرمز أسهل في القراءة وأسهل في الكتابة. ليست هناك حاجة للتفكير في أسماء الكيانات المستوردة - هناك بعض القواعد الواضحة التي يسهل اتباعها.
خطط التنمية
تعجبني فكرة تعريف خصائص الكود بأسماء متغيرة ، لذلك سأحاول تطويرها داخل عمليات الاستيراد الذكية وفي المشروعات الأخرى.
فيما يتعلق بالواردات الذكية ، أخطط:
- إضافة دعم للإصدارات الجديدة من بيثون.
- استكشف إمكانية الاعتماد على ممارسات المجتمع الحالية على تعليق التعليقات التوضيحية على الكود.
- استكشاف إمكانية صنع واردات كسول.
- قم بتطبيق الأدوات المساعدة لإنشاء التكوين تلقائيًا من أكواد المصدر وإعادة تكوين المصادر لاستخدام smart_imports.
- أعد كتابة جزء من الكود C لتسريع العمل مع AST.
- لتطوير التكامل مع اللينتر و IDEs إذا كان هؤلاء لديهم مشاكل في تحليل الكود بدون واردات واضحة.
بالإضافة إلى ذلك ، أنا مهتم برأيك حول السلوك الافتراضي للمكتبة وقواعد الاستيراد.
شكراً لك على التغلب على ورقة النص هذه: