Меню

Python ошибки на русском

Зачем эта статья?

Об обработке текстов на естественном языке сейчас знают все. Все хоть раз пробовали задавать вопрос Сири или Алисе, пользовались Grammarly (это не реклама), пробовали генераторы стихов, текстов… или просто вводили запрос в Google. Да, вот так просто. На самом деле Google понимает, что вы от него хотите, благодаря штукам, которые умеют обрабатывать и анализировать естественную речь в вашем запросе.

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

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

Обратите внимание на следующий запрос:

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

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

Откуда взялась статья?

Одним из важных составляющих в области ИИ является обработка текстов на естественном языке. В процессе изучения данной тематики я начал задавать себе вопросы, которые в конечном итоге привели меня к изучению кодировок, представлению текстов в памяти, как они преобразуются, приводятся к нормальной форме. Я плохо понимал эту тему в начале, потребовалось немало времени и мозгового ресурса, чтобы понять, принять и запомнить некоторые вещи. Написанием данной статьи я хочу облегчить жизнь людям, которые столкнутся с необходимостью чтения и обработки текстов на Python и самому закрепить изученное. А некоторыми полезными поинтами своего изучения я постараюсь поделиться в данной статье.

Важная ремарка: я не являюсь специалистом в области обработки текстов. Изложенный материал является результатом исключительно любительского изучения.

Проблема чтения файлов

Допустим, у нас есть файл с текстом. Нам нужно этот текст прочитать. Казалось бы, пиши себе такой вот скрипт для чтения из файла да и радуйся:

with open("some_text.txt", "r") as file:
    content = file.read()

print(content)

В файле содержится вот такое вот изречение:

pitón

что переводится с испанского как питон. Однако консоль OC Windows 10 покажет нам немного другой результат:

C:myhabrTextsInPython> python .script1.py
pitón

Сейчас мы разберёмся, что именно пошло не так и по какой причине.

Кодировка

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

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

with open("simple_text.txt", "r") as file:
    text = file.read()
print(text)

Посмотрим на результат:

C:myhabrTextsInPython> python .script2.py
ÿþ<♦8♦@♦

Очень интересно, ничего непонятно. По умолчанию Python использует кодировку utf-8, но видимо запись в файл происходила не с её помощью. Здесь нам придёт на помощь дополнительный параметр функции open — параметр encoding, который позволяет указать конкретную кодировку, в которой следует прочитать файл (или записывать в него). Попробуем перебрать несколько кодировок и найти подходящую.

codecs = ["cp1252", "cp437", "utf-16be", "utf-16"]

for codec in codecs:
    with open("simple_text.txt", "r", encoding=codec) as file:
        text = file.read()
    print(codec.rjust(12), "|", text)

Результат:

C:myhabrTextsInPython> python .script3.py
      cp1252 | ÿþ<8@
       cp437 |  ■<8@
    utf-16be | 㰄㠄䀄
      utf-16 | мир

Разные кодировки расшифровывают байты из файла по-разному, то есть разным кодовым позициям могут соотвествовать разные символы. Пример примитивный, несложно догадаться, что истинная кодировка файла — это utf-16.

Важный поинт: при записи и чтении из файлов следует указывать конкретную кодировку, это позволит избежать путаницы в дальнейшем.

Ошибки, связанные с кодировками

При возникновении ошибки, связанной с кодировками, интерпретатор выдаст одно из следующих исключений:

  • UnicodeError. Это общее исключение для ошибок кодировки.

  • UnicodeDecodeError. Данное исключение возбуждается, если встречается кодовая позиция, которая отсутствует в кодировке.

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

Попытка выполнения вот такого кода (в файле всё ещё содержится испанский питон):

with open("some_text.txt", "r", encoding="ascii") as file:
    file.read()

даст нам следующий результат:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)

Кодировка ASCII не поддерживает никакой алфавит, кроме английского. Поэтому декодирование символа «ó» вызывает у ASCII сложности. Однако Python всемогущ и есть механизм, который позволяет обработать ошибки кодировок. Это дополнительный параметр методов encode и decode — параметр errors. Он может принимать следующие значения:

Для обеих функций:

Обозначение

Суть

strict

Значение по умолчанию. Несоотвествующие кодировке символы возбуждают исключения UnicodeError и наследуемые от него.

ignore

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

Только для метода encode:

Обозначение

Суть

replace

Несоотвествующие символы заменяются на символ ?

xmlcharrefreplace

Несоответствующие символы заменяются на соответсвующие значения XML.

backslashreplace

Несоответствующие символы заменяются на определённые последовательности с обратным слэшем.

namereplace

Несоответствующие символы заменяются на имена этих символов, которые берутся из базы данных Unicode.

Также отдельно выделены значения surrogatepass и surrogateescape.

Приведём пример использования таких обработчиков:

>>> text = "pitón"
>>> text.encode("ascii", errors="ignore")
b'pitn'
>>> text.encode("ascii", errors="replace")
b'pit?n'
>>> text.encode("ascii", errors="xmlcharrefreplace")
b'pitón'
>>> text.encode("ascii", errors="backslashreplace")
b'pit\xf3n'
>>> text.encode("ascii", errors="namereplace")
b'pit\N{LATIN SMALL LETTER O WITH ACUTE}n'

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

Cворачивание регистра

Сворачивание регистра — это попытка унифицировать текст любого представления к канонической форме. Например, приведение всего текста в нижний регистр. Также над текстом производятся некоторые преобразования (например, немецкая «эсцет» — «ß» — преобразуется в «ss»). В Python 3.3 появился метод str.casefold(), который как раз выполняет сворачивание регистра. Если текст содержит только символы кодировки latin1, результат применения этого метода будет аналогичен методу str.lower().

И по классике приведём пример:

>>> text = "Die größte Stadt der Welt liegt in China"
>>> text.casefold()
'die grösste stadt der welt liegt in china'

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

Важный поинт: привести текст можно не только методом str.lower(), но и методом str.casefold(), который может выполнить дополнительные преобразования текста.

Нормализация

Нормализация — это полноценное приведение текста к единому представлению.

Чтобы обозначить важность нормализации, приведём простой пример:

letter1 = "µ"
letter2 = "μ"

Внешне два этих символа выглядят абсолютно одинаково. Однако если мы попытаемся вывести имена этих символов, как их видит интерпретатор Python’a, результат нас порядком удивит.

В Python есть отличный встроенный модуль, который содержит данные о символах Unicode, их имена, являются ли они цифрамии и т.п. (методы по типу str.isdigit() берут информацию из этих данных). Воспользуемся данным модулем, чтобы вывести имена символов, исходя из информации, которая содержится в базе данных Unicode.

import unicodedata

letter1 = "µ"
letter2 = "μ"
print(unicodedata.name(letter1))
print(unicodedata.name(letter2))

Результат выполнения данного кода:

C:myhabrTextsInPython> python .script7.py
MICRO SIGN
GREEK SMALL LETTER MU

Итак, интерпретатор Python’a видит эти символы как два разных, но в стандарте Unicode они имеют одинаковое отображение.Такие символы называют каноническими эквивалентами. Приложения будут считать два этих символа одинаковыми, но не интерпретатор.

