При импорте установленного пакета из сценария возникает ошибка "AttributeError: модуль не имеет атрибута" или "ImportError: невозможно импортировать имя"
У меня есть скрипт с именем requests.py
который импортирует пакет запросов. Сценарий либо не может получить доступ к атрибутам из пакета, либо не может импортировать их. Почему это не работает и как я могу это исправить?
Следующий код поднимает AttributeError
,
import requests
res = requests.get('http://www.google.ca')
print(res)
Traceback (most recent call last):
File "/Users/me/dev/rough/requests.py", line 1, in <module>
import requests
File "/Users/me/dev/rough/requests.py", line 3, in <module>
requests.get('http://www.google.ca')
AttributeError: module 'requests' has no attribute 'get'
Следующий код поднимает ImportError
,
from requests import get
res = get('http://www.google.ca')
print(res)
Traceback (most recent call last):
File "requests.py", line 1, in <module>
from requests import get
File "/Users/me/dev/rough/requests.py", line 1, in <module>
from requests import get
ImportError: cannot import name 'get'
Следующий код поднимает ImportError
,
from requests.auth import AuthBase
class PizzaAuth(AuthBase):
"""Attaches HTTP Pizza Authentication to the given Request object."""
def __init__(self, username):
# setup any auth-related data here
self.username = username
def __call__(self, r):
# modify and return the request
r.headers['X-Pizza'] = self.username
return r
Traceback (most recent call last):
File "requests.py", line 1, in <module>
from requests.auth import AuthBase
File "/Users/me/dev/rough/requests.py", line 1, in <module>
from requests.auth import AuthBase
ImportError: No module named 'requests.auth'; 'requests' is not a package
6 ответов
Это происходит потому, что ваш локальный модуль с именем requests.py
тени установлены requests
модуль, который вы пытаетесь использовать. Текущий каталог добавляется к sys.path
, так что локальное имя имеет приоритет над установленным именем.
Дополнительный совет отладки, когда он появляется, состоит в том, чтобы внимательно посмотреть на Traceback и понять, что имя вашего скрипта соответствует модулю, который вы пытаетесь импортировать:
Обратите внимание на имя, которое вы использовали в вашем скрипте:
File "/Users/me/dev/rough/requests.py", line 1, in <module>
Модуль, который вы пытаетесь импортировать: requests
Переименуйте ваш модуль во что-то другое, чтобы избежать конфликта имен.
Python может генерировать requests.pyc
файл рядом с вашим requests.py
файл (в __pycache__
каталог в Python 3). Удалите это и после переименования, так как интерпретатор все равно будет ссылаться на этот файл, создавая ошибку заново. Тем не менее pyc
файл в __pycache__
недолжно влиять на ваш код, если py
файл был удален
В примере переименование файла в my_requests.py
, удаляя requests.pyc
и снова работает успешно печатает <Response [200]>
,
Для автора исходного вопроса и для тех, кто ищет в строке "AttributeError: модуль не имеет атрибута", общее объяснение согласно принятому ответу состоит в том, что созданный пользователем сценарий имеет конфликт имен с библиотекой. имя файла. Однако обратите внимание, что проблема может заключаться не в имени скрипта, который генерирует ошибку (как это было в вышеупомянутом случае), ни в каком-либо из имен библиотечных модулей, явно импортированных этим скриптом. Может потребоваться небольшая детективная работа, чтобы выяснить, какой файл вызывает проблему.
В качестве примера для иллюстрации проблемы представьте, что вы создаете скрипт, который использует библиотеку "десятичная дробь" для точных вычислений с плавающей запятой с десятичными числами, и вы называете свой скрипт ".mydecimal.py
"который содержит строку"import decimal
Msgstr "Нет проблем с этим, но вы обнаружите, что это вызывает эту ошибку:
AttributeError: 'module' object has no attribute 'Number'
Это произошло бы, если бы вы ранее написали скрипт под названием "numbers.py
"потому что" десятичная "библиотека вызывает стандартную библиотеку" числа ", но вместо этого находит ваш старый скрипт. Даже если вы удалили это, это может не решить проблему, потому что Python мог бы преобразовать это в байт-код и сохранить его в кеше как "numbers.pyc
", так что вам придется выследить это также.
Краткое содержание
Подобные проблемы возникают, когда исходный файл Python в проекте имеет то же имя, что и какой-либо внешний библиотечный модуль (либо в стандартной библиотеке, либо в установленном стороннем пакете). При попытке из внешней библиотеки (которая требует использования абсолютного импорта) вместо этого обнаруживается собственный модуль проекта, что вызывает различные ошибки в зависимости от точных деталей.
Самый простой способ решить проблему, в обычных случаях, — переименовать поврежденный файл. Также может потребоваться найти и удалить соответствующие файлы. (Это совершенно безопасно; файлы представляют собой просто кэш работы, которая ранее была проделана для перевода исходного кода Python в байт-код для виртуальной машины Python, аналогично
Предпочтение отдается собственному модулю проекта, поскольку Python ищет исходный код модуля для абсолютного импорта. В зависимости от того, как именно запускается Python, путь поиска модуля, определенный в , обычно начинается с пути, находящегося внутри текущего проекта. Python сначала будет искать там, а затем в папках стандартной или сторонней библиотеки. Этот процесс поиска не учитывает папку, в которой находится модуль импорта ; любые относительные пути относятся к текущему рабочему каталогу процесса , а не к модулю импорта . (Однако пути стандартной библиотеки обычно являются абсолютными путями и в любом случае находятся ближе к концу .)
Это происходит потому, что собственный модуль проекта просто не определяет функцию, класс и т. д., которые вызывающий код хочет использовать из модуля внешней библиотеки. В результате
В редких, неудачных случаях собственный модуль проекта может определить что-то с тем же именем, но делать что-то другое. Это может вызвать любое исключение или другую логическую ошибку . Например, если первый пример вопроса изменен:
import requests
def get():
pass
res = requests.get('http://www.google.ca')
print(res)
теперь
Использование определенного импорта из импорта приводит к тому, что сообщение об ошибке сообщается по-другому, поскольку проблема теперь обнаруживается во время самого процесса импорта . В примере в вопросе
Другие попытки импорта могут привести к другим
Использование звездного импорта, например
Отягчающие факторы
Система Python по умолчанию основана на именах, а не на путях к файлам, и Python не различает «файлы сценариев (драйверов)» и «файлы библиотек (модулей)». Любой исходный код Python можно редактировать. Хотя модуль кэширования Python импортирует, основной скрипт обычно не находится в этом кеше, а это означает, что он вполне способен выполнить попытку . Вот что происходит в примере в вопросе: поскольку
еще не был отредактирован, в кеше ничего нет, поэтому попытка в скрипте драйвера (с именем ) к будет искать что-то для импорта и найдет сценарий драйвера (т. е. собственный исходный файл). Это вызывает проблемы только во время импорта, если модуль пытается использовать этот импорт в своей собственной инициализации, например, выполняя from-import. В противном случае проблема будет отложена, что приведет (обычно)
или при попытке использовать эту функциональность. Смотрите также: Всякий раз, когда модуль загружается (т. е. импортируется из источника, а не из кэша), запускаются его собственные операторы верхнего уровня, что приводит к рекурсивному повторному импорту. Любой из этих косвенных импортов потенциально может найти локальный код. Стандартная библиотека Python не является пакетом и в основном использует абсолютный импорт для обращения к другим своим компонентам. Например, как указано в ответе Дэйва Роува, попытка импортировать стандартную библиотеку
модуль мог выйти из строя при попытке из исходного файла с именем или внутри проекта, в котором есть такой исходный файл. В одном особенно пагубном случае наличие файла, указанного в проекте (или текущего рабочего каталога при запуске Python в интерактивном режиме), приводит к сбою интерактивной справки :
$ touch token.py $ python Python 3.8.10 (default, Nov 14 2022, 12:59:47) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> help Type help() for interactive help, or help(object) for help about object. >>> help() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python3.8/_sitebuiltins.py", line 102, in __call__ import pydoc File "/usr/lib/python3.8/pydoc.py", line 66, in <module> import inspect File "/usr/lib/python3.8/inspect.py", line 40, in <module> import linecache File "/usr/lib/python3.8/linecache.py", line 11, in <module> import tokenize File "/usr/lib/python3.8/tokenize.py", line 35, in <module> from token import EXACT_TOKEN_TYPES ImportError: cannot import name 'EXACT_TOKEN_TYPES' from 'token' (/current/working directory/token.py)
Обратная трассировка сообщает нам все, что нам нужно знать: вызов
запускает отложенный импорт стандартной библиотеки , который косвенно пытается импортировать стандартную библиотеку, но находит нашу, которая не содержит соответствующего имени. В старых версиях Python было ещё хуже: сделал бы звездный импорт из , а затем его код верхнего уровня попытается использовать определенное там имя, в результате чего - и трассировка стека без упоминания имени файла . Python может импортировать из кэшированных файлов байт-кода (), даже если соответствующий исходный файл переименован или удален. Это предназначено для ускорения импорта, но создает дополнительный шаг для поддержания «чистоты» каталогов кода. После переименования любого файла для решения подобной проблемы обязательно проверьте также кеш байт-кода.
Решение проблемы
Конечно, стандартный совет прост и понятен: просто переименуйте файлы, которые были импортированы по ошибке, и удалите все кэшированные файлы байт-кода для этих модулей. По умолчанию в Python 3.x файлы любых файлов в папке будут помещены в подпапку со специальным именем.
Однако, хотя этот подход быстро решает насущную проблему для большинства людей, сталкивающихся с ней, совет не очень хорошо масштабируется. Есть много потенциально проблемных имен; хотя большинство проектов не будут заинтересованы во включении исходного файла с именем, например
Контроль
Конечно, поскольку проблема вызвана указанием того, что абсолютный импорт должен сначала просматриваться в текущем проекте, этого можно избежать, просто изменив файл .
Проблемный путь описан в документации как «потенциально небезопасный путь». При использовании интерактивной подсказки Python это будет пустая строка (относительный путь, эквивалентный
Чтобы избежать использования этого пути в , выполните одно из следующих действий :
В Python 3.11 и более поздних версиях установите
переменную среды или используйтепараметр командной строки для Python.В Python 3.4 и более поздних версиях используйте
параметр командной строки для запуска в изолированном режиме. Однако это имеет несколько других эффектов: будут игнорироваться такие переменные среды, каки , а также пропустит добавление в путь каталога пакетов сайта, специфичного для пользователя (поэтому код не будет иметь доступа к сторонним библиотекам, которые были установлены с помощью вариант для Пипа). Если все остальное не помогло, рассмотрите возможность манипулирования файлами . Это беспорядочно и подвержено ошибкам, но это всего лишь обычный список строк с путями к файлам, и изменение его содержимого повлияет на будущее.
с. ( Не пытайтесь заменить объект списка; это не сработает, поскольку Python не ищет его по имени, а использует жестко запрограммированную внутреннюю ссылку. Уничтожить или заменить этот объект из Python невозможно; это просто имя, которое инициализируется для ссылки на него.)
Имейте в виду, что удаление «небезопасного» пути предотвратит работу преднамеренного абсолютного импорта внутри пакета. Это неудобно для некоторых небольших проектов, но также является хорошим поводом научиться правильной организации пакетов. Говоря о которых:
Использование относительного импорта внутри пакетов
Хорошо организованный проект Python обычно состоит из одного или нескольких (обычно одного) пакетов, хранящихся в подпапках основной папки проекта, а также одного или нескольких сценариев драйверов, размещенных вне подпапок пакета. Сценарии драйверов обычно используют один абсолютный импорт для доступа к функциям пакета, реализуя при этом некоторую простую логику оболочки (например, для анализа аргументов командной строки, форматирования и сообщения о неперехваченных в противном случае исключениях и т. д.). Тем временем пакет будет использовать относительный импорт для своего собственного содержимого, а абсолютный импорт — только там, где это необходимо для доступа к другим пакетам (стандартная библиотека и сторонние зависимости).
Например, проект может быть организован следующим образом:
project
├── src
│ └── my_package
│ └── x.py
│ └── y.py
│ └── z.py
└── driver.py
Введите код
from my_package.x import entry_point
if __name__ == '__main__':
entry_point()
(Если для анализа аргументов командной строки необходима логика, она обычно должна быть в драйвере, а не в коде пакета.)
Тогда код внутри пакета будет использовать относительный импорт, поэтому
from .y import first_thing
from .z import second_thing
def entry_point():
first_thing()
second_thing()
Это берет лучшее из обоих миров : первоначальный абсолютный импорт настраивает пакет верхнего уровня так, что относительный импорт будет работать, а относительный импорт не будет работать в зависимости от конфигурации. Даже если не предпринимать никаких действий по настройке, он обычно включает папку со сценариями драйвера, а не папки пакетов; таким образом, это также автоматически позволяет избежать конфликтов путей импорта. (При абсолютном импорте содержимое пакета не будет найдено, если не указан соответствующий путь к пакету; но обычно в этом случае импорт из текущего пакета был преднамеренным.)
Это также позволяет избежать установки ловушек для следующего проекта, который имеет зависимость от этого проекта. Скажем, например, что мы реализуем и публикуем пакет, а кто-то другой пишет, что у него есть зависимость. Поскольку код будет использовать относительный импорт для других
Разумеется, чтобы первоначальный абсолютный импорт работал, в файле необходимо указать папку пакета верхнего уровня.
Отключение кэширования байт-кода
Если файл необходимо переименовать, избежать проблем с файлами будет проще, если их вообще не существует. В конце концов, они не нужны ; они, опять же, просто предназначены для ускорения импорта при последующих запусках программы.
Чтобы запретить создание файлов, используйте команду
Линтинг, чтобы избежать проблемных имен
Рассмотрите возможность использования сторонних инструментов (таких как плагины IDE) для предупреждения об именах файлов, используемых стандартной библиотекой или сторонними библиотеками в проекте. В Python 3.10 и более поздних версиях полный список имен модулей стандартной библиотеки также доступен как
sys.stdlib_module_names
. Сюда входят имена, которые могут отсутствовать в текущей установке Python (например, компоненты, специфичные для ОС, или вещи, которые иногда отключаются или опускаются, например Tkinter).
просто отложите импорт, который вызывает ошибку, до последнего
ex:
import requests
import flask
import numpy
еслиimport flask
вызывает ошибку, затем переместите ее в конец импорта
solution:-
import requests
import numpy
import flask
Python ищет объект запросов в вашем
requests.py
модуль.
Либо ПЕРЕИМЕНОВАТЬ этот файл, либо использовать
from __future__ import absolute_import
в верхней части вашего
requests.py
модуль.
По
import requests
вы импортируете файл request.py, а не модуль. Просто переименуйте файл.