Nie można zaimportować języka Python z innego folderu

90

Nie mogę zmusić Pythona do zaimportowania modułu w podfolderze. Pojawia się błąd, gdy próbuję utworzyć wystąpienie klasy z zaimportowanego modułu, ale sam import się udaje. Oto moja struktura katalogów:

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

Oto zawartość 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

I 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)

Błąd to: AttributeError: obiekt „moduł” nie ma atrybutu „Użytkownik”

ryeguy
źródło
1
Czy możesz wkleić komunikat o błędzie?
Harley Holcombe,
Jeśli z user.py, chcę zaimportować server.py. Co ja robię?
Chandra Kanth

Odpowiedzi:

142

Uważam, że musisz utworzyć plik o nazwie __init__.pyw katalogu Models, aby Python traktował go jako moduł.

Następnie możesz:

from Models.user import User

Możesz dołączyć kod do __init__.py(na przykład kod inicjujący, którego potrzebuje kilka różnych klas) lub pozostawić to pole puste. Ale musi tam być.

Dana
źródło
2
Dzięki, nigdy wcześniej nie słyszałem o pakietach.
ryeguy
1
Właściwie podczas importowania Python traktuje blah.py i blah / __ init__.py dokładnie tak samo.
pi.
2
To było nieoczywiste ... I mówię sobie: RTFM FMF
Alexander.Iljushkin
1
Zauważ, że to nie zadziała, jeśli używasz server.py z innego katalogu.
kchoi
1
co jeśli server.py znajduje się w innym folderze?
Mandroid
24

Musisz utworzyć __init__.pyw Modelspodfolderze. Plik może być pusty. Definiuje pakiet.

Następnie możesz:

from Models.user import User

Przeczytaj wszystko na ten temat w samouczku Pythona tutaj .

Jest to również dobry artykuł o organizacji plików projektów Pythona tutaj .

nosklo
źródło
A jeśli plik NIE jest pusty?
darkgaze
12

importuj użytkownika

u = user.User () #error w tej linii

Z powodu braku __init__ wspomnianego powyżej, można by się spodziewać błędu ImportError, który wyjaśniłby problem.

Nie otrzymujesz go, ponieważ „użytkownik” jest również istniejącym modułem w bibliotece standardowej. Twoja instrukcja import przechwytuje tę jedną i próbuje znaleźć w niej klasę User; to nie istnieje i tylko wtedy pojawia się błąd.

Ogólnie dobrym pomysłem jest, aby import był absolutny:

import Server.Models.user

aby uniknąć tego rodzaju dwuznaczności. Rzeczywiście, z Pythona 2.7 „import user” w ogóle nie będzie wyglądał względem bieżącego modułu.

Jeśli naprawdę chcesz importów względnych, możesz je mieć jawnie w Pythonie 2.5 i nowszych, używając nieco brzydkiej składni:

from .user import User
bobince
źródło
9

Prawidłowy sposób zaimportowania modułu znajdującego się w folderze nadrzędnym, gdy nie masz standardowej struktury pakietu, to:

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

(możesz połączyć ostatnie dwie linie, ale w ten sposób jest łatwiejszy do zrozumienia).

To rozwiązanie jest wieloplatformowe i jest na tyle ogólne, że nie trzeba go modyfikować w innych okolicznościach.

glarrain
źródło
7

Brakuje __init__.py. Z samouczka Pythona:

Pliki __init__.py są wymagane, aby Python traktował katalogi jako zawierające pakiety; ma to na celu zapobieżenie przypadkowemu ukryciu prawidłowych modułów, które pojawiają się później na ścieżce wyszukiwania modułów, przez katalogi o wspólnej nazwie, takiej jak łańcuch. W najprostszym przypadku __init__.py może być po prostu pustym plikiem, ale może również wykonać kod inicjujący dla pakietu lub ustawić zmienną __all__, opisaną później.

Umieść pusty plik o nazwie __init__.py w swoim katalogu Models, a wszystko powinno być złote.

Harper Shelby
źródło
1

jak wypisujesz parametry os.path.dirname.... polecenie?

import os, sys
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(CURRENT_DIR))
user3440815
źródło
0

Moim preferowanym sposobem jest umieszczenie __init__.py w każdym katalogu zawierającym moduły, które są używane przez inne moduły, aw punkcie wejścia, nadpisanie sys.path, jak poniżej:

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

Dzięki temu pliki w określonych katalogach są widoczne do importu i mogę zaimportować użytkownika z Server.py.

kchoi
źródło
0

Po przejrzeniu odpowiedzi udzielonych przez tych autorów powyżej - Zorglub29, Tom, Mark, Aaron McMillin, lucasamaral, JoeyZhao, Kjeld Flarup, Procyclinsur, martin.zaenker, tooty44 i debugowaniu problemu, z którym miałem do czynienia, odkryłem inny przypadek użycia z powodu do którego miałem ten problem. Dlatego dodam poniżej moje obserwacje w celach informacyjnych.

W swoim kodzie miałem cykliczny import klas. Na przykład:

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)

Pojawił się następujący błąd, gdy próbowałem wykonać python -m pytest tests / test_utilities.py do wykonywania UT napisanych w 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'

Sposób, w jaki rozwiązałem ten błąd, polegał na ponownym rozłożeniu kodu w celu przeniesienia funkcjonalności w klasie cyklicznego importu, aby móc usunąć cykliczny import klas.

Zauważ, że mam __init__.pyplik w moim folderze „ src ”, a także w folderze „ testy ” i nadal byłem w stanie pozbyć się błęduImportError ” po prostu przez ponowne uwzględnienie kodu.

Poniższy link stackoverflow zawiera znacznie więcej szczegółów na temat zależności cyklicznych w Pythonie .

shekharlondhe
źródło