Посмотрим на ещё один пример:

>>> s1 = 'café'
>>> s2 = 'cafeu0301'
>>> s1, s2
('café', 'café')
>>> s1 == s2
False
>>> len(s1), len(s2)
(4, 5)

Данные символы также будут являться каноническими эквивалентами. Из примера мы видим, что символ «é» в стандарте Unicodeможет быть представлен двумя способами, которые к тому же имеют разную длину. Символ «é» может быть представлен одним или двумя байтами.

Решением таких конфликтов занимается нормализация. Она реализована в Python в функции unicodedata.normalize.Первым аргумент является так называемая форма нормализации — нормализации строк Unicode, которые позволяют определить, эквивалентны ли какие-либо две строки Unicode друг другу. Всего предлагается четыре формы:

Форма

Описание

Normalization Form D (NFD)

Canonical Decomposition

Normalization Form C (NFC)

Canonical Decomposition, следующая за Canonical Composition

Normalization Form KD (NFKD)

Compatibility Decomposition

Normalization Form KC (NFKC)

Compatibility Decomposition, следующая за Canonical Composition

Разберём каждую форму немного подробнее.

  • NFC

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

>>> unicodedata.normalize("NFC", s1), unicodedata.normalize("NFC", s2)
('café', 'café')
>>> len(unicodedata.normalize("NFC", s1)), len(unicodedata.normalize("NFC", s2))
(4, 4)
>>> unicodedata.normalize("NFC", s1) == unicodedata.normalize("NFC", s2)
True
>>> len(unicodedata.normalize("NFC", s1)) == len(unicodedata.normalize("NFC", s2))
True

Итак, нормализация обеих строк внешне их не изменила, однако длина строки s2 стала равной 4 (т.е. на один байт меньше). Была произведена композиция байтов eu0301, которые являлись отображением «é». Данная последовательность была заменена на минимальное представление символа, т.е. теперь представление этого символа для интерпретатора выглядит как в строке s1. Как результат, мы видим, что длина нормализованных строк стала равной, и сами строки также стали равны.

  • NFD

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

>>> unicodedata.normalize("NFD", s1), unicodedata.normalize("NFD", s2)
('café', 'café')
>>> len(unicodedata.normalize("NFD", s1)), len(unicodedata.normalize("NFD", s2))
(5, 5)
>>> unicodedata.normalize("NFD", s1) == unicodedata.normalize("NFD", s2)
True
>>> len(unicodedata.normalize("NFD", s1)) == len(unicodedata.normalize("NFD", s2))
True

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

На данном этапе настал момент ввести понятие символа совместимости. Символы совместимости (compatibility characters) были введены в Unicode ради совместимости с другими стандартами, в частности, стандарты, которые предшествовали Unicode. Это означает, что некоторые символы могут встречаться в стандарте несколько раз. Мы уже могли наблюдать это явление в начале этого раздела на примере с символом «мю». Он считается символом совместимости.

  • NFKC и NFKD

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

Немного модифицируем наш пример из начала раздела. Выведем кодовые позиции символов до и после нормализации:

import unicodedata

letter1 = "µ"
letter2 = "μ"
print("Before normalizing:", ord(letter1), ord(letter2))
letter1 = unicodedata.normalize("NFKC", letter1)
letter2 = unicodedata.normalize("NFKC", letter2)
print("After normalizing:", ord(letter1), ord(letter2))

И результат выполнения кода:

Before normalizing: 181 956
After normalizing: 956 956

Итак, мы видим, что первый символ (который являлся знаком «микро») был заменён на греческую «мю», т.е. более предпочтительное представление символа. Таким образом, если необходимо, например, провести частотный анализ текста, формы нормализации, которые затрагивают символы совместимости, могут помочь с этим, приводя символы совместимости к единому представлению.

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

Дополнительные материалы: что использовалось в статье и что почитать по теме

«Fluent Python», Лучано Ромальо

В этой книге целая глава посвящена изучению строк, байтов и Unicode (Глава 4. Тексты и байты). Она есть на русском и английском языках, но в русском переводе допущено немало ошибок, так что открывайте русский вариант на свой страх и риск. Материал статьи в большей степени опирается на данную книгу. Некоторые примеры также взяты оттуда.

Документация для Unicode на официальном сайте Python

Куда ж без неё, родимой. Там тоже можно найти немало полезной информации, если вам понадобится работать с текстами и делать больше, чем просто считывание из файла. Хотя в некоторых случаях и на этом можно споткнуться.

Unicode® Standard Annex

Это части стандарта Unicode, которые выложены в открытый доступ в виде отдельных статей. Почитать их можно вот здесь.

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

Синтаксис обработки исключений

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

Ошибку нельзя обработать, а исключения Python обрабатываются при выполнении программы. Ошибка может быть синтаксической, но существует и много видов исключений, которые возникают при выполнении и не останавливают программу сразу же. Ошибка может указывать на критические проблемы, которые приложение и не должно перехватывать, а исключения — состояния, которые стоит попробовать перехватить. Ошибки — вид непроверяемых и невозвратимых ошибок, таких как OutOfMemoryError, которые не стоит пытаться обработать.

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

Ошибки могут быть разных видов:

  • Синтаксические
  • Недостаточно памяти
  • Ошибки рекурсии
  • Исключения

Разберем их по очереди.

Синтаксические ошибки (SyntaxError)

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

Рассмотрим на примере.

a = 8
b = 10
c = a b
File "", line 3
 c = a b
       ^
SyntaxError: invalid syntax

Стрелка вверху указывает на место, где интерпретатор получил ошибку при попытке исполнения. Знак перед стрелкой указывает на причину проблемы. Для устранения таких фундаментальных ошибок Python будет делать большую часть работы за программиста, выводя название файла и номер строки, где была обнаружена ошибка.

Недостаточно памяти (OutofMemoryError)

Ошибки памяти чаще всего связаны с оперативной памятью компьютера и относятся к структуре данных под названием “Куча” (heap). Если есть крупные объекты (или) ссылки на подобные, то с большой долей вероятности возникнет ошибка OutofMemory. Она может появиться по нескольким причинам:

  • Использование 32-битной архитектуры Python (максимальный объем выделенной памяти невысокий, между 2 и 4 ГБ);
  • Загрузка файла большого размера;
  • Запуск модели машинного обучения/глубокого обучения и много другое;

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

Но поскольку Python использует архитектуру управления памятью из языка C (функция malloc()), не факт, что все процессы восстановятся — в некоторых случаях MemoryError приведет к остановке. Следовательно, обрабатывать такие ошибки не рекомендуется, и это не считается хорошей практикой.

Ошибка рекурсии (RecursionError)

Эта ошибка связана со стеком и происходит при вызове функций. Как и предполагает название, ошибка рекурсии возникает, когда внутри друг друга исполняется много методов (один из которых — с бесконечной рекурсией), но это ограничено размером стека.

Все локальные переменные и методы размещаются в стеке. Для каждого вызова метода создается стековый кадр (фрейм), внутрь которого помещаются данные переменной или результат вызова метода. Когда исполнение метода завершается, его элемент удаляется.

