2015-06-24 2 views
24

Что такое чистый способ создания многострочной линии with в python? Я хочу открыть несколько файлов внутри одного with, но это достаточно далеко, чтобы я хотел его на нескольких строках. Как это:Python multi-line with statement

class Dummy: 
    def __enter__(self): pass 
    def __exit__(self, type, value, traceback): pass 

with Dummy() as a, Dummy() as b, 
    Dummy() as c: 
    pass 

К сожалению, это SyntaxError. Поэтому я пробовал это:

with (Dummy() as a, Dummy() as b, 
     Dummy() as c): 
    pass 

Также ошибка синтаксиса. Однако это сработало:

with Dummy() as a, Dummy() as b,\ 
    Dummy() as c: 
    pass 

Но что, если я хочу поместить комментарий? Это не работает:

with Dummy() as a, Dummy() as b,\ 
    # my comment explaining why I wanted Dummy() as c\ 
    Dummy() as c: 
    pass 

также не любое очевидное изменение на размещении \ с.

Есть ли способ создать многострочный оператор with, который позволяет делать комментарии внутри него?

+0

На самом деле, большой вопрос в том, что PEP-8 говорит об этом, поскольку PEP-8 ограничивает длину строки до 80 символов, что и делает это необходимым. – Justin

+0

Мнение: PEP-8 отлично, но я думаю, что ограничение 80 символов слишком низкое. Он основан на старых терминальных ограничениях, а не на человеческом удобстве.Я не выступаю за линии с более чем 300 персонажами, но у меня нет проблем с 120-130 или около того. – TigerhawkT3

+1

@ TigerhawkT3 Я думаю, что ограничение 80 символов тоже слишком низкое, но я вижу преимущество, когда я работаю над проектом, который требует одновременного открытия 5 файлов. Гораздо проще видеть каждый файл. Однако я могу сделать исключение для этого файла. – Justin

ответ

26

Учитывая, что вы добавили этот Python 3, если вам нужно пересыпать комментарии с вашими менеджерами контекста, я хотел бы использовать contextlib.ExitStack:

with ExitStack() as stack: 
    a = stack.enter_context(Dummy()) # Relevant comment 
    b = stack.enter_context(Dummy()) # Comment about b 
    c = stack.enter_context(Dummy()) # Further information 

Это эквивалентно

with Dummy() as a, Dummy() as b, Dummy() as c: 

Это имеет то преимущество, что вы можете создать свой кон текстовых менеджеров в цикле вместо необходимости отдельно указывать каждый из них. Документация дает пример того, что если вы хотите открыть кучу файлов, и у вас есть имена файлов в списке, вы можете сделать

with ExitStack() as stack: 
    files = [stack.enter_context(open(fname)) for fname in filenames] 

Если ваши менеджеры контекста занимают так много места на экране, что вы хотите поместить комментарии между ними, вы, вероятно, достаточно, чтобы захотеть использовать какой-то цикл.


Как г-н Бессмертный упоминает в комментариях, есть contextlib backport на PyPI под названием contextlib2. Если вы находитесь на Python 2, вы можете использовать реализацию backport ExitStack.

+0

Это отличное решение. – Matthias

+1

Существует резерв обновлений contextlib для Python 2 на [pypi] (https://pypi.python.org/pypi/contextlib2/). Он обеспечивает 'ExitStack()' среди прочего. –

2

Это не совсем чистый, но вы можете сделать это:

with Dummy() as a, Dummy() as b, (
    #my comment 
    Dummy()) as c: 
    pass 

Там нет никаких синтаксических ошибок, но это не самый чистый. Вы также можете сделать это:

with Dummy() as a, Dummy() as b, Dummy(
    #my comment 
    ) as c: 
    pass 

Рассмотрим найти способ сделать это без использования комментариев в середине with.

+0

Молодцы Quincunx. Это довольно сумасшедший синтаксис, но я думаю, что это то, чего хотел OP. Я не думал, что это возможно. – DevShark

+6

Забавный факт: он _is_ ОП. – TigerhawkT3

+0

@DevShark Это делает то, что я хочу, но есть ли способ сделать это, что не так сумасшедшим? Это вопрос, который я хочу знать. Иногда я хочу, чтобы python смог сказать: «В конце этой строки есть оператор, который требует что-то после него, поэтому лучше проверить следующую строку для другого операнда». – Justin

12

Это кажется tidiest мне:

with open('firstfile', 'r') as (f1 # first 
), open('secondfile', 'r') as (f2 # second 
): 
    pass 
+0

Это чувствует себя намного лучше, чем то, что я нашел. – Justin