إنشاء كتلة "مع" على العديد من مديري السياق؟ [مكرر

StackOverflow https://stackoverflow.com/questions/3024925

سؤال

هذا السؤال لديه بالفعل إجابة هنا:

لنفترض أن لديك ثلاثة كائنات تحصل عليها عبر Manager Context ، على سبيل المثال قفل واتصال DB ومقبس IP. يمكنك الحصول عليها بواسطة:

with lock:
   with db_con:
       with socket:
            #do stuff

ولكن هل هناك طريقة للقيام بذلك في كتلة واحدة؟ شيء مثل

with lock,db_con,socket:
   #do stuff

علاوة على ذلك ، هل من الممكن ، بالنظر إلى مجموعة من الكائنات غير المعروفة التي تحتوي على مديري السياق ، هل من الممكن أن تفعل ذلك بطريقة ما:

a=[lock1, lock2, lock3, db_con1, socket, db_con2]
with a as res:
    #now all objects in array are acquired

إذا كانت الإجابة "لا" ، فهل لأن الحاجة إلى مثل هذه الميزة تعني تصميمًا سيئًا ، أو ربما ينبغي أن أقترحها في PEP؟ : p

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

المحلول

في بيثون 2.7 و 3.1 وما فوق, ، يمكنك كتابة:

with A() as X, B() as Y, C() as Z:
    do_something()

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


في بيثون 3.3, ، يمكنك إدخال قائمة غير معروفة من مديري السياق باستخدام ContextLib.exitStack:

with ExitStack() as stack:
    for mgr in ctx_managers:
        stack.enter_context(mgr)
    # ...

يتيح لك ذلك إنشاء مديري السياق أثناء إضافتهم إلى ExitStack, الذي يمنع المشكلة المحتملة مع contextlib.nested (المذكورة أدناه).

ContextLib2 يوفر خلفية ExitStack لبيثون 2.6 و 2.7.


في بيثون 2.6 وتحت, ، يمكنك استخدام contextlib.nested:

from contextlib import nested

with nested(A(), B(), C()) as (X, Y, Z):
    do_something()

يعادل:

m1, m2, m3 = A(), B(), C()
with m1 as X:
    with m2 as Y:
        with m3 as Z:
            do_something()

لاحظ أن هذا ليس هو نفسه تمامًا باستخدام التداخل عادة with, ، لان A(), B(), ، و C() سيتم استدعاء جميعها في البداية ، قبل الدخول إلى مديري السياق. هذا لن يعمل بشكل صحيح إذا كانت إحدى هذه الوظائف تثير استثناء.

contextlib.nested يتم إهماله في إصدارات Python الأحدث لصالح الأساليب المذكورة أعلاه.

نصائح أخرى

الجزء الأول من سؤالك ممكن في بيثون 3.1.

مع أكثر من عنصر واحد ، تتم معالجة مديري السياق كما لو كان هناك متعددة مع عبارات متداخلة:

with A() as a, B() as b:
    suite

يعادل

with A() as a:
    with B() as b:
        suite

تغير في الإصدار 3.1: دعم تعبيرات السياق المتعددة

@إجابة Interjay صحيحة. ومع ذلك ، إذا كنت بحاجة إلى القيام بذلك لمديري السياق الطويل ، على سبيل المثال مديري السياق Mock.Patch ، فأنت تدرك بسرعة أنك تريد كسر هذا عبر الأسطر. اتضح أنه لا يمكنك لفهم في Parens ، لذلك عليك استخدام عمليات الرفع الخلفي. هذا ما يبدو عليه:

with mock.patch('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') as a, \
        mock.patch('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb') as b, \
        mock.patch('cccccccccccccccccccccccccccccccccccccccccc') as c:
    do_something()

يتم حل الجزء الثاني من سؤالك contextlib.ExitStack في بيثون 3.3.

متابعة من استجابة @sage88 ، يمكنك دائمًا تعيين هذه التصحيحات للحصول على أسماء متغيرة ذات معنى قبل الدخول إليها.

يمكنك إنشاء تلك التصحيحات في خطوط متعددة

a_patch = mock.patch('aaaaaaa') 
b_patch = mock.patch('bbbbbbb')
c_patch = mock.patch('ccccccc') 
with a_patch as a, b_patch as b, as c:    
    do_something()
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top