Чтобы воспроизвести эту ошибку, определим функцию recursion, которая будет рекурсивной — вызывать сама себя в бесконечном цикле. В результате появится ошибка StackOverflow или ошибка рекурсии, потому что стековый кадр будет заполняться данными метода из каждого вызова, но они не будут освобождаться.

def recursion():
    return recursion()

recursion()
---------------------------------------------------------------------------

RecursionError                            Traceback (most recent call last)

 in 
----> 1 recursion()


 in recursion()
      1 def recursion():
----> 2     return recursion()


... last 1 frames repeated, from the frame below ...


 in recursion()
      1 def recursion():
----> 2     return recursion()


RecursionError: maximum recursion depth exceeded

Ошибка отступа (IndentationError)

Эта ошибка похожа по духу на синтаксическую и является ее подвидом. Тем не менее она возникает только в случае проблем с отступами.

Пример:

for i in range(10):
    print('Привет Мир!')
  File "", line 2
    print('Привет Мир!')
        ^
IndentationError: expected an indented block

Исключения

Даже если синтаксис в инструкции или само выражение верны, они все равно могут вызывать ошибки при исполнении. Исключения Python — это ошибки, обнаруживаемые при исполнении, но не являющиеся критическими. Скоро вы узнаете, как справляться с ними в программах Python. Объект исключения создается при вызове исключения Python. Если скрипт не обрабатывает исключение явно, программа будет остановлена принудительно.

Программы обычно не обрабатывают исключения, что приводит к подобным сообщениям об ошибке:

Ошибка типа (TypeError)

a = 2
b = 'PythonRu'
a + b
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in 
      1 a = 2
      2 b = 'PythonRu'
----> 3 a + b


TypeError: unsupported operand type(s) for +: 'int' and 'str'

Ошибка деления на ноль (ZeroDivisionError)

10 / 0
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

 in 
----> 1 10 / 0


ZeroDivisionError: division by zero

Есть разные типы исключений в Python и их тип выводится в сообщении: вверху примеры TypeError и ZeroDivisionError. Обе строки в сообщениях об ошибке представляют собой имена встроенных исключений Python.

Оставшаяся часть строки с ошибкой предлагает подробности о причине ошибки на основе ее типа.

Теперь рассмотрим встроенные исключения Python.

Встроенные исключения

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

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

  • Try: он запускает блок кода, в котором ожидается ошибка.
  • Except: здесь определяется тип исключения, который ожидается в блоке try (встроенный или созданный).
  • Else: если исключений нет, тогда исполняется этот блок (его можно воспринимать как средство для запуска кода в том случае, если ожидается, что часть кода приведет к исключению).
  • Finally: вне зависимости от того, будет ли исключение или нет, этот блок кода исполняется всегда.

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

Ошибка прерывания с клавиатуры (KeyboardInterrupt)

Исключение KeyboardInterrupt вызывается при попытке остановить программу с помощью сочетания Ctrl + C или Ctrl + Z в командной строке или ядре в Jupyter Notebook. Иногда это происходит неумышленно и подобная обработка поможет избежать подобных ситуаций.

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

try:
    inp = input()
    print('Нажмите Ctrl+C и прервите Kernel:')
except KeyboardInterrupt:
    print('Исключение KeyboardInterrupt')
else:
    print('Исключений не произошло')

Исключение KeyboardInterrupt

Стандартные ошибки (StandardError)

Рассмотрим некоторые базовые ошибки в программировании.

Арифметические ошибки (ArithmeticError)

  • Ошибка деления на ноль (Zero Division);
  • Ошибка переполнения (OverFlow);
  • Ошибка плавающей точки (Floating Point);

Все перечисленные выше исключения относятся к классу Arithmetic и вызываются при ошибках в арифметических операциях.

Деление на ноль (ZeroDivisionError)

Когда делитель (второй аргумент операции деления) или знаменатель равны нулю, тогда результатом будет ошибка деления на ноль.

try:  
    a = 100 / 0
    print(a)
except ZeroDivisionError:  
    print("Исключение ZeroDivisionError." )
else:  
    print("Успех, нет ошибок!")
Исключение ZeroDivisionError.

Переполнение (OverflowError)

Ошибка переполнение вызывается, когда результат операции выходил за пределы диапазона. Она характерна для целых чисел вне диапазона.

try:  
    import math
    print(math.exp(1000))
except OverflowError:  
    print("Исключение OverFlow.")
else:  
    print("Успех, нет ошибок!")
Исключение OverFlow.

Ошибка утверждения (AssertionError)

Когда инструкция утверждения не верна, вызывается ошибка утверждения.

Рассмотрим пример. Предположим, есть две переменные: a и b. Их нужно сравнить. Чтобы проверить, равны ли они, необходимо использовать ключевое слово assert, что приведет к вызову исключения Assertion в том случае, если выражение будет ложным.

try:  
    a = 100
    b = "PythonRu"
    assert a == b
except AssertionError:  
    print("Исключение AssertionError.")
else:  
    print("Успех, нет ошибок!")

Исключение AssertionError.

Ошибка атрибута (AttributeError)

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

class Attributes(obj):
    a = 2
    print(a)

try:
    obj = Attributes()
    print(obj.attribute)
except AttributeError:
    print("Исключение AttributeError.")

2
Исключение AttributeError.

Ошибка импорта (ModuleNotFoundError)

Ошибка импорта вызывается при попытке импортировать несуществующий (или неспособный загрузиться) модуль в стандартном пути или даже при допущенной ошибке в имени.

import nibabel
---------------------------------------------------------------------------

ModuleNotFoundError                       Traceback (most recent call last)

 in 
----> 1 import nibabel


ModuleNotFoundError: No module named 'nibabel'

Ошибка поиска (LookupError)

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

Здесь есть два вида исключений:

  • Ошибка индекса (IndexError);
  • Ошибка ключа (KeyError);

Ошибка ключа

Если ключа, к которому нужно получить доступ, не оказывается в словаре, вызывается исключение KeyError.

try:  
    a = {1:'a', 2:'b', 3:'c'}  
    print(a[4])  
except LookupError:  
    print("Исключение KeyError.")
else:  
    print("Успех, нет ошибок!")

Исключение KeyError.

Ошибка индекса

Если пытаться получить доступ к индексу (последовательности) списка, которого не существует в этом списке или находится вне его диапазона, будет вызвана ошибка индекса (IndexError: list index out of range python).

try:
    a = ['a', 'b', 'c']  
    print(a[4])  
except LookupError:  
    print("Исключение IndexError, индекс списка вне диапазона.")
else:  
    print("Успех, нет ошибок!")
Исключение IndexError, индекс списка вне диапазона.

Ошибка памяти (MemoryError)

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

Ошибка имени (NameError)

Ошибка имени возникает, когда локальное или глобальное имя не находится.

В следующем примере переменная ans не определена. Результатом будет ошибка NameError.

try:
    print(ans)
except NameError:  
    print("NameError: переменная 'ans' не определена")
else:  
    print("Успех, нет ошибок!")
NameError: переменная 'ans' не определена

