Jak importować klasy zdefiniowane w __init__.py

105

Próbuję zorganizować niektóre moduły na własny użytek. Mam coś takiego:

lib/
  __init__.py
  settings.py
  foo/
    __init__.py
    someobject.py
  bar/
    __init__.py
    somethingelse.py

W programie lib/__init__.pychcę zdefiniować klasy, które będą używane podczas importowania lib. Jednak wydaje mi się, że nie mogę tego rozgryźć bez rozdzielenia klas na pliki i zaimportowania ich do formatu __init__.py.

Zamiast mówić:

    lib/
      __init__.py
      settings.py
      helperclass.py
      foo/
        __init__.py
        someobject.py
      bar/
        __init__.py
        somethingelse.py

from lib.settings import Values
from lib.helperclass import Helper

Chcę coś takiego:

    lib/
      __init__.py  #Helper defined in this file
      settings.py
      foo/
        __init__.py
        someobject.py
      bar/
        __init__.py
        somethingelse.py

from lib.settings import Values
from lib import Helper

Czy to możliwe, czy muszę rozdzielić klasę na inny plik?

EDYTOWAĆ

OK, jeśli zaimportuję lib z innego skryptu, mogę uzyskać dostęp do klasy pomocnika. Jak mogę uzyskać dostęp do klasy pomocnika z settings.py?

Przykładem tutaj opisuje Referencje wewnątrz opakowania. Cytuję „podmoduły często muszą odnosić się do siebie”. W moim przypadku lib.settings.py potrzebuje Helpera, a lib.foo.someobject potrzebuje dostępu do Helpera, więc gdzie powinienem zdefiniować klasę Helper?

scottm
źródło
Twój ostatni przykład powinien działać. Czy widzisz błąd ImportError? Czy możesz wkleić szczegóły?
Joe Holloway

Odpowiedzi:

81
  1. lib/Katalog nadrzędny ' ' musi znajdować się w sys.path.

  2. Twój „ lib/__init__.py” może wyglądać tak:

    from . import settings # or just 'import settings' on old Python versions
    class Helper(object):
          pass

Wtedy następujący przykład powinien zadziałać:

from lib.settings import Values
from lib import Helper

Odpowiedź na zredagowaną wersję pytania:

__init__.pyokreśla, jak Twój pakiet wygląda z zewnątrz. Jeśli chcesz użyć Helperw, settings.pyzdefiniuj Helperw innym pliku, np. „ lib/helper.py”.

.
| `- import_submodule.py
    `- lib
    | - __init__.py
    | - foo
    | | - __init__.py
    | `- someobject.py
    | - helper.py
    `- settings.py

2 katalogi, 6 plików

Komenda:

$ python import_submodule.py

Wynik:

settings
helper
Helper in lib.settings
someobject
Helper in lib.foo.someobject

# ./import_submodule.py
import fnmatch, os
from lib.settings import Values
from lib import Helper

print
for root, dirs, files in os.walk('.'):
    for f in fnmatch.filter(files, '*.py'):
        print "# %s/%s" % (os.path.basename(root), f)
        print open(os.path.join(root, f)).read()
        print


# lib/helper.py
print 'helper'
class Helper(object):
    def __init__(self, module_name):
        print "Helper in", module_name


# lib/settings.py
print "settings"
import helper

class Values(object):
    pass

helper.Helper(__name__)


# lib/__init__.py
#from __future__ import absolute_import
import settings, foo.someobject, helper

Helper = helper.Helper


# foo/someobject.py
print "someobject"
from .. import helper

helper.Helper(__name__)


# foo/__init__.py
import someobject
jfs
źródło
Jak mogę zaimportować klasę Helper w pliku settings.py w moim przykładzie?
scottm
Nie rozumiem, dlaczego jest to okólnik. Jeśli settings.py potrzebuje pomocnika i mówi, że foo.someobject.py potrzebuje pomocnika, gdzie sugerujesz, żebym to zdefiniował?
scottm
wow, słowo „pomocnik” naprawdę zaczyna tracić znaczenie w tym przykładzie. Jednak pokazałeś mi, czego szukałem.
scottm
3
upvoted dla "__init__.py określa, jak twój pakiet wygląda z zewnątrz."
Petri
19

Jeśli lib/__init__.pydefiniuje klasę Helper, to w settings.py możesz użyć:

from . import Helper

To działa, ponieważ. jest katalogiem bieżącym i działa jako synonim pakietu lib z punktu widzenia modułu ustawień. Zauważ, że nie jest konieczne eksportowanie Helpera przez __all__.

(Potwierdzone w Pythonie 2.7.10, działającym w systemie Windows).

yoyo
źródło
1
To działa dobrze dla mnie, downvoters proszę wyjaśnić swoje niezadowolenie?
jojo
8

Po prostu umieściłeś je w __init__.py.

A więc z test / classes.py:

class A(object): pass
class B(object): pass

... i test / __ init__.py:

from classes import *

class Helper(object): pass

Możesz importować test i mieć dostęp do A, B i pomocnika

>>> import test
>>> test.A
<class 'test.classes.A'>
>>> test.B
<class 'test.classes.B'>
>>> test.Helper
<class 'test.Helper'>
Aaron Maenpaa
źródło
Skąd robisz import?
Aaron Maenpaa
Powiedz, że chcę zaimportować pomocnika z lib / settings.py?
scottm
1
To prawie na pewno spowoduje cykliczny import (ustawienia zaimportują test, a test zaimportuje ustawienia) ... co spowoduje wygenerowanie opisanego przez Ciebie NameError. Jeśli potrzebujesz pomocników, które są używane wewnątrz pakietu, powinieneś zdefiniować wewnętrzny moduł, którego używa twój kod.
Aaron Maenpaa
1
Pomocnicy w init .py powinni być przeznaczone dla użytkowników zewnętrznych, aby uprościć interfejs API.
Aaron Maenpaa
1
Jeśli nie jest częścią API, to init .py naprawdę nie jest dla niego odpowiednim miejscem. lib / internal.py lub podobny byłby znacznie lepszy (co ma podwójny cel - zmniejszenie prawdopodobieństwa importu cyklicznego).
Aaron Maenpaa
5

Dodaj coś takiego do lib/__init__.py

from .helperclass import Helper

teraz możesz go zaimportować bezpośrednio:

from lib import Helper

Dawid Gosławski
źródło
4

Edytuj, ponieważ źle zrozumiałem pytanie:

Po prostu włóż Helperklasę __init__.py. To całkowicie pytoniczne. Po prostu wydaje się dziwne z języków takich jak Java.

Richard Levasseur
źródło
Nie zrozumiałeś. Jeśli to możliwe, chcę wyeliminować plik helperClass.py.
scottm
Richard nie jest jedynym, który źle zrozumiał. Twój przykład powinien działać. Co to jest śledzenie?
Troy J. Farrell
Cóż, w takim razie poprawnie przeczytałem dokumenty. Więc teraz właśnie zrobiłem przypadek testowy i działa dobrze.
scottm
Mam taką samą konfigurację w moim pakiecie, ale jestem coraz ImportError „nie może nazwa import Helper”
scottm
Myślę, że moim problemem jest to, że próbuję zaimportować klasę pomocnika z settings.py, jak mogę to zrobić?
scottm
2

Tak to mozliwe. Możesz także chcieć zdefiniować __all__w __init__.pyplikach. Jest to lista modułów, które zostaną zaimportowane, gdy to zrobisz

from lib import *
vartec
źródło
-6

Może to zadziała:

import __init__ as lib
Turion
źródło