سؤال

هل من الممكن إعلان أكثر من متغير باستخدام with بيان في بيثون؟

شيء مثل:

from __future__ import with_statement

with open("out.txt","wt"), open("in.txt") as file_out, file_in:
    for line in file_in:
        file_out.write(line)

... أو تقوم بتنظيف اثنين من الموارد في نفس الوقت المشكلة؟

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

المحلول

من الممكن بيثون 3 منذ v3.1 و بيثون 2.7.. وبعد الجديد with بناء الجملة يدعم مديري السياق المتعدد:

with A() as a, B() as b, C() as c:
    doSomething(a,b,c)

على عكس. contextlib.nested, هذا يضمن ذلك a و b سيكون لها __exit__()يسمى حتى لو C() أو هو __enter__() الطريقة ترفع استثناء.

نصائح أخرى

contextlib.nested يدعم هذا:

import contextlib

with contextlib.nested(open("out.txt","wt"), open("in.txt")) as (file_out, file_in):

   ...

تحديث:
لاقتبس الوثائق، فيما يتعلق contextlib.nested:

مهملة منذ الإصدار 2.7: يدعم التصريح مع العبارة الآن هذه الوظيفة مباشرة (بدون خطأ مربكة عرضة).

يرى إجابة Rafał Dowgird للمزيد من المعلومات.

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

with A() as a, \
     B() as b, \
     C() as c:
    doSomething(a,b,c)

الأقواس لا تعمل، لأن بيثون يخلق tuple بدلا من ذلك.

with (A(),
      B(),
      C()):
    doSomething(a,b,c)

منذ tuples تفتقر إلى __enter__ سمة، يمكنك الحصول على خطأ (غير محدد ولا يحدد نوع الفصل):

AttributeError: __enter__

إذا حاولت استخدام as داخل الأقواس، يلفت بيثون الخطأ في تحليل الوقت:

with (A() as a,
      B() as b,
      C() as c):
    doSomething(a,b,c)

خطأ قواعدي: بناء جملة غير صالح

https:/bugs.python.org/issue12782. يبدو أن مرتبط بهذه المشكلة.

أعتقد أنك تريد القيام بذلك بدلا من ذلك:

from __future__ import with_statement

with open("out.txt","wt") as file_out:
    with open("in.txt") as file_in:
        for line in file_in:
            file_out.write(line)

منذ بيثون 3.3، يمكنك استخدام الفصل ExitStack من contextlib وحدة.

يمكن أن تدير متحرك عدد الكائنات العلمية السياق، مما يعني أنه سيثبت أنه مفيد بشكل خاص إذا كنت لا تعرف عدد الملفات التي ستتعامل معها.

تقوم حالة الاستخدام الكنسي المذكورة في الوثائق بإدارة عدد ديناميكي من الملفات.

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # All opened files will automatically be closed at the end of
    # the with statement, even if attempts to open files later
    # in the list raise an exception

إليك مثال عام:

from contextlib import ExitStack

class X:
    num = 1

    def __init__(self):
        self.num = X.num
        X.num += 1

    def __repr__(self):
        cls = type(self)
        return '{cls.__name__}{self.num}'.format(cls=cls, self=self)

    def __enter__(self):
        print('enter {!r}'.format(self))
        return self.num

    def __exit__(self, exc_type, exc_value, traceback):
        print('exit {!r}'.format(self))
        return True

xs = [X() for _ in range(3)]

with ExitStack() as stack:
    print(stack._exit_callbacks)
    nums = [stack.enter_context(x) for x in xs]
    print(stack._exit_callbacks)
print(stack._exit_callbacks)
print(nums)

انتاج:

deque([])
enter X1
enter X2
enter X3
deque([<function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86158>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f861e0>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86268>])
exit X3
exit X2
exit X1
deque([])
[1, 2, 3]
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top