Как исправить синтаксическую ошибку в питоне
Как чинить SyntaxError
SyntaxError — это ошибка, которая легко может ввести в ступор начинающего программиста. Стоит забыть одну запятую или не там поставить кавычку и Python наотрез откажется запускать программу. Что ещё хуже, по выводу в консоль сложно сообразить в чём дело. Выглядят сообщения страшно и непонятно. Что с этим делать — не ясно. Вот неполный список того, что можно встретить:
Работать будем с программой, которая выводит на экран список учеников. Её код выглядит немного громоздко и, возможно, непривычно. Если не всё написанное вам понятно, то не отчаивайтесь, чтению статьи это не помешает.
Ожидается примерно такой результат в консоли:
Но запуск программы приводит к совсем другому результату. Скрипт сломан:
Первое слово SyntaxError Яндекс не понял. Помогите ему и разделите слова пробелом:
Теория. Синтаксические ошибки
Программирование — это не магия, а Python — не волшебный шар. Он не умеет предсказывать будущее, у него нет доступа к секретным знаниями, это просто автомат, это программа. Узнайте как она работает, как ищет ошибки в коде, и тогда легко найдете эффективный способ отладки. Вся необходимая теория собрана в этом разделе, дочитайте до конца.
SyntaxError — это синтаксическая ошибка. Она случается очень рано, еще до того, как Python запустит программу. Вот что делает компьютер, когда вы запускаете скрипт командой python script.py :
Синтаксическая ошибка SyntaxError возникает на четвёртом этапе в момент, когда Python разбирает текст программы на понятные ему компоненты. Сложные выражения в коде он разбирает на простейшие инструкции. Вот пример кода и инструкции для него:
SyntaxError случается когда Python не смог разбить сложный код на простые инструкции. Зная это, вы можете вручную разбить код на инструкции, чтобы затем проверить каждую из них по отдельности. Ошибка прячется в одной из инструкций.
1. Найдите поломанное выражение
Этот шаг сэкономит вам кучу сил. Найдите в программе сломанный участок кода. Его вам предстоит разобрать на отдельные инструкции. Посмотрите на вывод программы в консоль:
Вторая строчка сообщает: File «script.py», line 9 — ошибка в файле script.py на девятой строчке. Но эта строка является частью более сложного выражения, посмотрите на него целиком:
2. Разбейте выражение на инструкции
В прошлых шагах вы узнали что сломан этот фрагмент кода:
Разберите его на инструкции:
Так выделил бы инструкции программист, но вот Python сделать так не смог и сломался. Пора выяснить на какой инструкции нашла коса на камень.
Теперь ваша задача переписать код так, чтобы в каждой строке программы исполнялось не более одной инструкции из списка выше. Так вы сможете тестировать их по отдельности и облегчите себе задачу. Так выглядит отделение инструкции по созданию строки:
Сразу запустите код, проверьте что ошибка осталась на прежнему месте. Приступайте ко второй инструкции:
Скорее всего, Python не распознал вызов функции. Проверьте это, избавьтесь от последней инструкции — от создания переменной label :
3. Проверьте синтаксис вызова функции
Теперь вы знаете что проблема в коде, вызывающем функцию. Можно помедитировать еще немного над кодом программы, пройтись по нему зорким взглядом еще разок в надежде на лучшее. А можно поискать в сети примеры кода для сравнения.
Запросите у Яндекса статьи по фразе “Python синтаксис функции”, а в них поищите код, похожий на вызов format и сравните. Вот одна из первых статей в поисковой выдаче:
Уверен, теперь вы нашли ошибку. Победа!
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.
Обработка ошибок и исключений в коде Python.
Существует, как минимум, два различимых вида ошибок:
Синтаксические ошибки.
Синтаксические ошибки, также известные как ошибки синтаксического анализа, являются, самой распространенной проблемой пока изучаете Python:
Парсер повторяет ошибочную строку и отображает небольшую «стрелку», указывающую на самую раннюю точку в строке, где была обнаружена ошибка. Ошибка вызвана или, по крайней мере, обнаружена токеном, предшествующим стрелке.
Исключения.
Даже если оператор или выражение синтаксически верны, это может вызвать ошибку при попытке выполнить его. Ошибки, обнаруженные во время выполнения, называются исключениями и не являются безусловно фатальными. Однако большинство исключений не обрабатываются программами и приводят к сообщениям об ошибках, как показано здесь:
Остальная часть строки содержит подробности, основанные на типе исключения и причинах его возникновения.
Встроенные исключения перечисляют встроенные исключения и их значения.
Обработка/перехват исключений try/except в Python.
Общие приемы перехвата ошибок конструкцией try/except. Оператор try может содержать несколько предложений except, чтобы указать обработчики для различных исключений. В этом случае будет выполнен только один обработчик.
Инструкция raise, принудительный вызов исключений в Python.
Инструкция raise позволяет программисту принудительно вызвать указанное исключение.
Создание пользовательских исключений в Python.
При создании модуля, который может вызывать несколько различных ошибок, создается базовый класс для исключений и подкласс, который создает определенные классы исключений для различных условий ошибки.
Инструкция finally, очистка внешних ресурсов в Python.
Если инструкция finally присутствует, то она будет выполняться как последняя задача перед завершением выполнения конструкции try. Инструкция finally выполняется независимо от того, создает ли блок кода в операторе try исключени
Тонкости работы конструкции try/except/else/finally в Python.
В операторе try определяется обработчики исключений и/или код очистки для группы операторов.
Отладочные утверждение assert в Python.
Коды ошибок Python
В этой статье вы узнаете о типах ошибок, которые вызываются интерпретатором, если что то в вашей программе идет не так. Все типы ошибок в Python можно разделить на два класса.
При возникновении, какой либо ошибки, которую программа не может обработать, вызывается тип ошибки указывающая на проблему. Избежать эти ошибки поможет нам обработка исключений в Python.
Синтаксические ошибки в Python
Такого рода ошибка возникает при неправильном использовании структуры языка. Рассмотрим пример такой ошибки:
Естественно, мы нарушили структура языка, а именно не поставили двоеточие в конце цикла for.
Логические ошибки в Python
Логические ошибка возникают, если у вас нет синтаксической ошибки. То есть интерпретатор Python, сначала анализирует ваш код на предмет наличия синтаксических ошибок. типы логических ошибок бывают разными, например:
И это только малая часть, Python не разрешит нам уничтожить мир, разделив число на ноль=). Каждый раз, когда Python видит необработанную ошибку, он пишет нам, что произошла ошибка по той или иной причине. Рассмотрим пример обработки ошибки.
Исключения 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. Урок 11. Работа с исключениями
Данный урок посвящен исключениям и работе с ними. Основное внимание уделено понятию исключения в языках программирования, обработке исключений в Python, их генерации и созданию пользовательских исключений.
Исключения в языках программирования
Исключениями (exceptions) в языках программирования называют проблемы, возникающие в ходе выполнения программы, которые допускают возможность дальнейшей ее работы в рамках основного алгоритма. Типичным примером исключения является деление на ноль, невозможность считать данные из файла (устройства), отсутствие доступной памяти, доступ к закрытой области памяти и т.п. Для обработки таких ситуаций в языках программирования, как правило, предусматривается специальный механизм, который называется обработка исключений (exception handling).
Исключения разделяют на синхронные и асинхронные. Синхронные исключения могут возникнуть только в определенных местах программы. Например, если у вас есть код, который открывает файл и считывает из него данные, то исключение типа “ошибка чтения данных” может произойти только в указанном куске кода. Асинхронные исключения могут возникнуть в любой момент работы программы, они, как правило, связаны с какими-либо аппаратными проблемами, либо приходом данных. В качестве примера можно привести сигнал отключения питания.
В языках программирования чаще всего предусматривается специальный механизм обработки исключений. Обработка может быть с возвратом, когда после обработки исключения выполнение программы продолжается с того места, где оно возникло. И обработка без возврата, в этом случае, при возникновении исключения, осуществляется переход в специальный, заранее подготовленный, блок кода.
Различают структурную и неструктурную обработку исключений. Неструктурная обработка предполагает регистрацию функции обработчика для каждого исключения, соответственно данная функция будет вызвана при возникновении конкретного исключения. Для структурной обработки язык программирования должен поддерживать специальные синтаксические конструкции, которые позволяют выделить код, который необходимо контролировать и код, который нужно выполнить при возникновении исключительной ситуации.
Ошибки и исключения в Python
В Python выделяют два различных вида ошибок: синтаксические ошибки и исключения.
Синтаксические ошибки в Python
Синтаксические ошибки возникают в случае если программа написана с нарушениями требований Python к синтаксису. Определяются они в процессе парсинга программы. Ниже представлен пример с ошибочным написанием функции print.
Исключения в Python
Второй вид ошибок – это исключения. Они возникают в случае если синтаксически программа корректна, но в процессе выполнения возникает ошибка (деление на ноль и т.п.). Более подробно про понятие исключения написано выше, в разделе “исключения в языках программирования”.
Пример исключения ZeroDivisionError, которое возникает при делении на 0.
В Python исключения являются определенным типом данных, через который пользователь (программист) получает информацию об ошибке. Если в коде программы исключение не обрабатывается, то приложение останавливается и в консоли печатается подробное описание произошедшей ошибки с указанием места в программе, где она произошла и тип этой ошибки.
Иерархия исключений в 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
Как видно из приведенной выше схемы, все исключения являются подклассом исключения BaseException. Более подробно об иерархии исключений и их описании можете прочитать здесь.
Обработка исключений в Python
Обработка исключений нужна для того, чтобы приложение не завершалось аварийно каждый раз, когда возникает исключение. Для этого блок кода, в котором возможно появление исключительной ситуации необходимо поместить во внутрь синтаксической конструкции try…except.
В приведенной выше программе возможных два вида исключений – это ValueError, возникающее в случае, если на запрос программы “введите число”, вы введете строку, и ZeroDivisionError – если вы введете в качестве числа 0.
Вывод программы при вводе нулевого числа будет таким.
start input number: 0 Error! stop
Если бы инструкций try…except не было, то при выбросе любого из исключений программа аварийно завершится.
Если ввести 0 на запрос приведенной выше программы, произойдет ее остановка с распечаткой сообщения об исключении.
start
input number: 0
Traceback (most recent call last):
File “F:/work/programming/python/devpractice/tmp.py”, line 3, in
tmp = 10 / val
ZeroDivisionError: division by zero
Обратите внимание, надпись stop уже не печатается в конце вывода программы.
Согласно документу по языку Python, описывающему ошибки и исключения, оператор try работает следующим образом:
Для указания набора исключений, который должен обрабатывать данный блок except их необходимо перечислить в скобках (круглых) через запятую после оператора except.
Если бы мы в нашей программе хотели обрабатывать только ValueError и ZeroDivisionError, то программа выглядела бы так.
Или так, если хотим обрабатывать ValueError, ZeroDivisionError по отдельность, и, при этом, сохранить работоспособность при возникновении исключений отличных от вышеперечисленных.
Существует возможность передать подробную информацию о произошедшем исключении в код внутри блока except.
Использование finally в обработке исключений
Для выполнения определенного программного кода при выходе из блока try/except, используйте оператор finally.
Не зависимо от того, возникнет или нет во время выполнения кода в блоке try исключение, код в блоке finally все равно будет выполнен.
Если необходимо выполнить какой-то программный код, в случае если в процессе выполнения блока try не возникло исключений, то можно использовать оператор else.
Генерация исключений в Python
Для принудительной генерации исключения используется инструкция raise.
Самый простой пример работы с raise может выглядеть так.
Таким образом, можно “вручную” вызывать исключения при необходимости.
Пользовательские исключения (User-defined Exceptions) в Python
В Python можно создавать собственные исключения. Такая практика позволяет увеличить гибкость процесса обработки ошибок в рамках той предметной области, для которой написана ваша программа.
Для реализации собственного типа исключения необходимо создать класс, являющийся наследником от одного из классов исключений.
P.S.
Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas. На нашем сайте вы можете найти вводные уроки по этой теме. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.
Глючный код на Python: 10 самых распространенных ошибок, которые допускают разработчики
О Python
Python — это интерпретируемый, объектно-ориентированный язык программирования высокого уровня с динамической семантикой. Встроенные структуры данных высокого уровня в сочетании с динамической типизацией и динамическим связыванием делают его очень привлекательным для БРПС (быстрой разработки прикладных средств), а также для использования в качестве скриптового и связующего языка для подключения существующих компонентов или сервисов. Python поддерживает модули и пакеты, тем самым поощряя модульность программы и повторное использование кода.
О данной статье
Простота и легкость в освоении данного языка может ввести разработчиков в заблуждение (особенно тех, кто еще только начинает изучать Python), так что можно упустить из виду некоторые важные тонкости и недооценить силу разнообразия возможных решений с помощью Python.
Имея это в виду, в этой статье представлен «топ-10» тонких, трудных для обнаружения ошибок, которые могут допустить даже продвинутые разработчики Python.
Ошибка № 1: неправильное использование выражений в качестве значений по умолчанию для аргументов функций
Python позволяет указывать, что у функции могут быть необязательные аргументы, путем задания для них значения по умолчанию. Это, конечно, очень удобная особенность языка, но может привести к неприятным последствиям, если тип такого значения будет изменяемым. Например, рассмотрим следующее определение функции:
Распространенная ошибка в данном случае — это думать, что значение необязательного аргумента будет устанавливаться в значение по умолчанию каждый раз, как функция будет вызываться без значения для этого аргумента. В приведенном выше коде, например, можно предположить, что повторно вызывая функцию foo() (то есть без указания значения для агрумента bar), она всегда будет возвращать «baz», поскольку предполагается, что каждый раз, когда вызывается foo () (без указания аргумента bar), bar устанавливается в [ ] (т. е. новый пустой список).
Но давайте посмотрим что же будет происходить на самом деле:
А? Почему функция продолжает добавлять значение по умолчанию «baz» к существующему списку каждый раз, когда вызывается foo(), вместо того, чтобы каждый раз создавать новый список?
Ответом на данный вопрос будет более глубокое понимание того, что творится у Python «под капотом». А именно: значение по умолчанию для функции инициализируется только один раз, во время определения функции. Таким образом, аргумент bar инициализируется по умолчанию (т. е. пустым списком) только тогда, когда foo() определен впервые, но последующие вызовы foo() (т. е. без указания аргумента bar) продолжат использовать тот же список, который был создан для аргумента bar в момент первого определения функции.
Для справки, распространенным «обходным путем» для этой ошибки является следующее определение:
Ошибка № 2: неправильное использование переменных класса
Рассмотрим следующий пример:
Вроде все в порядке.
Ага, все как и ожидалось.
Что за черт?! Мы же только изменили A.x. Почему же C.x тоже изменилось?
В Python переменные класса обрабатываются как словари и следуют тому, что часто называют Порядком разрешения методов (MRO). Таким образом, в приведенном выше коде, поскольку атрибут x не найден в классе C, он будет найден в его базовых классах (только A в приведенном выше примере, хотя Python поддерживает множественное наследование). Другими словами, C не имеет своего собственного свойства x, независимого от A. Таким образом, ссылки на C.x фактически являются ссылками на A.x. Это будет вызывать проблемы, если не обрабатывать такие случаи должным образом. Так что при изучении Python обратите особое внимание на аттрибуты класса и работу с ними.
Ошибка № 3: неправильное указание параметров для блока исключения
Предположим, что у вас есть следующий кусок кода:
Проблема здесь заключается в том, что выражение except не принимает список исключений, указанных таким образом. Скорее, в Python 2.x выражение «except Exception, e» используется для привязки исключения к необязательному второму заданному второму параметру (в данном случае e), чтобы сделать его доступным для дальнейшей проверки. В результате в приведенном выше коде исключение IndexError не перехватывается выражением except; скорее, вместо этого исключение заканчивается привязкой к параметру с именем IndexError.
Правильный способ перехвата нескольких исключений с помощью выражения except — указать первый параметр в виде кортежа, содержащего все исключения, которые нужно перехватить. Кроме того, для максимальной совместимости используйте ключевое слово as, так как этот синтаксис поддерживается как в Python 2, так и в Python 3:
Ошибка № 4: непонимание правил области видимости Python
Вышеуказанная ошибка возникает потому, что, когда вы присваиваете переменную в области видимости, Python автоматически считает ее локальной для этой области и скрывает любую переменную с аналогичным именем в любой вышестоящей области.
Таким образом, многие удивляются, когда получают UnboundLocalError в ранее работающем коде, когда он модифицируется путем добавления оператора присваивания где-нибудь в теле функции.
Эта особенность особенно сбивает разработчиков с толку при использовании списков. Рассмотрим следующий пример:
А? Почему foo2 падает, в то время как foo1 работает нормально?
Ответ такой же, как в предыдущем примере, но, по распространенному мнению, здесь ситуация более тонкая. foo1 не применяет оператор присваивания к lst, тогда как foo2 — да. Помня, что lst + = [5] на самом деле является просто сокращением для lst = lst + [5], мы видим, что мы пытаемся присвоить значение lst (поэтому Python предполагает, что он находится в локальной области видимости). Однако значение, которое мы хотим присвоить lst, основано на самом lst (опять же, теперь предполагается, что он находится в локальной области видимости), который еще не был определен. И мы получаем ошибку.
Ошибка № 5: изменение списка во время итерации по нему
Проблема в следующем куске кода должна быть достаточно очевидной:
Удаление элемента из списка или массива во время итерации по нему — это проблема Python, которая хорошо известна любому опытному разработчику программного обеспечения. Но, хотя приведенный выше пример может быть достаточно очевидным, даже опытные разработчики могут встать на эти грабли в гораздо более сложном коде.
К счастью, Python включает в себя ряд элегантных парадигм программирования, которые при правильном использовании могут привести к значительному упрощению и оптимизации кода. Дополнительным приятным следствием этого является то, что в более простом коде вероятность попасться на ошибку случайного удаления элемента списка во время итерации по нему значительно меньше. Одна из таких парадигм — генераторы списков. Кроме того, понимание работы генераторов списков особенно полезны для избежания этой конкретной проблемы, как показано в этой альтернативной реализацией приведенного выше кода, которая прекрасно работает:
Ошибка № 6: непонимание того, как Python связывает переменные в замыканиях
Рассмотрим следующий пример:
Вы можете ожидать следующий вывод:
Но на самом деле вы получите вот что:
Это происходит из-за поздней привязки в Python, которое заключается в том, что значения переменных, используемых в замыканиях, ищутся во время вызова внутренней функции. Таким образом, в приведенном выше коде всякий раз, когда вызывается какая-либо из возвращаемых функций, значение i ищется в окружающей области видимости во время ее вызова (а к тому времени цикл уже завершился, поэтому i уже был присвоен конечный результат — значение 4).
Решение этой распространенной проблемы с Python будет таким:
Вуаля! Мы используем здесь аргументы по умолчанию для генерации анонимных функций для достижения желаемого поведения. Некоторые назвали бы это решение элегантным. Некоторые —
тонким. Некоторые ненавидят подобные штуки. Но если вы разработчик Python, в любом случае, это важно понимать.
Ошибка № 7: создание циклических зависимостей модуля
Допустим, у вас есть два файла, a.py и b.py, каждый из которых импортирует другой, следующим образом:
Сначала попробуем импортировать a.py:
Сработало просто отлично. Возможно, это вас удивляет. В конце концов, модули циклически импортируют друг друга и это, вероятно, должено быть проблемой, не так ли?
Ответ заключается в том, что простое наличие циклического импорта модулей само по себе не является проблемой в Python. Если модуль уже был импортирован, Python достаточно умен, чтобы не пытаться повторно импортировать его. Однако, в зависимости от точки, в которой каждый модуль пытается получить доступ к функциям или переменным, определенным в другом, вы действительно можете столкнуться с проблемами.
Итак, возвращаясь к нашему примеру, когда мы импортировали a.py, у него не было проблем с импортом b.py, поскольку b.py не требует, чтобы что-либо из a.py было определено во время его импорта. Единственная ссылка в b.py на a — это вызов a.f(). Но этот вызов в g() и ничего в a.py или b.py не вызывает g(). Так что все работает прекрасно.
Но что произойдет, если мы попытаемся импортировать b.py (без предварительного импорта a.py, то есть):
Ой-ой. Это не хорошо! Проблема здесь в том, что в процессе импорта b.py он пытается импортировать a.py, который, в свою очередь, вызывает f(), который пытается получить доступ к b.x. Но b.x еще не было определено. Отсюда исключение AttributeError.
По крайней мере, одно из решений этой проблемы довольно тривиально. Просто измените b.py, чтобы импортировать a.py в g():
Теперь, когда мы его импортируем, все нормально:
Ошибка № 8: пересечение имен с именами модулями стандартной библиотеки Python
Одна из прелестей Python — это множество модулей, которые поставляются «из коробки». Но в результате, если вы сознательно не будете за этим следить, можно столкнуться с тем, что имя вашего модуля может быть с тем же именем, что и модуль в стандартной библиотеке, поставляемой с Python (например, в вашем коде может быть модуль с именем email.py, который будет конфликтовать со модулем стандартной библиотеки с таким же именем).
Это может привести к серьезным проблемам. Например, если какой-нибудь из модулей будет пытаться импортировать версию модуля из стандартной библиотеки Python, а у вас в проекте будет модуль с таким же именем, который и будет по ошибке импортирован вместо модуля из стандартной библиотеки.
Поэтому следует проявлять осторожность, чтобы не использовать те же имена, что и в модулях стандартной библиотеки Python. Гораздо проще изменить название модуля в своем проекте, нежели подать запрос на изменение имени модуля в стандартной библиотеке и получить на него одобрение.
Ошибка № 9: неспособность учесть различия Python 2 и Python 3
Рассмотрим следующий файл foo.py:
На Python 2 он отработает нормально:
Но теперь давайте посмотрим как он будет работать в Python 3:
Что здесь только что произошло? «Проблема» в том, что в Python 3 объект в блоке исключения недоступен за его пределами. (Причина этого заключается в том, что в противном случае объекты в этом блоке будут сохраняться в памяти до тех пор, пока сборщик мусора не запустится и не удалит ссылки на них оттуда).
Один из способов избежать этой проблемы — сохранить ссылку на объект блока исключения за пределами этого блока, чтобы он оставался доступным. Вот версия предыдущего примера, которая использует эту технику, тем самым получая код, который подходит как для Python 2, так и для Python 3:
Запустим его в Python 3:
Ошибка № 10: неправильное использование метода __del__
Допустим, у вас есть вот такой файл mod.py:
И вы пытаетесь сделать вот такое из другого another_mod.py:
И получите ужасный AttributeError.
Почему? Потому что, как сообщается здесь, когда интерпретатор отключается, глобальные переменные модуля все имеют значение None. В результате в приведенном выше примере, в момент вызова __del__, имя foo уже было установлено в None.
Решением этой «задачи со звездочкой» будет использование atexit.register(). Таким образом, когда ваша программа завершает выполнение (то есть при нормальном выходе из нее), ваши handle’ы удаляются до того, как интерпретатор звершает работу.
С учетом этого, исправление для приведенного выше кода mod.py может выглядеть примерно так:
Подобная реализация обеспечивает простой и надежный способ вызова любой необходимой очистки после обычного завершения программы. Очевидно, что решение о том, как поступить с объектом, который связан с имненем self.myhandle, остается за foo.cleanup, но, думаю, идею вы поняли.
Заключение
Python — это мощный и гибкий язык со множеством механизмов и парадигм, которые могут значительно повысить производительность. Однако, как и в случае с любым программным инструментом или языком, при ограниченном понимании или оценке его возможностей, при разработке могут возникать непредвиденные проблемы.
Ознакомление с нюансами Python, затронутыми в этой статье, поможет оптимизировать использование языка, избегая при этом некоторых распространенных ошибок.