Untuk apa __init__.py?


Jawaban:


1456

Ini digunakan untuk menjadi bagian yang diperlukan dari sebuah paket ( lama, pra-3.3 "paket reguler" , bukan 3.3 "paket namespace" yang lebih baru ).

Berikut dokumentasinya.

Python mendefinisikan dua jenis paket, paket reguler dan paket namespace. Paket reguler adalah paket tradisional seperti yang ada di Python 3.2 dan sebelumnya. Paket reguler biasanya diimplementasikan sebagai direktori yang berisi __init__.pyfile. Ketika paket reguler diimpor, __init__.pyfile ini secara implisit dieksekusi, dan objek yang didefinisikannya terikat dengan nama-nama dalam namespace paket. The __init__.pyFile dapat berisi kode Python yang sama bahwa setiap modul lainnya dapat berisi, dan Python akan menambahkan beberapa atribut tambahan untuk modul ketika diimpor.

Tapi cukup klik tautannya, itu berisi contoh, informasi lebih lanjut, dan penjelasan tentang paket namespace, jenis paket tanpa itu __init__.py.


187
Apa artinya ini: "ini dilakukan untuk mencegah direktori dengan nama umum, seperti string, dari menyembunyikan modul yang valid secara tidak sengaja yang terjadi kemudian pada jalur pencarian modul"?
Carl G

97
@CarlG Python mencari daftar direktori untuk menyelesaikan nama, misalnya, pernyataan impor. Karena ini bisa berupa direktori apa saja, dan yang sewenang-wenang dapat ditambahkan oleh pengguna akhir, pengembang harus khawatir tentang direktori yang kebetulan berbagi nama dengan modul Python yang valid, seperti 'string' dalam contoh dokumen. Untuk meringankan ini, ia mengabaikan direktori yang tidak mengandung file bernama _ _ init _ _.py (tanpa spasi), bahkan jika itu kosong.
Alchemist Dua-Bit

186
@CarlG Coba ini. Buat direktori yang disebut 'datetime' dan di dalamnya buat dua file kosong, file init.py (dengan garis bawah) dan datetime.py. Sekarang buka interpreter, import sys, dan issue sys.path.insert(0, '/path/to/datetime'), ganti path itu dengan path ke direktori apa pun yang baru saja Anda buat. Sekarang coba sesuatu seperti from datetime import datetime;datetime.now(). Anda harus mendapatkan AttributeError (karena mengimpor file kosong Anda sekarang). Jika Anda mengulangi langkah-langkah ini tanpa membuat file init kosong, ini tidak akan terjadi. Itulah yang dimaksudkan untuk dicegah.
Alchemist Dua-Bit

4
@ DarekNędza Anda telah memasang sesuatu yang salah jika Anda tidak bisa membuka juru bahasa Python dan mengeluarkannya from datetime import datetimetanpa kesalahan. Itu bagus sepanjang perjalanan kembali ke versi 2.3!
Alchemist Dua-Bit

