__init__.py มีไว้เพื่ออะไร


2336

มี__init__.pyไว้สำหรับอะไรในไดเรกทอรีแหล่ง Python


16
ตามความคิดเห็นด้านล่างโดย @Rob_before_edits และstackoverflow thread 37139786นี้ดูเหมือนว่าinit .pyไม่จำเป็นสำหรับ Python 3.3+ อีกต่อไป
Jun711

คำตอบ:


1454

มันเคยเป็นส่วนที่จำเป็นของแพคเกจ ( เก่า pre-3.3 "แพ็คเกจปกติ"ไม่ใช่3.3+ "แพ็คเกจเนมสเปซ" ที่ใหม่กว่า )

นี่คือเอกสารประกอบ

Python กำหนดแพ็คเกจสองประเภทแพ็คเกจปกติและแพ็คเกจเนมสเปซ แพ็คเกจปกติเป็นแพ็คเกจแบบดั้งเดิมเนื่องจากมีอยู่ใน Python 3.2 และรุ่นก่อนหน้า โดยทั่วไปแพคเกจปกติจะใช้งานเป็นไดเรกทอรีที่มี__init__.pyไฟล์ เมื่อนำเข้าแพคเกจปกติ__init__.pyไฟล์นี้จะถูกดำเนินการโดยปริยายและวัตถุที่กำหนดจะถูกผูกไว้กับชื่อในเนมสเปซของแพคเกจ __init__.pyไฟล์สามารถมีรหัสหลามเดียวกันกับที่โมดูลอื่น ๆ สามารถมีและงูหลามจะเพิ่มคุณลักษณะบางอย่างเพิ่มเติมไปยังโมดูลเมื่อมันถูกนำเข้า

แต่เพียงแค่คลิกที่ลิงค์แล้วมันมีตัวอย่างเช่นข้อมูลเพิ่มเติมและคำอธิบายของแพคเกจ namespace __init__.pyชนิดของแพคเกจโดยไม่ต้อง


187
สิ่งนี้หมายความว่า: "สิ่งนี้ถูกทำขึ้นเพื่อป้องกันไดเรกทอรีที่มีชื่อสามัญเช่นสตริงจากการซ่อนโมดูลที่ถูกต้องที่ไม่ได้ตั้งใจซึ่งเกิดขึ้นในภายหลังบนเส้นทางการค้นหาโมดูล"?
Carl G

97
@CarlG Python ค้นหารายการไดเรกทอรีเพื่อแก้ไขชื่อเช่นนำเข้าข้อความ เนื่องจากสิ่งเหล่านี้สามารถเป็นไดเรกทอรีใดก็ได้และผู้ใช้ปลายทางสามารถเพิ่มได้โดยผู้ใช้ปลายทางนักพัฒนาจึงต้องกังวลเกี่ยวกับไดเรกทอรีที่เกิดขึ้นเพื่อแบ่งปันชื่อกับโมดูล Python ที่ถูกต้องเช่น 'string' ในตัวอย่างเอกสาร เพื่อบรรเทาสิ่งนี้จะไม่สนใจไดเรกทอรีที่ไม่มีไฟล์ชื่อ _ _ init _ _.py (ไม่มีช่องว่าง) แม้ว่าจะว่างเปล่าก็ตาม
นักเล่นแร่แปรธาตุสองบิต

186
@CarlG ลองนี้ สร้างไดเรกทอรีที่ชื่อว่า 'datetime' และในนั้นจะสร้างไฟล์เปล่าสองไฟล์คือไฟล์ init.py (ที่มีเครื่องหมายขีดล่าง) และ datetime.py ตอนนี้เปิดล่ามนำเข้า sys และปัญหาsys.path.insert(0, '/path/to/datetime')แทนที่เส้นทางนั้นด้วยเส้นทางไปยังไดเรกทอรีใด ๆ ที่คุณเพิ่งทำ from datetime import datetime;datetime.now()ตอนนี้พยายามบางอย่างเช่น คุณควรรับ AttributeError (เพราะมันกำลังนำเข้าไฟล์เปล่าของคุณตอนนี้) หากคุณต้องทำซ้ำขั้นตอนเหล่านี้โดยไม่สร้างไฟล์ init ว่างเปล่าสิ่งนี้จะไม่เกิดขึ้น นั่นคือสิ่งที่ตั้งใจจะป้องกัน
นักเล่นแร่แปรธาตุสองบิต

