Не удается заставить Python импортировать из другой папки

91

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

Server
    -server.py
    -Models
        --user.py

Вот содержимое server.py:

from sys import path
from os import getcwd
path.append(getcwd() + "\\models") #Yes, i'm on windows
print path
import user

u=user.User() #error on this line

И user.py:

class User(Entity):
    using_options(tablename='users')

    username = Field(String(15))
    password = Field(String(64))
    email    = Field(String(50))
    status   = Field(Integer)
    created  = Field(DateTime)

Ошибка: AttributeError: объект 'module' не имеет атрибута 'User'

ржаной
источник
1
Вы можете вставить сообщение об ошибке?
Harley Holcombe
Если из user.py, я хочу импортировать server.py. Что мне делать?
Чандра Кант

Ответы:

142

Я считаю, что вам нужно создать файл с именем __init__.py в каталоге Models, чтобы python рассматривал его как модуль.

Тогда вы сможете:

from Models.user import User

Вы можете включить код в __init__.py(например, код инициализации, который нужен нескольким различным классам) или оставить его пустым. Но он должен быть там.

Дана
источник
2
Спасибо, никогда раньше не слышал о пакетах.
ryeguy
1
Фактически, при импорте Python обрабатывает blah.py и blah / __ init__.py точно так же.
пи.
2
Это было неочевидно ... И я говорю себе: RTFM FMF
Александр.Илюшкин
1
Обратите внимание, что это не сработает, если вы запускаете server.py из другого каталога.
kchoi
1
что, если server.py находится в другой папке?
Mandroid
24

Вы должны создать __init__.pyв Modelsподпапке. Файл может быть пустым. Он определяет пакет.

Тогда вы сможете:

from Models.user import User

Прочтите об этом в учебнике по Python здесь .

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

носкло
источник
Что делать, если файл НЕ пустой?
darkgaze
12

импортный пользователь

u = user.User () # ошибка в этой строке

Из-за отсутствия __init__, упомянутого выше, можно ожидать ошибки ImportError, которая проясняет проблему.

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

Как правило, рекомендуется сделать импорт абсолютным:

import Server.Models.user

чтобы избежать такой двусмысленности. Действительно, из Python 2.7 «импортный пользователь» вообще не будет выглядеть относительно текущего модуля.

Если вам действительно нужен относительный импорт, вы можете явно указать его в Python 2.5 и выше, используя несколько уродливый синтаксис:

from .user import User
бобинс
источник
9

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

import os, sys
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(CURRENT_DIR))

(вы можете объединить последние две строки, но этот способ легче понять).

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

Glarrain
источник
7

Вам не хватает __init__.py. Из учебника Python:

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

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

Харпер Шелби
источник
1

как вы выписываете параметры os.path.dirname.... команда?

import os, sys
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(CURRENT_DIR))
user3440815
источник
0

Я предпочитаю иметь __init__.py в каждом каталоге, содержащем модули, которые используются другими модулями, а в точке входа переопределить sys.path, как показано ниже:

def get_path(ss):
  return os.path.join(os.path.dirname(__file__), ss)
sys.path += [
  get_path('Server'), 
  get_path('Models')
]

Это делает файлы в указанных каталогах видимыми для импорта, и я могу импортировать пользователя из Server.py.

кчой
источник
0

Пройдя ответы, данные вышеупомянутыми участниками - Zorglub29, Tom, Mark, Aaron McMillin, lucasamaral, JoeyZhao, Kjeld Flarup, Procyclinsur, martin.zaenker, tooty44 и проблему, с которой я столкнулся, я обнаружил другой вариант использования из-за к которому я столкнулся с этой проблемой. Поэтому добавляю свои наблюдения ниже для справки.

В моем коде у меня был циклический импорт классов. Например:

src
 |-- utilities.py (has Utilities class that uses Event class)  
 |-- consume_utilities.py (has Event class that uses Utilities class)
 |-- tests
      |-- test_consume_utilities.py (executes test cases that involves Event class)

У меня возникла следующая ошибка, когда я попытался выполнить python -m pytest tests / test_utilities.py для выполнения UT, написанных в test_utilities.py.

ImportError while importing test module '/Users/.../src/tests/test_utilities.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_utilities.py:1: in <module>
    from utilities import Utilities
...
...
E   ImportError: cannot import name 'Utilities'

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

Обратите внимание: у меня есть __init__.pyфайл в папке src , а также в папке tests , и я все же смог избавиться от ошибки ImportError. , просто повторно код.

Следующая ссылка на stackoverflow предоставляет гораздо больше подробностей о круговой зависимости в Python .

Shekharlondhe
источник