سؤال

بيب 8 يقول:

  • يتم وضع الواردات دائما في أعلى الملف ، مباشرة بعد أي وحدة نمطية التعليقات و docstrings ، وقبل وحدة العوالم والثوابت.

في المناسبة، قمت بانتهاك PEP 8.في بعض الأحيان أقوم باستيراد أشياء داخل الوظائف.كقاعدة عامة، أفعل ذلك إذا كان هناك استيراد يتم استخدامه فقط ضمن دالة واحدة.

أي آراء؟

تحرير (السبب الذي يجعلني أشعر بالاستيراد في الوظائف يمكن أن يكون فكرة جيدة):

سبب رئيسي:يمكن أن يجعل الكود أكثر وضوحًا.

  • عندما أنظر إلى كود الدالة قد أسأل نفسي:"ما هي الوظيفة / الفئة xxx؟" (xxx قيد الاستخدام داخل الوظيفة).إذا كانت كل الواردات الخاصة بي موجودة في الجزء العلوي من الوحدة، فيجب أن أذهب هناك لأحدد ما هو xxx.هذه مشكلة أكثر عند الاستخدام from m import xxx.رؤية m.xxx في الوظيفة ربما يخبرني بالمزيد.اعتمادا على ما m يكون:هل هي وحدة/حزمة عالية المستوى معروفة (import m)؟أم أنها وحدة فرعية/حزمة (from a.b.c import m)?
  • في بعض الحالات، وجود هذه المعلومات الإضافية ("ما هو xxx؟") بالقرب من مكان استخدام xxx يمكن أن يجعل الدالة أسهل في الفهم.
هل كانت مفيدة؟

المحلول

على المدى الطويل، أعتقد أنك ستقدر وجود معظم عمليات الاستيراد في الجزء العلوي من الملف، وبهذه الطريقة يمكنك معرفة مدى تعقيد الوحدة الخاصة بك من خلال ما تحتاج إلى استيراده.

إذا كنت أقوم بإضافة تعليمات برمجية جديدة إلى ملف موجود، فعادةً ما أقوم بالاستيراد حيثما تكون هناك حاجة إليه، ثم إذا بقي الرمز فسأجعل الأمور أكثر ديمومة عن طريق نقل سطر الاستيراد إلى أعلى الملف.

نقطة أخرى، أفضل الحصول على ImportError استثناء قبل تشغيل أي كود - كفحص سلامة، وهذا سبب آخر للاستيراد في الأعلى.

أنا أستعمل pyChecker للتحقق من الوحدات غير المستخدمة.

نصائح أخرى

هناك مناسبتان قمت بانتهاك PEP 8 في هذا الصدد:

  • الواردات الدائرية:الوحدة A تستورد الوحدة B، ولكن هناك شيء ما في الوحدة B يحتاج إلى الوحدة A (على الرغم من أن هذه غالبًا ما تكون علامة على أنني بحاجة إلى إعادة بناء الوحدات للتخلص من التبعية الدائرية)
  • إدراج نقطة توقف pdb: import pdb; pdb.set_trace() هذا مفيد ب/ج لا أريد أن أضعه import pdb في الجزء العلوي من كل وحدة قد أرغب في تصحيح الأخطاء، ومن السهل أن تتذكر إزالة الاستيراد عندما أقوم بإزالة نقطة التوقف.

خارج هاتين الحالتين، من الجيد وضع كل شيء في الأعلى.يجعل التبعيات أكثر وضوحا.

فيما يلي حالات استخدام الاستيراد الأربع التي نستخدمها

  1. importfrom x import y و import x as y) في القمة

  2. خيارات للاستيراد.في القمة.

    import settings
    if setting.something:
        import this as foo
    else:
        import that as foo
    
  3. الاستيراد المشروط.يستخدم مع مكتبات JSON وXML وما شابه.في القمة.

    try:
        import this as foo
    except ImportError:
        import that as foo
    
  4. استيراد ديناميكي.وحتى الآن، ليس لدينا سوى مثال واحد على ذلك.

    import settings
    module_stuff = {}
    module= __import__( settings.some_module, module_stuff )
    x = module_stuff['x']
    

    لاحظ أن هذا الاستيراد الديناميكي لا يجلب التعليمات البرمجية ، ولكنه يجلب تعقيدا هياكل البيانات مكتوبة بلغة بايثون.انها نوع من مثل قطعة من البيانات المخللة إلا أننا مخللها باليد.

    يوجد هذا أيضًا، بشكل أو بآخر، في الجزء العلوي من الوحدة النمطية


