zen of Python vs с утверждением-философское размышление

Я не собираюсь просто тратить ваше время, но:это произошло с вами, а с помощью языка Python with утверждение, что это действительно противоречит 5-й строке "Дзен питона", которая идет"плоская лучше, чем вложенная"? Может ли какой-нибудь просветленный гуру Python поделиться со мной некоторыми своими идеями по этому поводу?

(Я всегда нахожу, что еще один уровень отступа появляется в моем коде каждый раз, когда я использую with вместо f.close()... и это не значит, что я не собираюсь использовать try: ... finally: ... в любом случае, и таким образом преимущества with все еще ускользает от меня, даже когда я все больше и больше люблю и понимаю Python...)


@glglgl (извините, я не могу найти способ написать код в комментариях): да, но если вы идете with Кстати, Ваш код становится:

try:
    with file(...) as f:
        ...
except IOError:
    ...

...и используя только с без try это то, что люди в конечном итоге делают в типе хакерского кода" одно использование", где они используют f.close() вместо С в любом случае (что плохо, потому что файл может не закрываться, если перед их f.close()), поэтому для" хакерского " кода люди просто не используют with потому что, я не знаю, я думаю, они просто находят его слишком "причудливым", и для хорошо структурированного кода он не приносит никаких преимуществ, поэтому мне кажется, что для него не осталось никакого реального мира... вот о чем я размышлял на самом деле.

5 ответов


да Дзен питона состояния "плоский лучше, чем вложенный", однако это не единственная характеристика, о которой мы заботимся; он также утверждает, что"простой лучше, чем сложный". Красота with это то, что он на самом деле придерживается и этих принципов, как я объясню ниже.

всякий раз, когда вы оказываетесь в философских размышлениях о функции в Python, вероятно, стоит посмотреть Предложения По Улучшению Python (PEPs) чтобы прочитать о мотивации этой функции. В этом случае PEP 343 -- заявление "с" говорит Это спереди абстрактно:

этот PEP добавляет новый оператор " с " на язык Python, чтобы сделать его можно исключить стандартное использование операторов try/finally.

исключение try/finally операторы делают код более простым и читаемым.

PEP 343 идет глубже, чем однако, обеспечивая некоторый упрощенный синтаксический сахар. Он устанавливает протокол context manager:

выражение сразу после ключевого слова with в инструкции является "контекстным выражением", поскольку это выражение дает основную подсказку, как в среду выполнения, которую context manager устанавливает для продолжительность тела заявления.

используя протокол context manager, авторы API могут помочь скрыть сложность и обеспечить правильность приобретение / высвобождение ресурсов в многопоточном контексте.

но настоящая красота with инструкция показана в Примере 12 из PEP 343, который поясняет, что:

" вложенный " менеджер контекста, который автоматически помещает поставляемый контексты слева-направо, чтобы избежать чрезмерного вдавливания.

С помощью nested() context manager вы можете взять код, который выглядит так:

with a as x:
    with b as y:
        with c as z:
            # Perform operation

и превратите это в следующее:

with nested(a, b, c) as (x, y, z):
             # Perform operation

отметим, что nested() был представлен в Python 2.5, но с версии 2.7 он устарел в пользу этой синтаксической формы множественного контекстного менеджера:

with a as x, b as y, c as z:
             # Perform operation

очевидно, что это не только проще и читабельнее, но и гораздо более плоский, чем вложенный. Таким образом, используя with идет по пути 無爲 :)

обновление: в ответ комментарии к ответу Симеона Виссера вот пример того, когда вы можете использовать несколько менеджеров контекста, чтобы открыть несколько файлов одновременно, когда вы хотите, чтобы zip содержимое двух (или более) файлов вместе, так что если открытие одного из файлов не удается, это сделает все это сбой и правильно закрыть каждый файл, который был открыт:

from itertools import izip
with open("/etc/passwd") as a, open("/etc/group") as b, open("/etc/shadow") as c:
    for lines in izip(a,b,c):
        print map(lambda x: x.split(':')[0], lines)

запустите этот пример дважды; один раз как root и один раз как обычный пользователь. Предполагая, что вы сохраните этот файл как ziptogether.py сначала попробуйте вызвать его как root с sudo python ziptogether.py и это удастся, но вызов его как обычного пользователя с помощью python ziptogether.py потерпит неудачу, потому что у вас нет разрешений на чтение /etc/shadow. При сбое context manager гарантирует, что файлы, которые были успешно открыты до сбоя, правильно закрыты, когда выполнение выходит за рамки with заявление.


обратите внимание, что Дзэн питона также говорит:

простое лучше, чем сложное.

комплекс лучше, чем сложные.

и

читабельность имеет значение.

использование контекстного менеджера в with оператор предоставляет несколько вещей:

  • правильное поведение, так как файл всегда закрыт
  • читабельность (with open(..) as f вполне понятно)

вы не можете указать на один элемент в Zen Python и утверждать, что весь код Python должен всегда удовлетворять всем элементам. Например, если минимальный уровень отступа для решения конкретной проблемы читаемым и правильным способом равен четырем, то так тому и быть: если уровень отступа три делает код менее читаемым, просто оставьте код в покое (четыре хорошо).


вы уже упоминаете об этом: это чище делать

f = file(...)
try:
    # do work on file
finally:
    f.close()

чем просто закрытие после операций с файлами , которые не будут достигнуты, если произойдет исключение.

если вы сравните try/finally to with, у вас такой же уровень отступа, поэтому вы ничего не теряете. Однако, если вы выполняете обработку исключений, у вас есть еще один уровень отступа, который действительно противоречит упомянутой точке Дзен.

OTOH,with инкапсулирует вещи и использует они легче и читабельнее, которые являются другими аспектами Дзен.


"Flat is better than nested"

Ну, что такое квартира?

import thirdparty
print "I Like Pie!"

vs

import org.example.thirdparty.something
System.out.println("I Like Cake")

etc...

Zen Python не просто применяет ограничение отступа для вашего кода. Это побуждает вас писать читаемый (и, следовательно, лучший) код. Если with оператор находится в функции, доступной только 3 слоям объектов (etc,one.two.three.func()), то это проблема.

в противном случае три уровня отступов так же хороши, как и любые другие.


причина предпочесть with Это то, что вам не нужно соединять вручную соответствующие операции (например,open(...)/.close(); но with конструкция более общая - не только для работы с файлами). Это важно, в частности, в тех случаях, когда вторая операция не может быть выполнена по причинам, которые не видны из исходного кода. Ты говоришь машине позаботьтесь об этом для меня, и машина лучше в случае чем человеческий. Таким образом, вы избавитесь от группы неприятных ошибок, которые может быть трудно найти.

кстати, вы должны использовать open(...) вместо file(...). В Python 3, ничего не знает о file(...) и в противном случае вам придется исправить свой код позже.