При импорте установленного пакета из сценария возникает ошибка "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 decimalMsgstr "Нет проблем с этим, но вы обнаружите, что это вызывает эту ошибку:

AttributeError: 'module' object has no attribute 'Number'

Это произошло бы, если бы вы ранее написали скрипт под названием "numbers.py"потому что" десятичная "библиотека вызывает стандартную библиотеку" числа ", но вместо этого находит ваш старый скрипт. Даже если вы удалили это, это может не решить проблему, потому что Python мог бы преобразовать это в байт-код и сохранить его в кеше как "numbers.pyc", так что вам придется выследить это также.

Краткое содержание

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

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

Предпочтение отдается собственному модулю проекта, поскольку Python ищет исходный код модуля для абсолютного импорта. В зависимости от того, как именно запускается Python, путь поиска модуля, определенный в , обычно начинается с пути, находящегося внутри текущего проекта. Python сначала будет искать там, а затем в папках стандартной или сторонней библиотеки. Этот процесс поиска не учитывает папку, в которой находится модуль импорта ; любые относительные пути относятся к текущему рабочему каталогу процесса , а не к модулю импорта . (Однако пути стандартной библиотеки обычно являются абсолютными путями и в любом случае находятся ближе к концу .)

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

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

      import requests

def get():
    pass

res = requests.get('http://www.google.ca')
print(res)

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

Использование определенного импорта из импорта приводит к тому, что сообщение об ошибке сообщается по-другому, поскольку проблема теперь обнаруживается во время самого процесса импорта . В примере в вопросеозначает, что вместо создания нового глобального имени, которое называет модуль, должно быть новое глобальное имя, которое называет функцию из этого модуля (то, которое будет называтьсяпосле простого импорта). Для этого необходимо найти атрибут из этого модуля; но поскольку был загружен не тот модуль, поиск атрибута не удался. Более поздние версии Python сообщают об этом как о вероятном циклическом импорте .

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

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

Отягчающие факторы

  1. Система Python по умолчанию основана на именах, а не на путях к файлам, и Python не различает «файлы сценариев (драйверов)» и «файлы библиотек (модулей)». Любой исходный код Python можно редактировать. Хотя модуль кэширования Python импортирует, основной скрипт обычно не находится в этом кеше, а это означает, что он вполне способен выполнить попытку . Вот что происходит в примере в вопросе: посколькуеще не был отредактирован, в кеше ничего нет, поэтому попытка в скрипте драйвера (с именем) кбудет искать что-то для импорта и найдет сценарий драйвера (т. е. собственный исходный файл).

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

  2. Всякий раз, когда модуль загружается (т. е. импортируется из источника, а не из кэша), запускаются его собственные операторы верхнего уровня, что приводит к рекурсивному повторному импорту. Любой из этих косвенных импортов потенциально может найти локальный код. Стандартная библиотека 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 было ещё хуже:сделал бы звездный импорт из, а затем его код верхнего уровня попытается использовать определенное там имя, в результате чего- и трассировка стека без упоминания имени файла.

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

Решение проблемы

Конечно, стандартный совет прост и понятен: просто переименуйте файлы, которые были импортированы по ошибке, и удалите все кэшированные файлы байт-кода для этих модулей. По умолчанию в Python 3.x файлы любых файлов в папке будут помещены в подпапку со специальным именем.; в версии 2.x они просто появлялись рядом с соответствующимифайлы.

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

Контроль

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

Проблемный путь описан в документации как «потенциально небезопасный путь». При использовании интерактивной подсказки Python это будет пустая строка (относительный путь, эквивалентный) — т. е. текущий рабочий каталог процесса 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, а не модуль. Просто переименуйте файл.

Другие вопросы по тегам