Ошибка выполнения (Runtime Error)

Ошибка «NotImplementedError»
Ошибка выполнения служит базовым классом для ошибки NotImplemented. Абстрактные методы определенного пользователем класса вызывают это исключение, когда производные методы перезаписывают оригинальный.

class BaseClass(object):
    """Опередляем класс"""
    def __init__(self):
        super(BaseClass, self).__init__()
    def do_something(self):
	# функция ничего не делает
        raise NotImplementedError(self.__class__.__name__ + '.do_something')

class SubClass(BaseClass):
    """Реализует функцию"""
    def do_something(self):
        # действительно что-то делает
        print(self.__class__.__name__ + ' что-то делает!')

SubClass().do_something()
BaseClass().do_something()

SubClass что-то делает!



---------------------------------------------------------------------------

NotImplementedError                       Traceback (most recent call last)

 in 
     14
     15 SubClass().do_something()
---> 16 BaseClass().do_something()


 in do_something(self)
      5     def do_something(self):
      6         # функция ничего не делает
----> 7         raise NotImplementedError(self.__class__.__name__ + '.do_something')
      8
      9 class SubClass(BaseClass):


NotImplementedError: BaseClass.do_something

Ошибка типа (TypeError)

Ошибка типа вызывается при попытке объединить два несовместимых операнда или объекта.

В примере ниже целое число пытаются добавить к строке, что приводит к ошибке типа.

try:
    a = 5
    b = "PythonRu"
    c = a + b
except TypeError:
    print('Исключение TypeError')
else:
    print('Успех, нет ошибок!')

Исключение TypeError

Ошибка значения (ValueError)

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

В этом примере встроенная операция float получат аргумент, представляющий собой последовательность символов (значение), что является недопустимым значением для типа: число с плавающей точкой.

try:
    print(float('PythonRu'))
except ValueError:
    print('ValueError: не удалось преобразовать строку в float: 'PythonRu'')
else:
    print('Успех, нет ошибок!')
ValueError: не удалось преобразовать строку в float: 'PythonRu'

Пользовательские исключения в Python

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

Это можно сделать, создав новый класс, который будет наследовать из класса Exception в Python.

class UnAcceptedValueError(Exception):   
    def __init__(self, data):    
        self.data = data
    def __str__(self):
        return repr(self.data)

Total_Marks = int(input("Введите общее количество баллов: "))
try:
    Num_of_Sections = int(input("Введите количество разделов: "))
    if(Num_of_Sections < 1):
        raise UnAcceptedValueError("Количество секций не может быть меньше 1")
except UnAcceptedValueError as e:
    print("Полученная ошибка:", e.data)

Введите общее количество баллов: 10
Введите количество разделов: 0
Полученная ошибка: Количество секций не может быть меньше 1

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

Недостатки обработки исключений в Python

У использования исключений есть свои побочные эффекты, как, например, то, что программы с блоками try-except работают медленнее, а количество кода возрастает.

Дальше пример, где модуль Python timeit используется для проверки времени исполнения 2 разных инструкций. В stmt1 для обработки ZeroDivisionError используется try-except, а в stmt2if. Затем они выполняются 10000 раз с переменной a=0. Суть в том, чтобы показать разницу во времени исполнения инструкций. Так, stmt1 с обработкой исключений занимает больше времени чем stmt2, который просто проверяет значение и не делает ничего, если условие не выполнено.

Поэтому стоит ограничить использование обработки исключений в Python и применять его в редких случаях. Например, когда вы не уверены, что будет вводом: целое или число с плавающей точкой, или не уверены, существует ли файл, который нужно открыть.

import timeit
setup="a=0"
stmt1 = '''
try:
    b=10/a
except ZeroDivisionError:
    pass'''

stmt2 = '''
if a!=0:
    b=10/a'''

print("time=",timeit.timeit(stmt1,setup,number=10000))
print("time=",timeit.timeit(stmt2,setup,number=10000))

time= 0.003897680000136461
time= 0.0002797570000439009

Выводы!

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

Обработка исключений — один из основных факторов, который делает код готовым к развертыванию. Это простая концепция, построенная всего на 4 блоках: try выискивает исключения, а except их обрабатывает.

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

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

  1. Синтаксические ошибки
  2. Логические ошибки

При возникновении, какой либо ошибки, которую программа не может обработать, вызывается тип ошибки указывающая на проблему. Избежать эти ошибки поможет нам обработка исключений в Python.

Такого рода ошибка возникает при неправильном использовании структуры языка. Рассмотрим пример такой ошибки:

for i in range(0,10)

print(i)

Результат:

SyntaxError: invalid syntax

Естественно, мы нарушили структура языка, а именно не поставили двоеточие в конце цикла for.

Логические ошибки в Python

Логические ошибка возникают, если у вас нет синтаксической ошибки. То есть интерпретатор Python, сначала анализирует ваш код на предмет наличия синтаксических ошибок. типы логических ошибок бывают разными, например:

  1. Если мы пытаемся открыть файл для чтения которого не существует, то словим ошибку FileNotFoundError
  2. Попытаемся разделить на ноль, получим ZeroDivisionError
  3. Попытаемся импортировать модуль, которого не существует, получим ImportError

И это только малая часть, Python не разрешит нам уничтожить мир, разделив число на ноль=).  Каждый раз, когда Python видит необработанную ошибку, он пишет нам, что произошла ошибка по той или иной причине. Рассмотрим пример обработки ошибки.

100 / 0

Результат:

Traceback (most recent call last):

File "<string>", line 301, in runcode

File "<interactive input>", line 1, in <module>

ZeroDivisionError: division by zero

&nbsp;

open("filenot.txt")

Результат:

Traceback (most recent call last):

File "<string>", line 301, in runcode

File "<interactive input>", line 1, in <module>

FileNotFoundError: [Errno 2]

No such file or directory: 'filenot.txt'

Исключения Python

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

Исключения Причина
AssertionError Вызывается при сбое assert оператора.
AttributeError Возникает, когда не удается присвоить атрибут или ссылку.
EOFError Вызывается, когда input()функция достигает состояния конца файла.
FloatingPointError Вызывается при сбое операции с числами имеющими десятичное значение.
GeneratorExit при close()вызове метода генератора.
ImportError Вызывается, когда импортированный модуль не найден.
IndexError когда индекс последовательности находится вне диапазона.
KeyError Возникает, когда ключ не найден в словаре.
KeyboardInterrupt При нажатии на клавишу прерывания
MemoryError Вызывается, когда в операции заканчивается память.
NameError Вызывается, когда переменная не найдена в локальной или глобальной области видимости.
NotImplementedError При работе с абстрактными методами
OSError Когда программа вызывает связанную с ОС ошибку
OverflowError Возникает, когда результат арифметической операции слишком велик
ReferenceError Вызывается, когда слабый ссылочный прокси-сервер используется для доступа к собранному мусором референту.
RuntimeError Возникает, когда ошибка не попадает ни под какую другую категорию.
StopIteration Вызывается функцией next(), которая говорит, что больше нет элементов
SyntaxError Синтаксическая ошибка
IndentationError При нарушении отступа
TabError При нарушении отступов
SystemError Ошибка с интепретатором
SystemExit Функция sys.exit()
TypeError Ошибка с типами данных
UnboundLocalError Вызывается, когда делается ссылка на локальную переменную в функции или методе, но ни одно значение не было привязано к этой переменной.
UnicodeError Ошибка кодировки
UnicodeEncodeError Ошибка кодировки
UnicodeDecodeError Ошибка декодирования
UnicodeTranslateError Ошибка кодировки
ValueError Когда функция получает аргумент с неправильным значением
ZeroDivisionError При делении на ноль

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

