__İnit__.py ne anlama geliyor?


Yanıtlar:


1454

Eskiden bir paketin gerekli bir parçasıydı ( eski, 3.3'ten önceki "normal paket" , daha yeni 3.3+ "ad alanı paketi" değil ).

İşte belgeler.

Python iki tür paket tanımlar, normal paketler ve ad alanı paketleri. Normal paketler, Python 3.2 ve önceki sürümlerde var oldukları için geleneksel paketlerdir. Normal bir paket genellikle __init__.pydosya içeren bir dizin olarak uygulanır . Normal bir paket içe aktarıldığında, bu __init__.pydosya dolaylı olarak yürütülür ve tanımladığı nesneler paketin ad alanındaki adlara bağlanır. __init__.pyDosya başka modül içerebilir aynı Python kodunu içerebilir ve içe aktarıldığında Python modülüne bazı ek özellikler eklemek olacaktır.

Ancak sadece bağlantıyı tıklayın, bir örnek, daha fazla bilgi ve ad alanı paketlerinin açıklaması, olmayan paketler türünü içerir __init__.py.


187
Bu şu anlama gelir: "Bu, dize gibi ortak bir ada sahip dizinlerin, daha sonra modül arama yolunda gerçekleşecek geçerli modülleri yanlışlıkla gizlemesini önlemek için yapılır"?
Carl G

97
@CarlG Python , içe aktarma ifadelerindeki isimleri çözmek için bir dizin listesi arar . Bunlar herhangi bir dizin olabileceğinden ve son kullanıcı tarafından isteğe bağlı olanlar eklenebileceğinden, geliştiricilerin docs örneğinde 'string' gibi geçerli bir Python modülüyle bir ad paylaşan dizinler hakkında endişelenmeleri gerekir. Bunu hafifletmek için, boş olsa bile _ _ init _ _.py (boşluk yok) adlı bir dosya içermeyen dizinleri yok sayar.
İki

186
@CarlG Bunu deneyin. 'Datetime' adlı bir dizin oluşturun ve içinde iki boş dosya yapın, init.py dosyası (alt çizgi ile) ve datetime.py. Şimdi bir tercüman açın, sys dosyasını içe aktarın ve sys.path.insert(0, '/path/to/datetime')bu yolu yeni yaptığınız dizine giden yolla değiştirin. Şimdi böyle bir şey deneyin from datetime import datetime;datetime.now(). Bir AttributeError almalısınız (çünkü boş dosyanızı şimdi içe aktarıyor). Bu adımları boş init dosyası oluşturmadan tekrarlayacak olsaydınız bu olmazdı. Önlemek istediği bu.
İki Bit Simyacı

4
@ DarekNędza Bir Python yorumlayıcısını açıp from datetime import datetimehatasız bir şekilde yayınlayamıyorsanız yanlış ayarlanmış bir şey var . 2.3 sürümüne kadar bu iyi!
İki Bit Simyacı

5
@SWang: Bu yanlış: builtinsyerleşik modülleri değil, yerleşik işlevleri ve sınıfları listeler (cf. docs.python.org/3/tutorial/modules.html#the-dir-function ). Listede istiyorsanız yerleşik modüllerin , do import sys; print(sys.builtin_module_names)(Bkz docs.python.org/3/library/sys.html#sys.builtin_module_names ).
Maggyero

842

Adlandırılmış dosyalar __init__.pydiskteki dizinleri Python paketi dizinleri olarak işaretlemek için kullanılır. Dosyalara sahipseniz

mydir/spam/__init__.py
mydir/spam/module.py

ve mydirsenin yolunda, sen kod alabilirsiniz module.pyolarak

import spam.module

veya

from spam import module

__init__.pyDosyayı kaldırırsanız , Python artık bu dizinin içinde alt modülleri aramaz, bu nedenle modülü içe aktarma girişimleri başarısız olur.

__init__.pyDosyası genellikle boş, ancak, örneğin, yukarıda verilen vb daha uygun adı, tutma kolaylığı fonksiyonları altında paketin seçilen kısımlarını vermek için kullanılabilir, init modülünün içeriği olarak erişilebilir

import spam

dayalı bu


96
Güncelleme: Dosya __init__.pyPython 2.X altında gerekliydi ve yine de Python 2.7.12 (test ettim) altında gerekli ancak artık (iddia edildiği gibi) Python 3.3'ten itibaren gerekli değil ve Python 3.4.3 (I test etti). Daha fazla bilgi için stackoverflow.com/questions/37139786 adresine bakın.
Rob_before_edits

4
Kullanma. Bu "ad alanı" paketidir, normal bir paket değildir. ad alanı paketi çok nadir kullanım durumlarında kullanılır. Kullanırken bilmeniz gerekmeyebilir. Sadece kullan __init__.py.
metan

2
ancak varsa setup.pyve kullanırsanız , her dizinde find_packages()olması gerekir __init__.py. Bkz. Stackoverflow.com/a/56277323/7127824
techkuz

484

Python paket olarak bir dizin etiketleme ve tanımlamanın yanı sıra __all__, __init__.pypaket düzeyinde herhangi değişken tanımlamak için izin verir. Bir paket, API benzeri bir şekilde sık sık içe aktarılacak bir şey tanımlarsa bunu yapmak genellikle uygundur. Bu model Pythonic "düz iç içe olandan daha iyi" felsefesine bağlılığı teşvik eder.

Bir örnek

İşte benim projelerimden bir örnek, hangi sık sık benim veritabanımı ile etkileşim için bir sessionmakerçağrı ithal Session. Birkaç modül içeren bir "veritabanı" paketi yazdım:

database/
    __init__.py
    schema.py
    insertions.py
    queries.py

Benim __init__.pyaşağıdaki kodu içerir:

import os

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

engine = create_engine(os.environ['DATABASE_URL'])
Session = sessionmaker(bind=engine)

SessionBurada tanımladığımdan , aşağıdaki sözdizimini kullanarak yeni bir oturum başlatabilirim. Bu kod, "veritabanı" paket dizininin içinden veya dışından yürütülürken kullanılanla aynıdır.

from database import Session
session = Session()

Tabii ki, bu küçük bir kolaylıktır - alternatif, Sessionveritabanı paketimdeki "create_session.py" gibi yeni bir dosyada tanımlamak ve aşağıdakileri kullanarak yeni oturumlar başlatmak olacaktır:

from database.create_session import Session
session = Session()

daha fazla okuma

Burada uygun kullanımları kapsayan oldukça ilginç bir reddit ipliği __init__.pyvar:

http://www.reddit.com/r/Python/comments/1bbbwk/whats_your_opinion_on_what_to_include_in_init_py/

Çoğunluk __init__.py, "açık, örtükten daha iyidir" felsefesini ihlal etmemek için dosyaların çok ince olması gerektiği görüşündedir.


3
engine, sessionmaker, create_engineVe ostüm ayrıca alınabilir databaseo ad alanının bir karmaşa yaptık gibi şimdi ... görünüyor.
ArtOfWarfare

9
@ArtOfWarfare, __all__ = [...]içe aktarılanları sınırlamak için kullanabilirsiniz import *. Ama bunun dışında, evet, dağınık bir üst düzey ad alanına bırakıldınız.
Nathan Gould

'VERİTABANI URL'sinin' ne olduğunu öğrenebilir miyim? Ben 'mysql + mysqldb: // root: python @ localhost: 3306 / test' ile create_engine ekleyerek bu çoğaltmaya çalıştım ama çalışmıyor. Teşekkürler.
SunnyBoiz

2
Nasıl erişim 'Session' sınıf tanımlanan olur init örn quieries.py inside paketinden?
vldbnc

252

Bunun 2 ana nedeni var __init__.py

  1. Kolaylık sağlamak için: diğer kullanıcıların işlevlerinizin paket hiyerarşisinde tam yerini bilmesi gerekmez.

    your_package/
      __init__.py
      file1.py
      file2.py
        ...
      fileN.py
    # in __init__.py
    from file1 import *
    from file2 import *
    ...
    from fileN import *
    # in file1.py
    def add():
        pass

    o zaman diğerleri tarafından add ()

    from your_package import add

    file1 bilmeden,

    from your_package.file1 import add
  2. Bir şeyin başlatılmasını istiyorsanız; örneğin, günlük kaydı (en üst düzeye konulması gerekir):

    import logging.config
    logging.config.dictConfig(Your_logging_config)

7
Cevabınızı okumadan önce, bir işlevi yerine açıkça çağırmanın iyi bir uygulama olduğunu düşündüm.
Aerin

2
@Aerin kısa ifadelerin (veya bu durumda öznel sonuçların) her zaman doğru olduğunu düşünmemek daha iyi olur. Adresinden içe aktarma __init__.pybazen yararlı olabilir, ancak her zaman değil.
Tobias Sette

2
Bu kod içe aktarma zamanında veya çalışma zamanında mı yürütülüyor?
user1559897

111

__init__.pyDosya modül olarak bunu içeren Python tedavi dizinleri yapar.

Ayrıca, bu bir modüle yüklenecek ilk dosyadır, bu nedenle modülü her yüklendiğinde çalıştırmak istediğiniz kodu yürütmek veya dışa aktarılacak alt modülleri belirlemek için kullanabilirsiniz.


89

Python 3.3 olduğundan __init__.py, dizinleri içe aktarılabilir Python paketleri olarak tanımlamak artık gerekli değildir.

PEP 420'yi kontrol edin : Örtülü Ad Alanı Paketleri :

__init__.pyİşaretçi dosyaları gerektirmeyen ve otomatik olarak birden çok yol parçasına yayılabilen paket dizinleri için yerel destek ( PEP 420'de açıklandığı gibi ad alanı paketlerine çeşitli üçüncü taraf yaklaşımlarından esinlenerek )

İşte test:

$ mkdir -p /tmp/test_init
$ touch /tmp/test_init/module.py /tmp/test_init/__init__.py
$ tree -at /tmp/test_init
/tmp/test_init
├── module.py
└── __init__.py
$ python3

>>> import sys
>>> sys.path.insert(0, '/tmp')
>>> from test_init import module
>>> import test_init.module

$ rm -f /tmp/test_init/__init__.py
$ tree -at /tmp/test_init
/tmp/test_init
└── module.py
$ python3

>>> import sys
>>> sys.path.insert(0, '/tmp')
>>> from test_init import module
>>> import test_init.module

referanslar:
https://docs.python.org/3/whatsnew/3.3.html#pep-420-implicit-namespace-packages
https://www.python.org/dev/peps/pep-0420/
__init__. py Python 3 paketleri için gerekli değil mi?


3
Bu "ad alanı" paketidir. Normal paket için kullanmayın.
metan

@methan, yorumunuzu biraz daha açıklayabilir misiniz?
Robert Lugg


57

Python'da paketin tanımı çok basittir. Java gibi, hiyerarşik yapı ve dizin yapısı aynıdır. Ama __init__.pybir paket içinde olmalısın . __init__.pyDosyayı aşağıdaki örnekle açıklayacağım :

package_x/
|--  __init__.py
|--    subPackage_a/
|------  __init__.py
|------  module_m1.py
|--    subPackage_b/
|------  __init__.py
|------  module_n1.py
|------  module_n2.py
|------  module_n3.py

__init__.pymevcut olduğu sürece boş olabilir. Dizinin bir paket olarak görülmesi gerektiğini belirtir. Tabii ki, __init__.pyuygun içeriği de ayarlayabilirsiniz.

Module_n1 modülüne bir işlev eklersek:

def function_X():
    print "function_X in module_n1"
    return

Koşu sonrası:

>>>from package_x.subPackage_b.module_n1 import function_X
>>>function_X()

function_X in module_n1 

Sonra hiyerarşi paketini takip ettik ve module_n1 işlevini çağırdık. __init__.pySubPackage_b'de şu şekilde kullanabiliriz :

__all__ = ['module_n2', 'module_n3']

Koşu sonrası:

>>>from package_x.subPackage_b import * 
>>>module_n1.function_X()

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named module_n1

Bu nedenle, * import kullanarak modül paketi __init__.pyiçeriğe tabidir .


Setup.py'm aynı içe aktarmayı paketlenmiş kitaplık üzerinden nasıl yapacak? from package_x.subPackage_b.module_n1 import function_X
technazi

bu yüzden anahtar götürmek burada "kullanarak * alma, modül paketi init .py içeriğine tabidir "
Minnie

54

Python bir __init__.pydosya olmadan çalışsa da yine de bir tane eklemelisiniz.

Bir paketin modül olarak ele alınması gerektiğini belirtir, bu nedenle paketini ekleyin (boş olsa bile).

Bir __init__.pydosyayı gerçekten kullanabileceğiniz bir durum da vardır :

Aşağıdaki dosya yapısına sahip olduğunuzu düşünün:

main_methods 
    |- methods.py

Ve bunu methods.pyiçeriyordu:

def foo():
    return 'foo'

Kullanmak foo()için aşağıdakilerden birine ihtiyacınız olacaktır:

from main_methods.methods import foo # Call with foo()
from main_methods import methods # Call with methods.foo()
import main_methods.methods # Call with main_methods.methods.foo()

Belki orada ( örneğin çalışma zamanları / bağımlılıklar) methods.pyiçinde kalmanız gerekir (veya istersiniz main_methods) ama sadece içe aktarmak istiyorsunuz main_methods.


Eğer adını değiştirdiyseniz methods.pyiçin __init__.pyo zaman kullanabilirsiniz foo()sadece ithal ederek main_methods:

import main_methods
print(main_methods.foo()) # Prints 'foo'

Bu __init__.py, paketin bir parçası olarak değerlendirildiği için çalışır .


Bazı Python paketleri aslında bunu yapar. Bir örnek ile JSON çalışan, import jsonaslında ithal etmektedir __init__.pydan json(pakete burada paket dosyası yapısını görmek ):

Kaynak kodu: Lib/json/__init__.py


39

__init__.py içinde bulunduğu dizine yüklenebilir bir modül gibi davranacaktır.

Kod okumayı tercih eden insanlar için, İki Bit Simyacı'nın yorumunu buraya yazdım .

$ find /tmp/mydir/
/tmp/mydir/
/tmp/mydir//spam
/tmp/mydir//spam/__init__.py
/tmp/mydir//spam/module.py
$ cd ~
$ python
>>> import sys
>>> sys.path.insert(0, '/tmp/mydir')
>>> from spam import module
>>> module.myfun(3)
9
>>> exit()
$ 
$ rm /tmp/mydir/spam/__init__.py*
$ 
$ python
>>> import sys
>>> sys.path.insert(0, '/tmp/mydir')
>>> from spam import module
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named spam
>>> 

30

Diğer python dosyalarının içe aktarılmasını kolaylaştırır. Bu dosyayı diğer py dosyalarını içeren bir dizine (şeyler söyleyin) yerleştirdiğinizde, import stuff.other gibi bir şey yapabilirsiniz.

root\
    stuff\
         other.py

    morestuff\
         another.py

__init__.pyPython, şeyler için kaynak kodunun nerede olduğunu bilmediğinden ve bir paket olarak tanıyamadığından, dizin öğeleri içinde bu olmadan , other.py dosyasını içe aktaramazsınız.


2
Projemde aynı yapıya sahibim (python 3.4) ama another.py diğer other.py'yi göremiyorum. İçe aktarımı nasıl yapmalıyım? root.stuff ithal diğer? VSCode hata ayıklama modunda çalışır, ancak komut satırında çalışmaz. Herhangi bir fikir?
rodrigorf

10

Bir __init__.pydosya içe aktarmayı kolaylaştırır. Bir __init__.pypaket içinde bir varsa, işlev a()dosyadan şu şekilde içe aktarılabilir b.py:

from b import a

Ancak bu olmadan doğrudan içe aktaramazsınız. Sistem yolunu değiştirmeniz gerekir:

import sys
sys.path.insert(0, 'path/to/b.py')

from b import a

ne demek " işlev a () b.py [snippet] dosyasından alınabilir, ancak bu olmadan doğrudan içe aktaramazsınız. "? A () işlevini __init__.py olmadan b.py dosyasından alabilirim.
aderchox
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.