إليك ما نفعله لجعل الكود أكثر وضوحًا:

  • اجعل الوحدات قصيرة.

  • إذا كانت كل الواردات الخاصة بي موجودة في الجزء العلوي من الوحدة، فيجب أن أذهب هناك لأحدد الاسم.إذا كانت الوحدة قصيرة، فمن السهل القيام بذلك.

  • في بعض الحالات، قد يؤدي وجود هذه المعلومات الإضافية بالقرب من مكان استخدام الاسم إلى تسهيل فهم الوظيفة.إذا كانت الوحدة قصيرة، فمن السهل القيام بذلك.

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

إذا كنت تستخدم IronPython، فقد قيل لي أنه من الأفضل استيراد الوظائف الداخلية (نظرًا لأن تجميع التعليمات البرمجية في IronPython قد يكون بطيئًا).وبالتالي، قد تتمكن من الحصول على طريقة لاستيراد الوظائف الداخلية بعد ذلك.لكن بخلاف ذلك، أعتقد أن الأمر لا يستحق محاربة التقاليد.

كقاعدة عامة، أفعل ذلك إذا كان هناك استيراد يتم استخدامه فقط ضمن دالة واحدة.

نقطة أخرى أود أن أشير إليها هي أن هذه قد تكون مشكلة صيانة محتملة.ماذا يحدث إذا قمت بإضافة دالة تستخدم وحدة نمطية كانت تستخدم سابقًا بواسطة دالة واحدة فقط؟هل ستتذكر إضافة الاستيراد إلى أعلى الملف؟أم أنك ستقوم بفحص كل وظيفة بحثًا عن الواردات؟

FWIW، هناك حالات يكون من المنطقي فيها الاستيراد داخل دالة.على سبيل المثال، إذا كنت تريد تعيين اللغة في cx_Oracle، فأنت بحاجة إلى تعيين NLS_متغير بيئة LANG قبل يتم استيراده.وبالتالي، قد ترى رمزًا مثل هذا:

import os

oracle = None

def InitializeOracle(lang):
    global oracle
    os.environ['NLS_LANG'] = lang
    import cx_Oracle
    oracle = cx_Oracle

لقد كسرت هذه القاعدة من قبل بالنسبة للوحدات التي يتم اختبارها ذاتيًا.أي أنها تُستخدم عادةً للدعم فقط، لكنني أحدد لها عنصرًا رئيسيًا بحيث يمكنك اختبار وظائفها إذا قمت بتشغيلها بنفسها.في هذه الحالة أقوم أحيانًا بالاستيراد getopt و cmd بشكل أساسي، لأنني أريد أن يكون واضحًا لأي شخص يقرأ الكود أن هذه الوحدات لا علاقة لها بالتشغيل العادي للوحدة ويتم تضمينها للاختبار فقط.

قادمة من السؤال حول تحميل الوحدة مرتين - لماذا ليس كلاهما؟

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

طالما كان الأمر كذلك import و لا from x import *, ، يجب أن تضعهم في الأعلى.فهو يضيف اسمًا واحدًا فقط إلى مساحة الاسم العامة، وتلتزم بـ PEP 8.بالإضافة إلى ذلك، إذا كنت بحاجة إليها لاحقًا في مكان آخر، فلن تضطر إلى نقل أي شيء.

إنها ليست مشكلة كبيرة، ولكن بما أنه لا يوجد فرق تقريبًا، فإنني أقترح القيام بما يقوله PEP 8.

ألقِ نظرة على النهج البديل المستخدم في sqlalchemy:حقن التبعية:

@util.dependencies("sqlalchemy.orm.query")
def merge_result(query, *args):
    #...
    query.Query(...)

لاحظ كيف يتم الإعلان عن المكتبة المستوردة في الديكور وتمريرها كوسيطة للوظيفة!

هذا الأسلوب يجعل الكود أكثر نظافة ويعمل أيضًا 4.5 مرات أسرع من import إفادة!

المعيار: https://Gist.github.com/kolypto/589e84fbcfb6312532658df2fabdb796

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top