В одной из предыдущих статей, мы с вами уже затрагивали тему обработки ошибок, с помощью try…except…finally, подробнее о них, в статье обработка исключений

Ошибки и исключения

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

my_string = "Привет, мир.
Traceback (most recent call last):
  File error.py, line 1
    my_string = "Привет, мир.
                             ^
SyntaxError: EOL while scanning string literal

Это сообщение указывает, что ваша программа содержит синтаксическую ошибку. Синтаксические ошибки фатальны; программа, содержащая их, не может работать. Когда вы пытаетесь запустить программу с синтаксической ошибкой, Python сообщает об этом.

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

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

У Python есть два типа ошибок: синтаксические ошибки и исключения. Исключением называется любая ошибка, которая не является синтаксической. В отличие от синтаксических ошибок, исключения необязательно фатальны.

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

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

Обработка исключений

Предположим, вы написали программу, которая принимает от пользователя два числа и выводит результат деления первого числа на второе:

a = input("Введите число: ")
b = input("Введите еще одно число: ")
a = int(a)
b = int(b)
print (a / b)

Программа будет работать. Однако если пользователь введет 0 в качестве
второго числа, вы столкнетесь с ошибкой:

Traceback (most recent call last):
  module division.py line 5
ZeroDivisionError: division by zero

Нельзя просто надеяться, что пользователь не введет 0 и отличным способом
выйти из положения может стать обработка исключений — инструмент, позволяющий «перехватить» исключения, если таковые возникают, и решить, что делать дальше.

Для обработки исключений используются ключевые слова try и except. При использовании обработки исключений, когда пользователь вторым числом вводит 0, программа может вывести им сообщение о недопустимости ввода нуля.

Все исключения в Python являются объектами, так что вы можете использовать их в своих программах. Список встроенных исключений можно найти здесь. Если вам кажется, что ваш код может сгенерировать исключение, используйте составную инструкцию с ключевыми словами try и except, чтобы перехватить его.

Блок try содержит код, который может сгенерировать исключение. Блок except содержит код, который будет выполняться лишь в том случае, если внутри блока try появится исключение. Ниже приведен пример использования обработки исключений, когда программа не прерывается при вводе 0 в качестве второго числа.

try:
    a = input("Введите число: ")
    b = input("Введите еще одно число: ")
    a = int(a)
    b = int(b)
    print (a / b)
except ZeroDivisionError:
    print("b не может быть нулем!")

Такая программа перехватит исключение ZeroDivisionError и передаст управление в блок except, в котором мы и выведем сообщение о недопустимости ввода 0.

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

Traceback (most recent call last):
  module division.py line 3
ValueError: invalid literal for int() with base 10: 'слово'

Это можно исправить, перехватив оба типа исключений

try:
    a = input("Введите число: ")
    b = input("Введите еще одно число: ")
    a = int(a)
    b = int(b)
    print (a / b)
except ZeroDivisionError:
    print("b не может быть нулем!")
except ValueError:
    print("Ошибка ввода числа")

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

try:
    10 / 0
    c = "Я никогда не определюсь."
except ZeroDivisionError:
    print(c)

Такая программа закончится с ошибкой

Traceback (most recent call last):
  module zero.py line 5
NameError: name 'c' is not defined

Python известен своим простым синтаксисом. Однако, когда вы изучаете Python в первый раз или когда вы попали на Python с большим опытом работы на другом языке программирования, вы можете столкнуться с некоторыми вещами, которые Python не позволяет. Если вы когда-либо получали + SyntaxError + при попытке запустить код Python, то это руководство может вам помочь. В этом руководстве вы увидите общие примеры неправильного синтаксиса в Python и узнаете, как решить эту проблему.

Неверный синтаксис в Python

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

Когда вы изучаете Python в первый раз, может быть неприятно получить + SyntaxError +. Python попытается помочь вам определить, где в вашем коде указан неверный синтаксис, но предоставляемый им traceback может немного сбить с толку. Иногда код, на который он указывает, вполне подходит.

*Примечание:* Если ваш код *синтаксически* правильный, то вы можете получить другие исключения, которые не являются `+ SyntaxError +`. Чтобы узнать больше о других исключениях Python и о том, как их обрабатывать, ознакомьтесь с https://realpython.com/python-exceptions/[Python Exceptions: Введение].

Вы не можете обрабатывать неправильный синтаксис в Python, как и другие исключения. Даже если вы попытаетесь обернуть блок + try + и + кроме + вокруг кода с неверным синтаксисом, вы все равно увидите, что интерпретатор вызовет + SyntaxError +.

+ SyntaxError + Исключение и трассировка

Когда интерпретатор обнаруживает неверный синтаксис в коде Python, он вызовет исключение + SyntaxError + и предоставит трассировку с некоторой полезной информацией, которая поможет вам отладить ошибку. Вот некоторый код, который содержит недопустимый синтаксис в Python:

 1 # theofficefacts.py
 2 ages = {
 3     'pam': 24,
 4     'jim': 24
 5     'michael': 43
 6 }
 7 print(f'Michael is {ages["michael"]} years old.')

Вы можете увидеть недопустимый синтаксис в литерале словаря в строке 4. Во второй записи + 'jim' + пропущена запятая. Если вы попытаетесь запустить этот код как есть, вы получите следующую трассировку:

$ python theofficefacts.py
File "theofficefacts.py", line 5
    'michael': 43
            ^
SyntaxError: invalid syntax

Обратите внимание, что сообщение трассировки обнаруживает ошибку в строке 5, а не в строке 4. Интерпретатор Python пытается указать, где находится неправильный синтаксис. Тем не менее, он может только указать, где он впервые заметил проблему. Когда вы получите трассировку + SyntaxError + и код, на который указывает трассировка, выглядит нормально, тогда вы захотите начать движение назад по коду, пока не сможете определить, что не так.

В приведенном выше примере нет проблемы с запятой, в зависимости от того, что следует после нее. Например, нет проблемы с отсутствующей запятой после + 'michael' + в строке 5. Но как только переводчик сталкивается с чем-то, что не имеет смысла, он может лишь указать вам на первое, что он обнаружил, что он не может понять.

*Примечание:* В этом руководстве предполагается, что вы знакомы с основами *tracebacks* в Python. Чтобы узнать больше о трассировке Python и о том, как их читать, ознакомьтесь с https://realpython.com/python-traceback/[Understanding Python Traceback].

