Não consigo fazer o Python importar de uma pasta diferente

90

Não consigo fazer o Python importar um módulo em uma subpasta. Recebo o erro quando tento criar uma instância da classe do módulo importado, mas a importação em si é bem-sucedida. Aqui está minha estrutura de diretório:

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

Aqui está o conteúdo de 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

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

O erro é: AttributeError: o objeto 'módulo' não tem atributo 'Usuário'

Ryeguy
fonte
1
Você pode colar a mensagem de erro?
Harley Holcombe
Se for de user.py, desejo importar server.py. O que eu faço?
Chandra Kanth

Respostas:

139

Acredito que você precise criar um arquivo chamado __init__.pyno diretório Models para que o python o trate como um módulo.

Então você pode fazer:

from Models.user import User

Você pode incluir o código no __init__.py(por exemplo, código de inicialização que algumas classes diferentes precisam) ou deixá-lo em branco. Mas deve estar lá.

Dana
fonte
2
Obrigado, nunca tinha ouvido falar de pacotes antes disso.
ryeguy
1
Na verdade, ao importar Python, blah.py e blah / __ init__.py são tratados exatamente da mesma forma.
pi.
2
Isso não foi óbvio ... E eu digo a mim mesmo: RTFM FMF
Alexander.Iljushkin
1
Observe que isso não funcionará se você estiver executando server.py de outro diretório.
kchoi
1
e se server.py estiver em outra pasta?
Mandroid
24

Você tem que criar __init__.pyna Modelssubpasta. O arquivo pode estar vazio. Ele define um pacote.

Então você pode fazer:

from Models.user import User

Leia tudo sobre isso no tutorial de python, aqui .

Também há um bom artigo sobre organização de arquivos de projetos Python aqui .

nosklo
fonte
E se o arquivo NÃO estiver vazio?
darkgaze
12

importar usuário

u = user.User () #error nesta linha

Por causa da falta de __init__ mencionada acima, você esperaria um ImportError que tornaria o problema mais claro.

Você não recebe um porque 'usuário' também é um módulo existente na biblioteca padrão. Sua instrução import pega aquele e tenta encontrar a classe User dentro dele; isso não existe e só então você obterá o erro.

Geralmente é uma boa ideia tornar sua importação absoluta:

import Server.Models.user

para evitar esse tipo de ambigüidade. Na verdade, a partir do Python 2.7, 'importar usuário' não parecerá de forma alguma relativo ao módulo atual.

Se você realmente deseja importações relativas, pode tê-las explicitamente no Python 2.5 e superior usando a sintaxe um tanto feia:

from .user import User
bobince
fonte
9

A maneira certa de importar um módulo localizado em uma pasta pai, quando você não tem uma estrutura de pacote padrão, é:

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

(você pode mesclar as duas últimas linhas, mas desta forma é mais fácil de entender).

Esta solução é multiplataforma e geral o suficiente para não precisar ser modificada em outras circunstâncias.

brilho
fonte
7

Você está perdendo __init__.py. Do tutorial Python:

Os arquivos __init__.py são necessários para fazer o Python tratar os diretórios como contendo pacotes; isso é feito para evitar que diretórios com um nome comum, como string, ocultem inadvertidamente módulos válidos que ocorrem posteriormente no caminho de pesquisa do módulo. No caso mais simples, __init__.py pode ser apenas um arquivo vazio, mas também pode executar o código de inicialização para o pacote ou definir a variável __all__, descrita posteriormente.

Coloque um arquivo vazio chamado __init__.py em seu diretório de Modelos, e tudo deve ser dourado.

Harper Shelby
fonte
1

como você escreve os parâmetros os.path.dirname.... comando?

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

Minha maneira preferida é ter __init__.py em cada diretório que contém módulos que são usados ​​por outros módulos e, no ponto de entrada, substituir sys.path conforme abaixo:

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

Isso torna os arquivos em diretórios especificados visíveis para importação e posso importar o usuário de Server.py.

kchoi
fonte
0

Depois de analisar as respostas dadas por esses contribuidores acima - Zorglub29, Tom, Mark, Aaron McMillin, lucasamaral, JoeyZhao, Kjeld Flarup, Procyclinsur, martin.zaenker, tooty44 e depurando o problema que estava enfrentando, descobri um caso de uso diferente devido para o qual eu estava enfrentando esse problema. Portanto, adiciono minhas observações abaixo para referência de qualquer pessoa.

No meu código, tive uma importação cíclica de classes. Por exemplo:

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)

Recebi o seguinte erro quando tentei executar python -m pytest tests / test_utilities.py para executar UTs escritos em 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'

A maneira como resolvi o erro foi refatorando meu código para mover a funcionalidade na classe de importação cíclica para que eu pudesse remover a importação cíclica de classes.

Observe, eu tenho o __init__.pyarquivo na minha pasta ' src ', bem como na pasta ' testes ' e ainda fui capaz de me livrar do ' ImportError ' apenas reformulando o código.

O seguinte link stackoverflow fornece muito mais detalhes sobre a dependência circular em Python .

Shekharlondhe
fonte