Python - утверждать против if & return


12

Я пишу сценарий, который что-то делает с текстовым файлом (хотя он не имеет отношения к моему вопросу). Поэтому, прежде чем что-то делать с файлом, я хочу проверить, существует ли файл. Я могу сделать это, не проблема, но проблема больше в эстетике.

Вот мой код, реализующий одну и ту же вещь двумя разными способами.

def modify_file(filename):
    assert os.path.isfile(filename), 'file does NOT exist.'


Traceback (most recent call last):
  File "clean_files.py", line 15, in <module>
    print(clean_file('tes3t.txt'))
  File "clean_files.py", line 8, in clean_file
    assert os.path.isfile(filename), 'file does NOT exist.'
AssertionError: file does NOT exist.

или:

def modify_file(filename):
    if not os.path.isfile(filename):
        return 'file does NOT exist.'


file does NOT exist.

Первый метод производит вывод, который в основном тривиален, единственное, что меня волнует, это то, что файл не существует.

Второй метод возвращает строку, это просто.

Мои вопросы: какой метод лучше сообщить пользователю, что файл не существует? Использование assertметода кажется чем-то более питонным.

Ответы:


33

Вместо этого вы выбрали бы третий вариант: использование raiseи конкретное исключение. Это может быть одно из встроенных исключений или вы можете создать собственное исключение для задания.

В этом случае я бы использовал IOError, но ValueErrorможет также подойти:

def modify_file(filename):
    if not os.path.isfile(filename):
        raise IOError('file does NOT exist.')

Использование определенного исключения позволяет вам вызывать другие исключения для различных исключительных обстоятельств и позволяет вызывающей стороне обрабатывать исключение изящно.

Конечно, многие файловые операции (вроде open()) сами по себе поднимаются OSError; явно первое тестирование, если файл существует, может быть избыточным здесь.

Не используйте assert; если вы запускаете python с -Oфлагом, все утверждения удаляются из кода.


Интересный момент про избыточность! Вы бы порекомендовали избежать этого? то есть "это все равно не получится позже"
Ciprian Tomoiagă

1
@ CiprianTomoiagă: зачем сдавать экзамены? Если следующая строка modify_file()является with open(filename) as f:, то IOErrorтакже будет поднят. Более поздние версии Python предоставили больше подробностей в подклассах IOError( FileNotFoundErrorособенно на ум), которые могут быть полезны для разработчика, использующего этот API. Если код выполняет свои собственные проверки и повышения, IOErrorто эта полезная деталь будет потеряна.
Мартейн Питерс

@MartijnPieters был бы приемлемым ответом на вопрос "зачем удваивать тесты?" быть когда первая проверка выполняется быстрее, чем исключение, возникающее при сбое open ()? например, проверка наличия файла происходит быстрее, чем попытка его открыть, и в конечном итоге не удается это сделать.
Марсель Уилсон

1
@MarcelWilson: Нет, потому что вы будете микрооптимизированы против метода, который выполняет ввод / вывод. Никакая настройка в мелкой семантике Python не ускорит процесс ввода-вывода, а только ухудшит удобочитаемость и удобство обслуживания. Сосредоточьтесь на областях с большим воздействием, я бы сказал.
Мартейн Питерс

12

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

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

В вашем конкретном случае, пропущенный файл почти наверняка является ошибкой пользователя, и его следует обработать, вызвав исключение.


5

От использования утверждений эффективно

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

Места, на которые стоит обратить внимание:

checking parameter types, classes, or values
checking data structure invariants
checking "can't happen" situations (duplicates in a list, contradictory state variables.)
after calling a function, to make sure that its return is reasonable 

Суть в том, что если что-то пойдет не так, мы хотим сделать это как можно скорее.

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

Утверждения не являются заменой для юнит-тестов или системных тестов, а скорее являются дополнением. Поскольку утверждения являются чистым способом проверки внутреннего состояния объекта или функции, они предоставляют «бесплатно» помощь в «прозрачном ящике» для теста черного ящика, который проверяет внешнее поведение.

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.