Существует несколько элементов трассировки + SyntaxError +, которые могут помочь вам определить, где в вашем коде содержится неверный синтаксис:

  • Имя файла , где встречается неверный синтаксис

  • Номер строки и воспроизводимая строка кода, где возникла проблема

  • Знак (+ ^ +) в строке ниже воспроизводимого кода, который показывает точку в коде, которая имеет проблему

  • Сообщение об ошибке , которое следует за типом исключения + SyntaxError +, которое может предоставить информацию, которая поможет вам определить проблему

В приведенном выше примере имя файла было + theofficefacts.py +, номер строки был 5, а каретка указывала на закрывающую кавычку из словарного ключа + michael +. Трассировка + SyntaxError + может не указывать на реальную проблему, но она будет указывать на первое место, где интерпретатор не может понять синтаксис.

Есть два других исключения, которые вы можете увидеть в Python. Они эквивалентны + SyntaxError +, но имеют разные имена:

  1. + + IndentationError

  2. + + TabError

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

Общие проблемы с синтаксисом

Когда вы впервые сталкиваетесь с + SyntaxError +, полезно знать, почему возникла проблема и что вы можете сделать, чтобы исправить неверный синтаксис в вашем коде Python. В следующих разделах вы увидите некоторые из наиболее распространенных причин, по которым может быть вызвано «+ SyntaxError +», и способы их устранения.

Неправильное использование оператора присваивания (+ = +)

В Python есть несколько случаев, когда вы не можете назначать объекты. Некоторые примеры присваивают литералам и вызовам функций. В приведенном ниже блоке кода вы можете увидеть несколько примеров, которые пытаются это сделать, и получающиеся в результате трассировки + SyntaxError +:

>>>

>>> len('hello') = 5
  File "<stdin>", line 1
SyntaxError: can't assign to function call

>>> 'foo' = 1
  File "<stdin>", line 1
SyntaxError: can't assign to literal

>>> 1 = 'foo'
  File "<stdin>", line 1
SyntaxError: can't assign to literal

Первый пример пытается присвоить значение + 5 + вызову + len () +. Сообщение + SyntaxError + очень полезно в этом случае. Он говорит вам, что вы не можете присвоить значение вызову функции.

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

*Примечание:* В приведенных выше примерах отсутствует повторяющаяся строка кода и каретка (`+ ^ +`), указывающая на проблему в трассировке. Исключение и обратная трассировка, которые вы видите, будут другими, когда вы находитесь в REPL и пытаетесь выполнить этот код из файла. Если бы этот код был в файле, то вы бы получили повторяющуюся строку кода и указали на проблему, как вы видели в других случаях в этом руководстве.

Вероятно, ваше намерение не состоит в том, чтобы присвоить значение литералу или вызову функции. Например, это может произойти, если вы случайно пропустите дополнительный знак равенства (+ = +), что превратит назначение в сравнение. Сравнение, как вы можете видеть ниже, будет правильным:

>>>

>>> len('hello') == 5
True

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

Неправильное написание, отсутствие или неправильное использование ключевых слов Python

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

Существует три распространенных способа ошибочного использования ключевых слов:

  1. Неправильное написание ключевое слово

  2. Отсутствует ключевое слово

  3. Неправильное использование ключевого слова

Если вы неправильно написали ключевое слово в своем коде Python, вы получите + SyntaxError +. Например, вот что происходит, если вы пишете ключевое слово + for + неправильно:

>>>

>>> fro i in range(10):
  File "<stdin>", line 1
    fro i in range(10):
        ^
SyntaxError: invalid syntax

Сообщение читается как + SyntaxError: неверный синтаксис +, но это не очень полезно. Трассировка указывает на первое место, где Python может обнаружить, что что-то не так. Чтобы исправить эту ошибку, убедитесь, что все ваши ключевые слова Python написаны правильно.

Другая распространенная проблема с ключевыми словами — это когда вы вообще их пропускаете:

>>>

>>> for i range(10):
  File "<stdin>", line 1
    for i range(10):
              ^
SyntaxError: invalid syntax

Еще раз, сообщение об исключении не очень полезно, но трассировка действительно пытается указать вам правильное направление. Если вы отойдете от каретки, то увидите, что ключевое слово + in + отсутствует в синтаксисе цикла + for +.

Вы также можете неправильно использовать защищенное ключевое слово Python. Помните, что ключевые слова разрешено использовать только в определенных ситуациях. Если вы используете их неправильно, у вас будет неправильный синтаксис в коде Python. Типичным примером этого является использование https://realpython.com/python-for-loop/#the-break-and-continue-statements [+ continue + или + break +] вне цикла. Это может легко произойти во время разработки, когда вы реализуете вещи и когда-то перемещаете логику за пределы цикла:

>>>

>>> names = ['pam', 'jim', 'michael']
>>> if 'jim' in names:
...     print('jim found')
...     break
...
  File "<stdin>", line 3
SyntaxError: 'break' outside loop

>>> if 'jim' in names:
...     print('jim found')
...     continue
...
  File "<stdin>", line 3
SyntaxError: 'continue' not properly in loop

Здесь Python отлично говорит, что именно не так. Сообщения " 'break' вне цикла " и " 'continue' не в цикле должным образом " помогут вам точно определить, что делать. Если бы этот код был в файле, то Python также имел бы курсор, указывающий прямо на неправильно использованное ключевое слово.

Другой пример — если вы пытаетесь назначить ключевое слово Python переменной или использовать ключевое слово для определения функции:

>>>

>>> pass = True
  File "<stdin>", line 1
    pass = True
         ^
SyntaxError: invalid syntax

>>> def pass():
  File "<stdin>", line 1
    def pass():
           ^
SyntaxError: invalid syntax

Когда вы пытаетесь присвоить значение + pass +, или когда вы пытаетесь определить новую функцию с именем + pass +, вы получите ` + SyntaxError + и снова увидеть сообщение + «неверный синтаксис» + `.

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

Список защищенных ключевых слов менялся с каждой новой версией Python. Например, в Python 3.6 вы можете использовать + await + в качестве имени переменной или имени функции, но в Python 3.7 это слово было добавлено в список ключевых слов. Теперь, если вы попытаетесь использовать + await + в качестве имени переменной или функции, это вызовет + SyntaxError +, если ваш код для Python 3.7 или более поздней версии.

Другим примером этого является + print +, который отличается в Python 2 от Python 3:

Version print Type Takes A Value

Python 2

keyword

no

Python 3

built-in function

yes

+ print + — это ключевое слово в Python 2, поэтому вы не можете присвоить ему значение. Однако в Python 3 это встроенная функция, которой можно присваивать значения.

Вы можете запустить следующий код, чтобы увидеть список ключевых слов в любой версии Python, которую вы используете:

import keyword
print(keyword.kwlist)

+ keyword + также предоставляет полезную + keyword.iskeyword () +. Если вам просто нужен быстрый способ проверить переменную + pass +, то вы можете использовать следующую однострочную строку:

>>>

>>> import keyword; keyword.iskeyword('pass')
True

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

Отсутствующие скобки, скобки и цитаты

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

>>>

>>> message = 'don't'
  File "<stdin>", line 1
    message = 'don't'
                   ^
SyntaxError: invalid syntax

