سؤال

أعتقد أن وضع بيان الاستيراد على مقربة من الجزء الذي يستخدمه يساعده على قدرته على الاستقرار من خلال جعل تبعياتها أكثر وضوحا. هل ستؤدي بيثون ذاكرة التخزين المؤقت هذا؟ يجب أن أهتم؟ هل هذه فكرة سيئة؟

def Process():
    import StringIO
    file_handle=StringIO.StringIO('hello world')
    #do more stuff

for i in xrange(10): Process()

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

هل كانت مفيدة؟

المحلول

إجابات أخرى تتفوق على الارتباك المعتدل حول كيفية import يعمل حقا.

هذا البيان:

import foo

يعادل تقريبا هذا البيان:

foo = __import__('foo', globals(), locals(), [], -1)

وهذا هو، إنه يخلق متغير في النطاق الحالي بنفس اسم الوحدة النمطية المطلوبة، وتعينها نتيجة للاتصال __import__() مع اسم الوحدة النمطية وحجم الحجج الافتراضية.

ال __import__() تعمل الوظيفة من الناحية الفنية تحويل سلسلة ('foo') في كائن الوحدة النمطية. يتم تخزين الوحدات المخزنة في sys.modules, وهذا هو المقام الأول __import__() يبدو - إذا كان sys.modules لديه إدخال ل 'foo', ، وهذا ما __import__('foo') سوف تعود، مهما كان. انها حقا لا تهتم بالنوع. يمكنك أن ترى هذا في العمل بنفسك؛ حاول تشغيل التعليمات البرمجية التالية:

import sys
sys.modules['boop'] = (1, 2, 3)
import boop
print boop

ترك جانبا المخاوف الأسلوبية في الوقت الراهن، وجود بيان الاستيراد داخل الوظيفة يعمل كيف تريد. إذا لم يتم استيراد الوحدة النمطية من قبل، فسيتم استيرادها وتتخشى مؤقتا في SYS.Modules. ثم يعين الوحدة النمطية إلى المتغير المحلي مع هذا الاسم. نعم هو كذلك ليس ليس كذلك تعديل أي حالة مستوى الوحدة النمطية. هو - هي هل ربما تعديل بعض الحالة العالمية (إضافة إدخال جديد إلى sys.modules).

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

نصائح أخرى

لطفا أنظر بيب 8.:

يتم وضع الواردات دائما في الجزء العلوي من الملف، فقط بعد أي تعليقات وحدة ومسكرات، وقبل وحدة العالم والثوابت.

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

على النمط جانبا، صحيح أن الوحدة النمطية المستوردة لن يتم استيرادها إلا مرة واحدة (ما لم reload يسمى الوحدة المذكورة). ومع ذلك، كل مكالمة إلى import Foo سوف تحقق ضمنيا لمعرفة ما إذا كانت هذه الوحدة محملة بالفعل (عن طريق التحقق sys.modules).

ضع في اعتبارك أيضا "تفكيك" لوظيفتين متساويين خلافا حيث يحاول المرء استيراد وحدة نمطية والآخر لا:

>>> def Foo():
...     import random
...     return random.randint(1,100)
... 
>>> dis.dis(Foo)
  2           0 LOAD_CONST               1 (-1)
              3 LOAD_CONST               0 (None)
              6 IMPORT_NAME              0 (random)
              9 STORE_FAST               0 (random)

  3          12 LOAD_FAST                0 (random)
             15 LOAD_ATTR                1 (randint)
             18 LOAD_CONST               2 (1)
             21 LOAD_CONST               3 (100)
             24 CALL_FUNCTION            2
             27 RETURN_VALUE        
>>> def Bar():
...     return random.randint(1,100)
... 
>>> dis.dis(Bar)
  2           0 LOAD_GLOBAL              0 (random)
              3 LOAD_ATTR                1 (randint)
              6 LOAD_CONST               1 (1)
              9 LOAD_CONST               2 (100)
             12 CALL_FUNCTION            2
             15 RETURN_VALUE        

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

سريع وقذرة timeit لا يوضح الاختبار تحسين سرعة متواضعة عند استخدامه Bar:

$ python -m timeit -s "from a import Foo,Bar" -n 200000 "Foo()"
200000 loops, best of 3: 10.3 usec per loop
$ python -m timeit -s "from a import Foo,Bar" -n 200000 "Bar()"
200000 loops, best of 3: 6.45 usec per loop

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

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

أيضا، ينتهك ما أعتقد أنه مبدأ مهم للغاية: لا تقم بتعديل حالة مستوى الوحدة النمطية مباشرة من داخل الوظيفة.

يحرر:

في الواقع، اتضح أن كل ما سبق هو هراء.

استيراد الوحدة النمطية لا تعديل حالة مستوى الوحدة النمطية (إنه تهيئة الوحدة النمطية التي يتم استيرادها، إذا لم يكن هناك شيء آخر، لكن هذا ليس في نفس الشيء). استيراد وحدة نمطية التي قمت باستيرادها بالفعل في مكان آخر لا تكاليفك باستثناء البحث sys.modules وإنشاء متغير في النطاق المحلي.

مع العلم أن هذا، أشعر بالغباء في إصلاح جميع الأماكن في التعليمات البرمجية التي قمت بإصلاحها، لكن هذا الصليب لتحمل.

عندما يضرب مترجم الثعبان ببيان الاستيراد، يبدأ في قراءة جميع تعريفات الوظائف في الملف الذي يتم استيراده. هذا ما يفسر السبب في بعض الأحيان، يمكن أن تستغرق الواردات بعض الوقت.

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

أود أن أقترح بعد الاتفاقية والحفاظ على الواردات في الجزء العلوي من ملف التعليمات البرمجية. إذا كنت ترغب حقا في تتبع التبعيات للوظائف، فأنا أقترح إضافةها في docstring. لهذه الوظيفة.

أستطيع أن أرى طريقتين عندما تحتاج إلى استيرادها محليا

  1. لغرض الاختبار أو للاستخدام المؤقت، تحتاج إلى استيراد شيء ما، في هذه الحالة يجب أن تضع الاستيراد في مكان الاستخدام.

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

خلاف ذلك وضعت دائما في أعلى الكفاءة والاتساق.

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