Импорт библиотеки из (или рядом с ним) скрипта с таким же именем вызывает "AttributeError: у модуля нет атрибута" или ImportError или NameError
У меня есть скрипт с именемrequests.py
, который должен использовать пакет стороннего производителя requests
. Скрипт либо не может импортировать пакет, либо не может получить доступ к его функциональности.
Почему это не работает и как мне это исправить?
Попытка простого импорта с последующим использованием функциональности приводит к 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'
В более поздних версиях Python вместо этого появляется сообщение об ошибке AttributeError: partially initialized module 'requests' has no attribute 'get' (most likely due to a circular import)
.
Использование from-import определенного имени приводит к 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'
В более поздних версиях Python вместо этого появляется сообщение об ошибке ImportError: cannot import name 'get' from partially initialized module 'requests' (most likely due to a circular import) (/Users/me/dev/rough/requests.py)
.
Использование from-import для модуля внутри пакета приводит к другому ImportError
:
from requests.auth import AuthBase
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
Использование звездочки-импорт, а затем использование функциональности вызывает NameError
:
from requests import *
res = get('http://www.google.ca')
print(res)
Traceback (most recent call last):
File "requests.py", line 1, in <module>
from requests import *
File "/Users/me/dev/rough/requests.py", line 3, in <module>
res = get('http://www.google.ca')
NameError: name 'get' is not defined
Для случаев, когда вы специально называете свой модуль таким же, как существующий, и хотите справиться с этой ситуацией, см. Как я могу импортировать из стандартной библиотеки, когда в моем проекте есть модуль с таким же именем? (Как я могу контролировать, где Python ищет модули?)
Переведено автоматически
Ответ 1
Это происходит потому, что ваш локальный модуль с именем requests.py
затеняет установленный requests
модуль, который вы пытаетесь использовать. Текущий каталог добавляется к sys.path
, поэтому локальное имя имеет приоритет над установленным именем.
Дополнительный совет по отладке, когда возникает такая проблема, заключается в том, чтобы внимательно просмотреть обратную трассировку и понять, что имя вашего скрипта, о котором идет речь, соответствует модулю, который вы пытаетесь импортировать:
Обратите внимание на имя, которое вы использовали в своем скрипте:
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]>
.
Примечание: Это происходит не только при присвоении имени вашему файлу в качестве модуля, который вы пытаетесь импортировать. Это также может произойти, если вы назовете свой файл так же, как модуль, импортированный модулем, который вы импортируете напрямую. Например, имея файл с именем copy.py
и пытаясь import pandas
получить его оттуда, вы получите
ImportError: cannot import name 'copy' from 'copy'
Это потому, что pandas
импортирует copy
. Здесь нет волшебного решения, поскольку вы не можете знать имена всех модулей в мире, но эмпирическое правило состоит в том, чтобы стараться делать имена модулей как можно более уникальными и пытаться менять имя всякий раз, когда вы получаете такую ошибку.
Ответ 2
Ошибка возникает из-за того, что у созданного пользователем скрипта имя совпадает с именем файла библиотеки. Однако обратите внимание, что проблема может быть вызвана косвенно. Может потребоваться небольшая детективная работа, чтобы выяснить, какой файл вызывает проблему.
Например: предположим, что у вас есть скрипт, mydecimal.py
который включает в себя import decimal
, намеревающийся использовать стандартную библиотеку decimal
библиотека для точных вычислений с плавающей запятой с десятичными числами. Это не вызывает проблем, потому что нет стандартной библиотеки mydecimal
. Однако так получилось, что decimal
импортирует numbers
(другой модуль стандартной библиотеки) для внутреннего использования, поэтому скрипт, вызываемый numbers.py
в вашем проекте, вызовет проблему.
Если вы по-прежнему сталкиваетесь с подобными проблемами после отслеживания собственных и переименования или удаления соответствующих .py
файлов в вашем проекте, также проверьте наличие .pyc
файлов , которые Python использует для кэширования компиляции байт-кода при импорте модулей. В 3.x они будут храниться в папках со специальным именем __pycache__
; такие папки и файлы можно удалять безопасно и возможно их подавить (но обычно вам этого не захочется).
Ответ 3
Краткие сведения
Проблемы, подобные этой, возникают, когда исходный файл Python в проекте имеет то же имя, что и какой-либо внешний библиотечный модуль (либо в стандартной библиотеке, либо в установленном пакете сторонних производителей). При попытке import
из внешней библиотеки (что требует использования абсолютного импорта), вместо этого обнаруживается собственный модуль проекта, вызывая различные ошибки в зависимости от точных деталей.
В обычных случаях самый простой способ устранить проблему - переименовать затронутый файл. Также может потребоваться найти и удалить любые соответствующие .pyc
файлы. (Это совершенно безопасно; файлы представляют собой просто кэш работы, которая ранее была проделана для перевода исходного кода Python в байт-код для виртуальной машины Python, аналогично .class
файлам в Java.)
Когда предпочтителен собственный модуль проекта, это происходит из-за того, как Python выполняет поиск исходного кода модуля для абсолютного импорта. В зависимости от того, как именно запускается Python, путь поиска модуля, определенный в sys.path
обычно , начинается с пути, который находится внутри текущего проекта. Python сначала посмотрит там, прежде чем искать в любой стандартной библиотеке или папках библиотек сторонних производителей. Этот процесс поиска не заботится о папке, в которой находится импортирующий модуль; любые относительные пути относятся к текущему рабочему каталогу процесса, а не к импортирующему модулю. (Однако стандартными путями к библиотекам обычно будут абсолютные пути, и в любом случае они будут ближе к концу из sys.path
.)
AttributeError
AttributeError
Происходит потому, что собственный модуль проекта просто не определяет функцию, класс и т.д., Которые вызывающий код хочет использовать из модуля внешней библиотеки. В результате, 'module' object
который представляет модуль проекта has no attribute
с указанным именем, так что это именно то, что утверждается в сообщении об ошибке.
В редких, неудачных случаях собственный модуль проекта может случайно определить что-то с тем же именем, так что оно выполняет что-то другое. Это может вызвать исключение любого типа или другую логическую ошибку. Например, если изменен первый пример из вопроса:
import requests
def get():
pass
res = requests.get('http://www.google.ca')
print(res)
теперь вместо этого будет вызван a TypeError
, потому что код попытается вызвать локально определенную get
функцию с неправильным количеством аргументов.
ImportError
Использование определенного параметра from-import приводит к тому, что сообщение об ошибке отображается по-другому, потому что проблема теперь обнаруживается во время самого процесса импорта. В примере в вопросе, from requests import get
означает, что вместо создания нового глобального имени, requests
которое называет модуль, должно быть новое глобальное имя, get
которое называет функцию из этого модуля (ту, которая будет называться requests.get
после простого импорта). Для этого требуется поиск get
атрибута из этого модуля; но поскольку был загружен неправильный модуль, поиск этого атрибута завершается ошибкой. Более поздние версии Python сообщают об этом как о вероятном циклическом импорте.
Другие попытки импорта могут вызывать разные ImportError
запросы, которые жалуются, что импортированный модуль "не является пакетом". Это не требует пояснений: в примере в вопросе, requests
это обычный модуль (потому что он определен файлом исходного кода, requests.py
), но желаемый requests
- определенный сторонней библиотекой - это пакет (определенный несколькими файлами внутри requests
папки в другом месте, включая __init__.py
который определяет некоторое содержимое пакета верхнего уровня, которое не является модулями, как get
функция).
NameError
Использование звездочки-импорт, например, from requests import *
, не приведет к сбою напрямую - он создает глобальные имена для всего содержимого модуля. Однако, поскольку модуль в примере вопроса не содержит ничего с именем get
, его попытка звездного импорта сама по себе также не определит это имя. Таким образом, возникает NameError
- обычная ошибка при попытке использовать глобальное имя, которого не существует.
Отягчающие факторы
import
Система Python по умолчанию основана на именах, а не на путях к файлам, и Python не делает различий между "файлами скрипта (драйвера)" и "файлами библиотеки (модуля)". Любой исходный код Python может бытьimport
отредактирован. Хотя Python кэширует импортируемый модуль, основного скрипта обычно не будет в этом кэше, что означает, что он вполне способен попытаться сделать этоimport
сам. Это то, что происходит в примере в вопросе: посколькуrequests
еще не былоimport
отредактировано, в кэше ничего нет, поэтому попытка в скрипте драйвера (с именемrequests.py
)import requests
будет искать что-то для импорта и найдет скрипт драйвера (т. Е. Его собственный исходный файл).Это вызывает проблему только во время импорта, если модуль пытается использовать этот импорт в своей собственной инициализации, например, выполняя from-import . В противном случае проблема будет отложена, что приведет (как правило) к
AttributeError
илиNameError
при попытке использовать функциональность. См. Также:- Что происходит при использовании взаимного или циклического импорта?
- Почему циклический импорт, по-видимому, работает дальше в стеке вызовов, но затем вызывает ImportError дальше вниз?
Всякий раз, когда модуль загружается (т. Е. Импортируется из его источника, а не с использованием кэша), выполняются его собственные инструкции верхнего уровня
import
, рекурсивно вызывая больше импорта. Любой из этих косвенных импортов потенциально может привести к обнаружению локального кода. Стандартная библиотека Python не является пакетом и в основном использует абсолютный импорт для обращения к другим своим компонентам. Например, как указано в ответе Дейва Роува, попытка импортировать стандартную библиотекуdecimal
модуль может завершиться неудачей при попытке из исходного файла с таким именемnumbers.py
или в проекте, у которого есть такой исходный файл.В одном особенно опасном случае наличие файла с именем
token.py
в проекте (или текущем рабочем каталоге при запуске 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)Обратная трассировка сообщает нам все, что нам нужно знать: вызов
help
запускает отложенный импорт стандартной библиотекиpydoc
, который косвенно пытается импортировать стандартную библиотекуtoken
, но находит нашу,token.py
которая не содержит соответствующего имени. В более старых версиях Python было еще хуже:tokenize
выполнялся звездообразный импорт изtoken
, а затем его код верхнего уровня пытался использовать определенное там имя, что приводило кNameError
- и трассировке стека, в котором не упоминалось имя файлаtoken.py
.Python может импортировать файлы с кэшированным байт-кодом (
.pyc
), даже если соответствующий исходный файл переименован или удален. Это предназначено для ускорения импорта, но создает дополнительный шаг для поддержания "чистоты" каталогов кода. После переименования любого.py
файла, чтобы решить подобную проблему, обязательно проверьте также кэш байт-кода.
Решение проблемы
Конечно, стандартный совет прост и понятен: просто переименуйте .py
файлы, которые были импортированы по ошибке, и удалите все файлы с кэшированным байт-кодом .pyc
для этих модулей. По умолчанию в Python 3.x .pyc
файлы для любых .py
файлов внутри папки будут помещены во вложенную папку со специальным именем __pycache__
; в 2.x они просто появились рядом с соответствующими .py
файлами.
Однако, хотя этот подход быстро решает насущную проблему для большинства людей, сталкивающихся с ним, совет не очень хорошо масштабируется. Существует множество потенциально проблемных имен; в то время как большинство проектов не будут заинтересованы в включении исходного файла с именем, например, os.py
, некоторые другие имена могут быть более желательными или их сложнее обойти. Итак, вот несколько других полезных методов:
Контролирующий sys.path
Конечно, поскольку проблема вызвана sys.path
указанием, что абсолютный импорт должен сначала просматриваться в текущем проекте, этого можно избежать, просто изменив sys.path
.
Проблемный путь описан в документации как "потенциально небезопасный путь". При использовании интерактивного запроса Python это будет пустая строка (относительный путь, эквивалентный '.'
) - т. е. Текущий рабочий каталог для процесса Python, отражающий любые изменения, внесенные с помощью, например, os.chdir
. Для сценариев драйверов, запускаемых нормально (python driver.py
), это будет каталог, в котором находится скрипт, в качестве абсолютного пути (не обязательно текущий рабочий каталог, поскольку путь может быть указан в командной строке, например python path/to/driver.py
). Для модулей, запускаемых с использованием -m
флага командной строки, это будет начальный текущий рабочий каталог (не обязательно, где расположен модуль, но также и абсолютный путь, на который не повлияет os.chdir
).
Чтобы избежать ввода этого пути sys.path
, выполните одно из следующих действий:
В Python 3.11 и выше, установите
PYTHONSAFEPATH
переменную окружения или используйте-P
параметр командной строки для Python.В Python 3.4 и выше используйте
-I
опцию командной строки для запуска в изолированном режиме. Это, однако, имеет несколько других эффектов: будут игнорироваться переменные среды, такие какPYTHONPATH
иPYTHONHOME
, а также будет пропущено добавление каталога пользовательских пакетов сайта в path (следовательно, код будет не иметь доступа к библиотекам сторонних производителей, которые были установлены с использованием--user
опции для Pip).Если все остальное не помогает, рассмотрите возможность манипулирования вручную
sys.path
. Это беспорядочно и подвержено ошибкам, ноsys.path
это всего лишь обычный список строк с путями к файлам, и изменение его содержимого повлияет на будущиеimport
файлы. (Не пытайтесь заменить объект list; это не сработает, потому что Python не ищет его по имениsys.path
, а использует жестко запрограммированную внутреннюю ссылку. Невозможно уничтожить или заменить этот объект из Python;sys.path
это просто имя, которое инициализируется для ссылки на него.)
Имейте в виду, что удаление "небезопасного" пути предотвратит работу преднамеренного абсолютного импорта внутри пакета. Это неудобно для некоторых небольших проектов, но также является хорошей причиной изучить правильную организацию пакетов. Кстати, об этом:
Использование относительного импорта внутри пакетов
Хорошо организованный проект на Python обычно состоит из одного или нескольких (обычно только одного) пакетов, хранящихся во вложенных папках основной папки проекта, плюс одного или нескольких сценариев драйверов, размещенных вне вложенных папок пакета. Скрипты драйвера обычно используют единый абсолютный импорт для доступа к функциональности пакета, реализуя при этом некоторую простую логику оболочки (например, для анализа аргументов командной строки, форматирования и сообщения о неперехваченных исключениях и т.д.). Тем временем пакет будет использовать относительный импорт повсюду для своего собственного содержимого и абсолютный импорт только там, где это необходимо для доступа к другим пакетам (стандартной библиотеке и зависимостям сторонних производителей).
Например, проект может быть организован следующим образом:
project
├── src
│ └── my_package
│ └── x.py
│ └── y.py
│ └── z.py
└── driver.py
Код в driver.py
будет использовать абсолютный импорт в пакет, например:
from my_package.x import entry_point
if __name__ == '__main__':
entry_point()
(Если для анализа аргументов командной строки необходима логика, обычно она должна быть в драйвере, а не в коде пакета.)
Тогда код внутри пакета будет использовать относительный импорт - поэтому x.py
может содержать что-то вроде:
from .y import first_thing
from .z import second_thing
def entry_point():
first_thing()
second_thing()
Это дает лучшее из обоих миров: начальный абсолютный импорт настраивает пакет верхнего уровня так, что относительный импорт будет работать, а относительный импорт будет исключен в зависимости от sys.path
конфигурации. Даже без выполнения шагов по настройке sys.path
обычно он включает папку со скриптами драйвера, но не какие-либо папки пакетов; таким образом, это также автоматически позволяет избежать конфликтов путей импорта. (Абсолютный импорт не найдет содержимое пакета, если не будет указан соответствующий путь к пакету; но обычно, когда это происходит, импорт из текущего пакета был преднамеренным.)
Это также позволяет избежать установки ловушек для следующего проекта, который имеет этот в качестве зависимости. Скажем, например, что мы реализуем и публикуем API
пакет, а кто-то другой пишет, Client
который имеет API
зависимость. Поскольку API
код будет использовать относительный импорт для других API
функций (скажем, from . import functionality
), собственный Client
проект functionality.py
не вызовет проблем.
Для работы начального абсолютного импорта, конечно, в должна быть указана папка пакета верхнего уровняsys.path
. Однако обычно это достигается путем установки пакета, поэтому проблем не возникает.
Отключение кэширования байт - кода
Если файл необходимо переименовать, избежать проблем с .pyc
файлами будет проще, если их просто не существует в первую очередь. В конце концов, они не нужны; они, опять же, просто предназначены для ускорения импорта при последующих запусках программы.
Чтобы подавить .pyc
генерацию файла, используйте -B
параметр командной строки или установите PYTHONDONTWRITEBYTECODE
переменную окружения. (Встроенного решения для удаления всех существующих .pyc
файлов нет, но это достаточно легко реализовать вручную, используя, например, shutil
стандартный библиотечный модуль.)
Linting, чтобы избежать проблемных имен
Рассмотрите возможность использования сторонних инструментов (таких как плагины IDE) для предупреждения об именах файлов, используемых стандартной библиотекой или библиотеками сторонних производителей в проекте. В Python 3.10 и выше полный список имен стандартных библиотечных модулей также доступен как sys.stdlib_module_names
. Сюда входят имена, которые могут отсутствовать в текущей установке Python (например, компоненты, зависящие от операционной системы, или вещи, которые иногда отключены или опущены, такие как Tkinter).
Ответ 4
просто запишите импорт, который вызывает ошибку, до последнего
ex:
import requests
import flask
import numpy
если import flask
вызывает ошибку, переместите это в конец импорта
solution:-
import requests
import numpy
import flask