5
@ Wang: Itu salah: builtinsdaftar fungsi dan kelas built-in, bukan modul built-in (lih. Docs.python.org/3/tutorial/modules.html#the-dir-function ). Jika Anda ingin daftar modul bawaan , lakukan import sys; print(sys.builtin_module_names)(lih. Docs.python.org/3/library/sys.html#sys.builtin_module_names ).
Maggyero

843

File yang diberi nama __init__.pydigunakan untuk menandai direktori pada disk sebagai direktori paket Python. Jika Anda memiliki file

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

dan mydirada di jalur Anda, Anda dapat mengimpor kode module.pysebagai

import spam.module

atau

from spam import module

Jika Anda menghapus __init__.pyfile, Python tidak akan lagi mencari submodul di dalam direktori itu, sehingga upaya untuk mengimpor modul akan gagal.

The __init__.pyfile biasanya kosong, tetapi dapat digunakan untuk mengekspor bagian yang dipilih dari paket dengan nama yang lebih nyaman, fungsi kenyamanan ditahan, dll Mengingat contoh di atas, isi modul init dapat diakses sebagai

import spam

berdasarkan ini


96
Pembaruan: File __init__.pydiperlukan di bawah Python 2.X dan masih diperlukan di bawah Python 2.7.12 (saya mengujinya) tetapi tidak lagi diperlukan dari (diduga) Python 3.3 dan seterusnya, dan tidak diperlukan di bawah Python 3.4.3 (I mengujinya). Lihat stackoverflow.com/questions/37139786 untuk lebih jelasnya.
Rob_before_edits

4
Jangan gunakan itu. Ini adalah paket "namespace", bukan paket biasa. paket namespace digunakan untuk kasus penggunaan yang sangat jarang. Anda mungkin tidak perlu tahu kapan menggunakannya. Gunakan saja __init__.py.
metana

2
Namun jika Anda memiliki setup.pydan menggunakannya find_packages()perlu ada __init__.pydi setiap direktori. Lihat stackoverflow.com/a/56277323/7127824
techkuz

484

Selain memberi label pada direktori sebagai paket Python dan mendefinisikan __all__, __init__.pymemungkinkan Anda untuk mendefinisikan variabel apa pun di tingkat paket. Melakukannya sering kali nyaman jika suatu paket mendefinisikan sesuatu yang akan sering diimpor, dengan cara seperti API. Pola ini mempromosikan kepatuhan pada filosofi "flat lebih baik daripada bersarang" dari Pythonic.

Sebuah contoh

Berikut adalah contoh dari salah satu proyek saya, di mana saya sering mengimpor sessionmakerdisebut Sessionuntuk berinteraksi dengan database saya. Saya menulis paket "database" dengan beberapa modul:

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

Saya __init__.pyberisi kode berikut:

import os

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

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

Karena saya mendefinisikan di Sessionsini, saya dapat memulai sesi baru menggunakan sintaksis di bawah ini. Kode ini akan sama dieksekusi dari dalam atau di luar direktori paket "database".

from database import Session
session = Session()

Tentu saja, ini adalah kenyamanan kecil - alternatifnya adalah dengan mendefinisikan Sessiondalam file baru seperti "create_session.py" dalam paket database saya, dan mulai sesi baru menggunakan:

from database.create_session import Session
session = Session()

Bacaan lebih lanjut

Ada thread reddit yang cukup menarik yang mencakup penggunaan yang sesuai di __init__.pysini:

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

Pendapat mayoritas tampaknya bahwa __init__.pyfile harus sangat tipis untuk menghindari melanggar filosofi "eksplisit lebih baik daripada implisit".


3
engine, sessionmaker, create_engine, Dan ossemua bisa juga diimpor dari databasesekarang ... sepertinya Anda telah membuat berantakan namespace itu.
ArtOfWarfare

9
@ArtOfWarfare, Anda dapat menggunakan __all__ = [...]untuk membatasi apa yang diimpor dengan import *. Tapi selain dari itu, ya, Anda memiliki namespace tingkat atas yang berantakan.
Nathan Gould

Bolehkah saya tahu apa itu 'URL DATABASE'? Saya mencoba mereplikasi ini dengan melampirkan create_engine dengan 'mysql + mysqldb: // root: python @ localhost: 3306 / test' tetapi tidak berhasil. Terima kasih.
SunnyBoiz

2
Bagaimana Anda mengakses kelas 'Sesi' yang didefinisikan dalam init dari paket di dalam misalnya quieries.py?
vldbnc

253

Ada 2 alasan utama __init__.py

  1. Untuk kenyamanan: pengguna lain tidak perlu mengetahui lokasi pasti fungsi Anda di hierarki paket Anda.

    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

    kemudian orang lain dapat memanggil add () oleh

    from your_package import add

    tanpa mengetahui file1, suka

    from your_package.file1 import add
  2. Jika Anda ingin sesuatu diinisialisasi; misalnya, pencatatan (yang harus diletakkan di tingkat atas):

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

7
oh, sebelum membaca jawaban Anda, saya pikir memanggil fungsi secara eksplisit dari lokasinya adalah praktik yang baik.
Aerin

2
@ Aerin akan lebih baik jangan menganggap pernyataan pendek (atau, dalam hal ini, kesimpulan subyektif) selalu benar. Mengimpor dari __init__.pyterkadang bermanfaat, tetapi tidak setiap saat.
Tobias Sette

2
Apakah kode ini dieksekusi pada waktu impor atau runtime?
user1559897

111

The __init__.pyFile membuat Python memperlakukan direktori yang berisi sebagai modul.

Selanjutnya, ini adalah file pertama yang dimuat dalam modul, sehingga Anda dapat menggunakannya untuk mengeksekusi kode yang ingin Anda jalankan setiap kali modul dimuat, atau menentukan submodul yang akan diekspor.


89

Sejak Python 3.3, __init__.pytidak lagi diperlukan untuk mendefinisikan direktori sebagai paket Python yang dapat diimpor.

Periksa PEP 420: Paket Namespace Tersirat :

Dukungan asli untuk direktori paket yang tidak memerlukan __init__.pyfile penanda dan dapat secara otomatis menjangkau banyak segmen jalur (terinspirasi oleh berbagai pendekatan pihak ketiga untuk paket namespace, seperti dijelaskan dalam PEP 420 )

Inilah tesnya:

$ 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

referensi:
https://docs.python.org/3/whatsnew/3.3.html#pep-420-implicit-namespace-packages
https://www.python.org/dev/peps/pep-0420/
Apakah __init__. py tidak diperlukan untuk paket di Python 3?


3
Ini adalah paket "namespace". Jangan menggunakannya untuk paket reguler.
metana

@methan, bisakah Anda menguraikan komentar Anda?
Robert Lugg


57

Dalam Python definisi paket sangat sederhana. Seperti Java, struktur hierarkis dan struktur direktori adalah sama. Tetapi Anda harus memiliki __init__.pysatu paket. Saya akan menjelaskan __init__.pyfile dengan contoh di bawah ini:

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__.pybisa kosong, asalkan ada. Ini menunjukkan bahwa direktori harus dianggap sebagai paket. Tentu saja, __init__.pybisa juga mengatur konten yang sesuai.

Jika kita menambahkan fungsi di module_n1:

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

Setelah berlari:

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

function_X in module_n1 

Kemudian kami mengikuti paket hierarki dan memanggil module_n1 fungsinya. Kita dapat menggunakan __init__.pydi subPackage_b seperti ini:

__all__ = ['module_n2', 'module_n3']

Setelah berlari:

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

Oleh karena itu menggunakan * impor, paket modul tergantung pada __init__.pykonten.


Bagaimana setup.py saya terlihat melakukan impor yang sama melalui perpustakaan paket? from package_x.subPackage_b.module_n1 import function_X
technazi

jadi kunci mengambil di sini adalah "menggunakan * impor, paket modul tunduk pada konten init .py"
Minnie

54

Meskipun Python bekerja tanpa __init__.pyfile, Anda tetap harus memasukkannya.

Ini menentukan paket harus diperlakukan sebagai modul, jadi karena itu sertakan (bahkan jika itu kosong).

Ada juga kasus di mana Anda sebenarnya dapat menggunakan __init__.pyfile:

Bayangkan Anda memiliki struktur file berikut:

main_methods 
    |- methods.py

Dan methods.pyberisi ini:

def foo():
    return 'foo'

Untuk menggunakan foo()Anda perlu salah satu dari yang berikut:

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

Mungkin di sana Anda perlu (atau ingin) untuk tetap methods.pydi dalam main_methods(runtimes / dependensi misalnya) tetapi Anda hanya ingin mengimpor main_methods.


Jika Anda mengubah nama methods.pyuntuk __init__.pymaka Anda dapat menggunakan foo()hanya dengan mengimpor main_methods:

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

Ini berfungsi karena __init__.pydiperlakukan sebagai bagian dari paket.


Beberapa paket Python sebenarnya melakukan ini. Contohnya adalah dengan JSON , di mana menjalankan import jsonsebenarnya mengimpor __init__.pydari jsonpaket ( lihat struktur file paket di sini ):

Kode sumber: Lib/json/__init__.py


39

__init__.py akan memperlakukan direktori itu sebagai modul yang dapat dimuat.

Untuk orang-orang yang lebih suka membaca kode, saya menaruh komentar Two-Bit Alchemist di sini.

$ 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

Ini memfasilitasi mengimpor file python lainnya. Ketika Anda menempatkan file ini di direktori (katakan hal-hal) yang berisi file py lainnya, maka Anda dapat melakukan sesuatu seperti mengimpor barang.other.

root\
    stuff\
         other.py

    morestuff\
         another.py

Tanpa ini __init__.pydi dalam hal-hal direktori, Anda tidak dapat mengimpor other.py, karena Python tidak tahu di mana kode sumber untuk barang-barang itu dan tidak dapat mengenalinya sebagai paket.


2
Saya memiliki struktur yang sama dalam proyek saya (python 3.4) tetapi saya tidak dapat membuat another.py melihat other.py. Bagaimana saya harus melakukan impor? dari root.stuff impor lainnya? Ini bekerja dalam mode debug VSCode tetapi tidak dalam baris perintah. Ada ide?
rodrigorf

10

Sebuah __init__.pyfile yang membuat impor mudah. Ketika sebuah __init__.pyhadir dalam suatu paket, fungsi a()dapat diimpor dari file b.pyseperti:

from b import a

Tanpa itu, Anda tidak dapat mengimpor secara langsung. Anda harus mengubah jalur sistem:

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

from b import a

apa maksud Anda " fungsi a () dapat diimpor dari file b.py [snippet] Tanpa itu, Anda tidak dapat mengimpor secara langsung. "? Saya dapat mengimpor fungsi a () dari file b.py tanpa __init__.py.
aderchox
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.