Python - затверджуйте vs 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то ця корисна деталь буде втрачена.
Martijn Pieters

@MartijnPieters буде прийнятною відповіддю на "чому подвоїти тести?" бути, коли перша перевірка швидша, ніж виняток, піднятий, коли open () не вдається? наприклад, коли перевірка наявності файлу відбувається швидше, ніж спроба відкрити і в кінцевому підсумку не зробити цього.
Марсель Вілсон

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

12

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

Його значення дещо обмежене, оскільки вам потрібно забезпечити здійснення цього шляху через код, і ви часто хочете додатково впоратися з проблемою окремим ifтвердженням у виробництві. assertнайкорисніший у таких ситуаціях, як "Я хочу корисно подолати цю проблему, якщо користувач потрапить на неї, але якщо розробник потрапить на неї, я хочу, щоб вона сильно вийшла з ладу, тому він виправить код, який викликає цю функцію неправильно".

У вашому конкретному випадку відсутній файл майже напевно є помилкою користувача, і його слід вирішувати шляхом винятку.


5

З використанняAsersertesEfectively

Перевіркою речовини () не слід перестаратися: якщо вона трясеться, як качка, можливо, не потрібно занадто глибоко розпитувати, чи є вона насправді. Іноді може бути корисно передавати значення, які не передбачали оригінальний програміст.

Місця для розгляду тверджень:

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.