Здесь трассировка указывает на неверный код, где после закрывающей одинарной кавычки стоит + t '+. Чтобы это исправить, вы можете сделать одно из двух изменений:

  1. Escape одиночная кавычка с обратной косой чертой (+ 'don ' t '+)

  2. Окружить всю строку в двойных кавычках (" не ")

Другая распространенная ошибка — забыть закрыть строку. Как для строк с двойными, так и с одинарными кавычками ситуация и обратная трассировка одинаковы:

>>>

>>> message = "This is an unclosed string
  File "<stdin>", line 1
    message = "This is an unclosed string
                                        ^
SyntaxError: EOL while scanning string literal

На этот раз каретка в трассировке указывает прямо на код проблемы. Сообщение + SyntaxError +, " EOL при сканировании строкового литерала ", немного более конкретно и полезно при определении проблемы. Это означает, что интерпретатор Python дошел до конца строки (EOL) до закрытия открытой строки. Чтобы это исправить, закройте строку с кавычкой, которая совпадает с той, которую вы использовали для ее запуска. В этом случае это будет двойная кавычка (`+» + `).

Кавычки, отсутствующие в инструкциях внутри f-string, также могут привести к неверному синтаксису в Python:

 1 # theofficefacts.py
 2 ages = {
 3     'pam': 24,
 4     'jim': 24,
 5     'michael': 43
 6 }
 7 print(f'Michael is {ages["michael]} years old.')

Здесь, ссылка на словарь + ages + внутри напечатанной f-строки пропускает закрывающую двойную кавычку из ссылки на ключ. Итоговая трассировка выглядит следующим образом:

$ python theofficefacts.py
  File "theofficefacts.py", line 7
    print(f'Michael is {ages["michael]} years old.')
         ^
SyntaxError: f-string: unterminated string

Python идентифицирует проблему и сообщает, что она существует внутри f-строки. Сообщение " неопределенная строка " также указывает на проблему. Каретка в этом случае указывает только на начало струны.

Это может быть не так полезно, как когда каретка указывает на проблемную область струны, но она сужает область поиска. Где-то внутри этой f-строки есть неопределенная строка. Вы просто должны узнать где. Чтобы решить эту проблему, убедитесь, что присутствуют все внутренние кавычки и скобки f-строки.

Ситуация в основном отсутствует в скобках и скобках. Например, если вы исключите закрывающую квадратную скобку из списка, Python обнаружит это и укажет на это. Однако есть несколько вариантов этого. Первый — оставить закрывающую скобку вне списка:

# missing.py
def foo():
    return [1, 2, 3

print(foo())

Когда вы запустите этот код, вам скажут, что есть проблема с вызовом + print () +:

$ python missing.py
  File "missing.py", line 5
    print(foo())
        ^
SyntaxError: invalid syntax

Здесь происходит то, что Python думает, что список содержит три элемента: + 1 +, + 2 + и +3 print (foo ()) +. Python использует whitespace для логической группировки вещей, и потому что нет запятой или скобки, отделяющей + 3 + от `+ print (foo ()) + `, Python объединяет их вместе как третий элемент списка.

Еще один вариант — добавить запятую после последнего элемента в списке, оставляя при этом закрывающую квадратную скобку:

# missing.py
def foo():
    return [1, 2, 3,

print(foo())

Теперь вы получаете другую трассировку:

$ python missing.py
  File "missing.py", line 6

                ^
SyntaxError: unexpected EOF while parsing

В предыдущем примере + 3 + и + print (foo ()) + были объединены в один элемент, но здесь вы видите запятую, разделяющую два. Теперь вызов + print (foo ()) + добавляется в качестве четвертого элемента списка, и Python достигает конца файла без закрывающей скобки. В трассировке говорится, что Python дошел до конца файла (EOF), но ожидал чего-то другого.

В этом примере Python ожидал закрывающую скобку (+] +), но повторяющаяся строка и каретка не очень помогают. Отсутствующие круглые скобки и скобки сложно определить Python. Иногда единственное, что вы можете сделать, это начать с каретки и двигаться назад, пока вы не сможете определить, чего не хватает или что нет.

Ошибочный синтаксис словаря

Вы видели ссылку: # syntaxerror-exception-and-traceback [ранее], чтобы вы могли получить + SyntaxError +, если не указывать запятую в словарном элементе. Другая форма недопустимого синтаксиса в словарях Python — это использование знака равенства (+ = +) для разделения ключей и значений вместо двоеточия:

>>>

>>> ages = {'pam'=24}
  File "<stdin>", line 1
    ages = {'pam'=24}
                 ^
SyntaxError: invalid syntax

Еще раз, это сообщение об ошибке не очень полезно. Повторная линия и каретка, однако, очень полезны! Они указывают прямо на характер проблемы.

Этот тип проблемы распространен, если вы путаете синтаксис Python с синтаксисом других языков программирования. Вы также увидите это, если перепутаете определение словаря с вызовом + dict () +. Чтобы это исправить, вы можете заменить знак равенства двоеточием. Вы также можете переключиться на использование + dict () +:

>>>

>>> ages = dict(pam=24)
>>> ages
{'pam': 24}

Вы можете использовать + dict () + для определения словаря, если этот синтаксис более полезен.

Использование неправильного отступа

Существует два подкласса + SyntaxError +, которые конкретно занимаются проблемами отступов:

  1. + + IndentationError

  2. + + TabError

В то время как другие языки программирования используют фигурные скобки для обозначения блоков кода, Python использует whitespace. Это означает, что Python ожидает, что пробелы в вашем коде будут вести себя предсказуемо. Он вызовет + IndentationError + , если в блоке кода есть строка с неправильным количеством пробелов:

 1 # indentation.py
 2 def foo():
 3     for i in range(10):
 4         print(i)
 5   print('done')
 6
 7 foo()

Это может быть сложно увидеть, но в строке 5 есть только два пробела с отступом. Он должен соответствовать выражению цикла + for +, которое на 4 пробела больше. К счастью, Python может легко определить это и быстро расскажет вам, в чем проблема.

Здесь также есть некоторая двусмысленность. Является ли строка + print ('done') + after циклом + for + или inside блоком цикла + for +? Когда вы запустите приведенный выше код, вы увидите следующую ошибку:

$ python indentation.py
  File "indentation.py", line 5
    print('done')
                ^
IndentationError: unindent does not match any outer indentation level

Хотя трассировка выглядит во многом как трассировка + SyntaxError +, на самом деле это + IndentationError +. Сообщение об ошибке также очень полезно. Он говорит вам, что уровень отступа строки не соответствует ни одному другому уровню отступа. Другими словами, + print ('done') + это отступ с двумя пробелами, но Python не может найти любую другую строку кода, соответствующую этому уровню отступа. Вы можете быстро это исправить, убедившись, что код соответствует ожидаемому уровню отступа.

Другой тип + SyntaxError + — это + TabError + , который вы будете видеть всякий раз, когда есть строка, содержащая либо табуляцию, либо пробелы для отступа, в то время как остальная часть файла содержит другую. Это может скрыться, пока Python не покажет это вам!

Если размер вкладки равен ширине пробелов на каждом уровне отступа, то может показаться, что все строки находятся на одном уровне. Однако, если одна строка имеет отступ с использованием пробелов, а другая — с помощью табуляции, Python укажет на это как на проблему:

 1 # indentation.py
 2 def foo():
 3     for i in range(10):
 4         print(i)
 5     print('done')
 6
 7 foo()

Здесь строка 5 имеет отступ вместо 4 пробелов. Этот блок кода может выглядеть идеально для вас, или он может выглядеть совершенно неправильно, в зависимости от настроек вашей системы.

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

$ tabs 4 # Sets the shell tab width to 4 spaces
$ cat -n indentation.py
     1   # indentation.py
     2   def foo():
     3       for i in range(10)
     4           print(i)
     5       print('done')
     6
     7   foo()

$ tabs 8 # Sets the shell tab width to 8 spaces (standard)
$ cat -n indentation.py
     1   # indentation.py
     2   def foo():
     3       for i in range(10)
     4           print(i)
     5           print('done')
     6
     7   foo()

$ tabs 3 # Sets the shell tab width to 3 spaces
$ cat -n indentation.py
     1   # indentation.py
     2   def foo():
     3       for i in range(10)
     4           print(i)
     5      print('done')
     6
     7   foo()

Обратите внимание на разницу в отображении между тремя примерами выше. Большая часть кода использует 4 пробела для каждого уровня отступа, но строка 5 использует одну вкладку во всех трех примерах. Ширина вкладки изменяется в зависимости от настройки tab width :

  • Если ширина вкладки равна 4 , то оператор + print + будет выглядеть так, как будто он находится вне цикла + for +. Консоль выведет + 'done' + в конце цикла.

  • Если ширина табуляции равна 8 , что является стандартным для многих систем, то оператор + print + будет выглядеть так, как будто он находится внутри цикла + for +. Консоль будет печатать + 'done' + после каждого числа.

  • Если ширина табуляции равна 3 , то оператор + print + выглядит неуместно. В этом случае строка 5 не соответствует ни одному уровню отступа.

Когда вы запустите код, вы получите следующую ошибку и трассировку:

$ python indentation.py
  File "indentation.py", line 5
    print('done')
                ^
TabError: inconsistent use of tabs and spaces in indentation

Обратите внимание на + TabError + вместо обычного + SyntaxError +. Python указывает на проблемную строку и дает вам полезное сообщение об ошибке. Это ясно говорит о том, что в одном и том же файле для отступа используется смесь вкладок и пробелов.

Решение этой проблемы состоит в том, чтобы все строки в одном и том же файле кода Python использовали либо табуляции, либо пробелы, но не обе. Для приведенных выше блоков кода исправление будет состоять в том, чтобы удалить вкладку и заменить ее на 4 пробела, которые будут печатать + 'done' + после завершения цикла + for +.

Определение и вызов функций

Вы можете столкнуться с неверным синтаксисом в Python, когда вы определяете или вызываете функции. Например, вы увидите + SyntaxError +, если будете использовать точку с запятой вместо двоеточия в конце определения функции:

>>>

>>> def fun();
  File "<stdin>", line 1
    def fun();
             ^
SyntaxError: invalid syntax

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

Кроме того, ключевые аргументы как в определениях функций, так и в вызовах функций должны быть в правильном порядке. Аргументы ключевых слов always идут после позиционных аргументов. Отказ от использования этого порядка приведет к + SyntaxError +:

>>>

>>> def fun(a, b):
...     print(a, b)
...
>>> fun(a=1, 2)
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument

Здесь, еще раз, сообщение об ошибке очень полезно, чтобы рассказать вам точно, что не так со строкой.

Изменение версий Python

Иногда код, который прекрасно работает в одной версии Python, ломается в более новой версии. Это связано с официальными изменениями в синтаксисе языка. Наиболее известным примером этого является оператор + print +, который перешел от ключевого слова в Python 2 к встроенной функции в Python 3:

>>>

>>> # Valid Python 2 syntax that fails in Python 3
>>> print 'hello'
  File "<stdin>", line 1
    print 'hello'
                ^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print('hello')?

Это один из примеров, где появляется сообщение об ошибке, сопровождающее + SyntaxError +! Он не только сообщает вам, что в вызове + print + отсутствует скобка, но также предоставляет правильный код, который поможет вам исправить оператор.

Другая проблема, с которой вы можете столкнуться, — это когда вы читаете или изучаете синтаксис, который является допустимым синтаксисом в более новой версии Python, но недопустим в той версии, в которую вы пишете. Примером этого является синтаксис f-string, которого нет в версиях Python до 3.6:

>>>

>>> # Any version of python before 3.6 including 2.7
>>> w ='world'
>>> print(f'hello, {w}')
  File "<stdin>", line 1
    print(f'hello, {w}')
                      ^
SyntaxError: invalid syntax

В версиях Python до 3.6 интерпретатор ничего не знает о синтаксисе f-строки и просто предоставляет общее сообщение «» неверный синтаксис «`. Проблема, в данном случае, в том, что код looks прекрасно работает, но он был запущен с более старой версией Python. В случае сомнений перепроверьте, какая версия Python у вас установлена!

Синтаксис Python продолжает развиваться, и в Python 3.8 появилось несколько интересных новых функций:

  • Walrus оператор (выражения присваивания)

  • F-string синтаксис для отладки
    *https://docs.python.org/3.8/whatsnew/3.8.html#positional-only-parameters[Positional-only arguments]

Если вы хотите опробовать некоторые из этих новых функций, то вам нужно убедиться, что вы работаете в среде Python 3.8. В противном случае вы получите + SyntaxError +.

Python 3.8 также предоставляет новый* + SyntaxWarning + *. Вы увидите это предупреждение в ситуациях, когда синтаксис допустим, но все еще выглядит подозрительно. Примером этого может быть отсутствие запятой между двумя кортежами в списке. Это будет действительный синтаксис в версиях Python до 3.8, но код вызовет + TypeError +, потому что кортеж не может быть вызван:

>>>

>>> [(1,2)(2,3)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object is not callable

Этот + TypeError + означает, что вы не можете вызывать кортеж, подобный функции, что, как думает интерпретатор Python, вы делаете.

В Python 3.8 этот код все еще вызывает + TypeError +, но теперь вы также увидите + SyntaxWarning +, который указывает, как вы можете решить проблему:

>>>

>>> [(1,2)(2,3)]
<stdin>:1: SyntaxWarning: 'tuple' object is not callable; perhaps you missed a comma?
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object is not callable

Полезное сообщение, сопровождающее новый + SyntaxWarning +, даже дает подсказку (" возможно, вы пропустили запятую? "), Чтобы указать вам правильное направление!

Заключение

В этом руководстве вы увидели, какую информацию предоставляет обратная связь + SyntaxError +. Вы также видели много распространенных примеров неправильного синтаксиса в Python и каковы решения этих проблем. Это не только ускорит ваш рабочий процесс, но и сделает вас более полезным рецензентом кода!

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

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

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Python ошибка при установке пакетов
  • Python ошибка при установке 0x80072f7d