4
@ DarekNędzaคุณได้ตั้งค่าบางอย่างไม่ถูกต้องหากคุณไม่สามารถเปิด Python interpreter และปัญหาได้from datetime import datetimeโดยไม่มีข้อผิดพลาด มันดีไปตลอดทางจนถึงเวอร์ชั่น 2.3!
นักเล่นแร่แปรธาตุ Two-Bit

5
@SWang: ไม่ถูกต้อง: builtinsแสดงรายการฟังก์ชั่นและคลาสในตัวไม่ใช่โมดูลในตัว (cf. docs.python.org/3/tutorial/modules.html#the-dir-function ) หากคุณต้องการแสดงรายการโมดูลในตัวให้ทำimport sys; print(sys.builtin_module_names)(cf. docs.python.org/3/library/sys.html#sys.builtin_module_names )
Maggyero

842

ไฟล์ที่__init__.pyมีชื่อถูกใช้เพื่อทำเครื่องหมายไดเรกทอรีบนดิสก์เป็นไดเรกทอรีแพคเกจ Python หากคุณมีไฟล์

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

และmydirอยู่ในเส้นทางของคุณคุณสามารถนำเข้ารหัสmodule.pyเป็น

import spam.module

หรือ

from spam import module

หากคุณลบ__init__.pyไฟล์ Python จะไม่ค้นหา submodules ในไดเรกทอรีนั้นอีกต่อไปดังนั้นความพยายามที่จะนำเข้าโมดูลจะล้มเหลว

__init__.pyไฟล์มักจะว่างเปล่า แต่สามารถนำมาใช้เพื่อการส่งออกส่วนที่เลือกของแพคเกจภายใต้ชื่อสะดวกมากขึ้นถือฟังก์ชั่นอำนวยความสะดวกอื่น ๆ ได้รับตัวอย่างข้างต้นเนื้อหาของโมดูล init ที่สามารถเข้าถึงได้เป็น

import spam

ตามนี้


96
อัปเดต: ไฟล์__init__.pyนี้จำเป็นต้องใช้ภายใต้ Python 2.X และยังจำเป็นต้องใช้ภายใต้ Python 2.7.12 (ฉันทดสอบ) แต่ไม่จำเป็นต้องใช้จาก (ถูกกล่าวหาว่า) Python 3.3 เป็นต้นไปและไม่จำเป็นต้องใช้ภายใต้ Python 3.4.3 (I ทดสอบแล้ว) ดูstackoverflow.com/questions/37139786สำหรับรายละเอียดเพิ่มเติม
Rob_before_edits

4
อย่าใช้มัน เป็นแพ็คเกจ "namespace" ไม่ใช่แพ็คเกจปกติ แพ็คเกจเนมสเปซใช้สำหรับกรณีการใช้งานที่หายากมาก คุณอาจไม่จำเป็นต้องรู้เมื่อใช้งาน __init__.pyใช้เพียงแค่
มีเทน

2
อย่างไรก็ตามหากคุณมีsetup.pyและคุณfind_packages()จำเป็นต้องมี__init__.pyทุกไดเรกทอรี ดูstackoverflow.com/a/56277323/7127824
techkuz

484

นอกเหนือไปจากการติดฉลากไดเรกทอรีเป็นแพคเกจหลามและการกำหนด__all__, __init__.pyช่วยให้คุณสามารถกำหนดตัวแปรใด ๆ ในระดับแพคเกจ การทำเช่นนั้นมักจะสะดวกถ้าแพ็คเกจกำหนดสิ่งที่จะนำเข้าบ่อยๆในรูปแบบ API รูปแบบนี้ส่งเสริมการยึดมั่นในปรัชญา "แบนดีกว่าซ้อน" Pythonic

ตัวอย่าง

นี่คือตัวอย่างจากหนึ่งในโครงการของฉันซึ่งฉันมักจะนำเข้าsessionmakerชื่อSessionเพื่อโต้ตอบกับฐานข้อมูลของฉัน ฉันเขียนแพ็คเกจ "ฐานข้อมูล" ด้วยโมดูลบางส่วน:

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

ฉัน__init__.pyมีรหัสต่อไปนี้:

import os

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

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

เนื่องจากฉันกำหนดไว้Sessionที่นี่ฉันสามารถเริ่มเซสชันใหม่โดยใช้ไวยากรณ์ด้านล่าง รหัสนี้จะเป็นรหัสเดียวกันจากภายในหรือภายนอกไดเรกทอรีแพ็คเกจ "ฐานข้อมูล"

from database import Session
session = Session()

แน่นอนว่านี่เป็นความสะดวกสบายขนาดเล็กทางเลือกอื่นคือการกำหนดSessionในไฟล์ใหม่เช่น "create_session.py" ในแพ็คเกจฐานข้อมูลของฉันและเริ่มเซสชันใหม่โดยใช้:

from database.create_session import Session
session = Session()

อ่านเพิ่มเติม

มีเธรด reddit ที่น่าสนใจครอบคลุมการใช้ที่เหมาะสม__init__.pyที่นี่:

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

ความคิดเห็นส่วนใหญ่ดูเหมือนว่า__init__.pyไฟล์ควรจะบางมากเพื่อหลีกเลี่ยงการละเมิดปรัชญา "ชัดเจนดีกว่าโดยปริยาย"


3
engine, sessionmaker, create_engineและosสามารถทั้งหมดนอกจากนี้ยังนำเข้ามาจากdatabaseตอนนี้ ... ดูเหมือนว่าคุณได้ทำระเบียบของ namespace ว่า
ArtOfWarfare

9
@ArtOfWarfare คุณสามารถใช้__all__ = [...]เพื่อ จำกัด import *สิ่งที่ได้รับนำเข้าด้วย แต่นอกจากนั้นใช่แล้วคุณจะเหลือเนมสเปซระดับบนสุดยุ่ง ๆ
Nathan Gould

ฉันขอทราบได้ไหมว่า 'DATABASE URL' คืออะไร? ฉันพยายามทำซ้ำสิ่งนี้โดยสร้าง create_engine ด้วย 'mysql + mysqldb: // root: python @ localhost: 3306 / test' แต่มันไม่ทำงาน ขอบคุณ
SunnyBoiz

2
คุณจะเข้าถึงคลาส 'เซสชัน' ที่กำหนดไว้ในinitจากภายในแพคเกจเช่น quieries.py ได้อย่างไร
vldbnc

252

มี 2 ​​เหตุผลหลักคือ __init__.py

  1. เพื่อความสะดวก: ผู้ใช้รายอื่นไม่จำเป็นต้องทราบตำแหน่งที่แน่นอนของฟังก์ชันในลำดับชั้นแพ็คเกจของคุณ

    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

    จากนั้นบุคคลอื่นสามารถโทรเพิ่ม () โดย

    from your_package import add

    ไม่ทราบ file1 เช่น

    from your_package.file1 import add
  2. หากคุณต้องการบางสิ่งบางอย่างที่จะเริ่มต้น; ตัวอย่างเช่นการบันทึก (ซึ่งควรใส่ในระดับบนสุด):

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

7
โอ้ก่อนที่จะอ่านคำตอบของคุณฉันคิดว่าการเรียกใช้ฟังก์ชั่นอย่างชัดเจนจากตำแหน่งของมันเป็นแนวปฏิบัติที่ดี
Aerin

2
@Aerin มันจะดีกว่าไม่พิจารณาข้อความสั้น ๆ (หรือในกรณีนี้ข้อสรุปส่วนตัว) จะเป็นจริงเสมอ การนำเข้าจาก__init__.pyอาจมีประโยชน์บางครั้ง แต่ไม่ทุกครั้ง
Tobias Sette

2
รหัสเหล่านี้ถูกดำเนินการในเวลาที่นำเข้าหรือรันไทม์
user1559897

111

__init__.pyไฟล์ดังกล่าวทำให้ Python จัดการกับไดเรกทอรีที่บรรจุเป็นโมดูล

นอกจากนี้ไฟล์นี้เป็นไฟล์แรกที่จะโหลดในโมดูลดังนั้นคุณสามารถใช้เพื่อเรียกใช้งานโค้ดที่คุณต้องการรันในแต่ละครั้งที่โหลดโมดูลหรือระบุ submodules ที่จะส่งออก


89

ตั้งแต่ Python 3.3 __init__.pyไม่จำเป็นต้องกำหนดไดเรกทอรีเป็นแพ็กเกจ Python ที่สามารถนำเข้าได้อีกต่อไป

ตรวจสอบPEP 420: แพ็คเกจเนมสเปซโดยนัย :

การสนับสนุนดั้งเดิมสำหรับไดเรกทอรีแพ็คเกจที่ไม่ต้องใช้__init__.pyไฟล์ทำเครื่องหมายและสามารถขยายส่วนของเส้นทางหลายส่วนโดยอัตโนมัติ (ได้รับแรงบันดาลใจจากวิธีการของบุคคลที่สามที่หลากหลายในแพ็คเกจเนมสเปซตามที่อธิบายในPEP 420 )

นี่คือการทดสอบ:

$ 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

การอ้างอิง:
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 ใช่ไหม


3
เป็นแพ็คเกจ "namespace" อย่าใช้มันสำหรับแพ็คเกจปกติ
มีเธน

@methan คุณช่วยอธิบายความคิดเห็นของคุณได้ไหม?
Robert Lugg


57

ใน Python นิยามของแพ็คเกจนั้นง่ายมาก เช่นเดียวกับ Java โครงสร้างลำดับชั้นและโครงสร้างไดเรกทอรีเหมือนกัน แต่คุณต้องมี__init__.pyในแพ็คเกจ ฉันจะอธิบาย__init__.pyไฟล์ด้วยตัวอย่างด้านล่าง:

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__.pyสามารถว่างเปล่าได้ตราบใดที่มีอยู่ มันบ่งชี้ว่าไดเรกทอรีควรถือเป็นแพคเกจ แน่นอน,__init__.pyสามารถตั้งค่าเนื้อหาที่เหมาะสม

หากเราเพิ่มฟังก์ชั่นใน module_n1:

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

หลังจากทำงาน:

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

function_X in module_n1 

จากนั้นเราติดตามแพคเกจลำดับชั้นและเรียกว่า module_n1 ฟังก์ชั่น เราสามารถใช้__init__.pyใน subPackage_b ดังนี้:

__all__ = ['module_n2', 'module_n3']

หลังจากทำงาน:

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

ดังนั้นการใช้ * การนำเข้าแพคเกจโมดูลอาจมี__init__.pyเนื้อหา


setup.py ของฉันจะมีลักษณะอย่างไรในการนำเข้าเดียวกันผ่านไลบรารีที่จัดทำแพคเกจ from package_x.subPackage_b.module_n1 import function_X
technazi

ดังนั้นสิ่งสำคัญที่ต้องนำติดตัวไปที่นี่คือ"การใช้ * การนำเข้าแพ็คเกจโมดูลอาจมีเนื้อหาinit .py"
Minnie

54

ถึงแม้ว่า Python จะทำงานโดยไม่มี__init__.pyไฟล์ แต่คุณก็ควรรวมไว้ด้วย

มันระบุแพคเกจควรได้รับการปฏิบัติเป็นโมดูลดังนั้นจึงรวมถึงมัน (แม้ว่าจะว่างเปล่า)

นอกจากนี้ยังมีกรณีที่คุณอาจใช้__init__.pyไฟล์จริง:

ลองนึกภาพคุณมีโครงสร้างไฟล์ต่อไปนี้:

main_methods 
    |- methods.py

และmethods.pyมีสิ่งนี้:

def foo():
    return 'foo'

หากต้องการใช้foo()คุณจะต้องมีสิ่งใดสิ่งหนึ่งต่อไปนี้:

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

อาจมีคุณต้องการ (หรือต้องการ) เพื่อเก็บไว้methods.pyข้างในmain_methods(ตัวอย่างเช่น runtimes / dependencies) แต่คุณต้องการนำเข้าmain_methodsเท่านั้น


หากคุณเปลี่ยนชื่อเป็นmethods.pyเป็น__init__.pyคุณสามารถใช้foo()เพียงนำเข้าmain_methods:

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

ใช้งานได้เพราะ__init__.pyถือว่าเป็นส่วนหนึ่งของแพ็คเกจ


แพ็กเกจ Python บางตัวทำสิ่งนี้จริง ตัวอย่างคือกับJSONโดยที่การรันกำลังimport jsonนำเข้า__init__.pyจากjsonแพ็คเกจจริง ( ดูโครงสร้างไฟล์แพ็คเกจที่นี่ ):

รหัสแหล่งที่มา: Lib/json/__init__.py


39

__init__.py จะถือว่าไดเรกทอรีมันเป็นโมดูลที่โหลดได้

สำหรับคนที่ชอบอ่านรหัสฉันใส่ความคิดเห็นของนักเล่นแร่แปรธาตุ Two-Bitที่นี่

$ 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

มันอำนวยความสะดวกในการนำเข้าไฟล์หลามอื่น ๆ เมื่อคุณวางไฟล์นี้ในไดเรกทอรี (พูดสิ่ง) ที่มีไฟล์ py อื่น ๆ จากนั้นคุณสามารถทำสิ่งต่าง ๆ เช่นนำเข้า stuff.other

root\
    stuff\
         other.py

    morestuff\
         another.py

หากไม่มีสิ่งนี้__init__.pyภายในเนื้อหาของไดเรกทอรีคุณจะไม่สามารถนำเข้า other.py เนื่องจาก Python ไม่ทราบว่าซอร์สโค้ดของสิ่งนั้นอยู่ที่ไหนและไม่สามารถรับรู้ได้ว่าเป็นแพคเกจ


2
ฉันมีโครงสร้างเดียวกันในโครงการของฉัน (python 3.4) แต่ฉันไม่สามารถสร้าง another.py ดู other.py ฉันควรนำเข้าอย่างไร จาก root.stuff นำเข้าอื่น ๆ มันทำงานในโหมดดีบัก VSCode แต่ไม่ได้อยู่ในบรรทัดคำสั่ง ความคิดใด ๆ
rodrigorf

10

__init__.pyไฟล์ทำให้การนำเข้าง่าย เมื่อมี__init__.pyอยู่ในแพ็คเกจฟังก์ชั่นa()สามารถนำเข้าจากไฟล์b.pyดังนี้:

from b import a

ไม่ว่าอย่างไรก็ตามคุณจะไม่สามารถนำเข้าโดยตรง คุณต้องแก้ไขเส้นทางของระบบ:

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

from b import a

คุณหมายถึงอะไร " ฟังก์ชั่น a () สามารถนำเข้าจากไฟล์ b.py [ตัวอย่าง] ถ้าไม่มีมันคุณไม่สามารถนำเข้าโดยตรง " ฉันสามารถนำเข้าฟังก์ชั่น a () จากไฟล์ b.py โดยไม่ต้อง __init__.py
aderchox
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.