การเพิ่มโค้ดใน __init__.py


85

ฉันกำลังดูว่าระบบโมเดลใน django ทำงานอย่างไรและฉันสังเกตเห็นบางสิ่งที่ฉันไม่เข้าใจ

ฉันรู้ว่าคุณสร้าง__init__.pyไฟล์ว่างเพื่อระบุว่าไดเร็กทอรีปัจจุบันเป็นแพ็กเกจ และคุณสามารถตั้งค่าตัวแปร__init__.pyเพื่อให้ import * ทำงานได้อย่างถูกต้อง

แต่ django เพิ่มคำสั่ง from ... import ... และกำหนดกลุ่มของคลาสใน__init__.py. ทำไม? สิ่งนี้ไม่ทำให้สิ่งต่างๆดูยุ่งเหยิง? มีเหตุผลที่ต้องใช้รหัสนี้__init__.pyหรือไม่?


13
นี่มันไม่เกี่ยวกับ Django จริงๆเหรอ? ใช่คุณเห็นครั้งแรกใน Django แต่ดูเหมือนว่าจะเป็น Python ที่บริสุทธิ์มากกว่าบางทีแท็ก Django อาจไม่เหมาะสมจริงๆ
ล็อต

ฉันไม่เห็นข้อความนำเข้าใน__init__.pydjango 1.8 นี่เป็นเวอร์ชันเก่าหรือไม่ ถ้าเป็นรุ่นไหน
Gobi Dasu

คำตอบ:


72

การนำเข้าทั้งหมด__init__.pyจะพร้อมใช้งานเมื่อคุณอิมพอร์ตแพ็กเกจ (ไดเร็กทอรี) ที่มีอยู่

ตัวอย่าง:

./dir/__init__.py:

import something

./test.py:

import dir
# can now use dir.something

แก้ไข: ลืมพูดถึงรหัสในการ__init__.pyทำงานในครั้งแรกที่คุณนำเข้าโมดูลจากไดเร็กทอรีนั้น ดังนั้นจึงเป็นจุดที่ดีที่จะใส่รหัสเริ่มต้นระดับแพ็คเกจ

แก้ไข 2: dgrant ชี้ให้เห็นถึงความสับสนที่อาจเกิดขึ้นในตัวอย่างของฉัน In __init__.py import somethingสามารถนำเข้าโมดูลใดก็ได้โดยไม่จำเป็นจากแพ็คเกจ ตัวอย่างเช่นเราสามารถแทนที่ด้วยimport datetimeจากนั้นในระดับบนสุดของเราtest.pyทั้งสองตัวอย่างเหล่านี้จะทำงาน:

import dir
print dir.datetime.datetime.now()

และ

import dir.some_module_in_dir
print dir.datetime.datetime.now()

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


โอเคขอบคุณ. แต่ฉันยังไม่แน่ใจว่าทำไมการเพิ่มคลาสจึงเป็นความคิดที่ดีที่__init__.py ฉันไม่ได้พิจารณารหัสเริ่มต้นคลาสเหล่านี้จริงๆ (แต่ฉันอาจจะคิดผิด)
Erik

ซึ่งอาจเป็นชั้นเรียนที่มีประโยชน์ทุกครั้งที่คุณทำงานกับแพ็กเกจ แต่ฉันไม่ต้องการคาดเดาอาจมีหลายเหตุผลว่าทำไมพวกเขาถึงอยู่ที่นั่นวัตถุประสงค์หรือไม่ :)
Alexander Kojevnikov

13
อาจเป็นเพราะเหตุผลทางประวัติศาสตร์ เมื่อคุณแปลงโมดูลเป็นแพ็กเกจ module.py เป็นโมดูล / __ init__.py โค้ดที่มีอยู่ทั้งหมดสามารถใช้งานได้เหมือนเดิม แต่ตอนนี้โมดูลสามารถมีโมดูลย่อยได้แล้ว
Łukasz

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

สิ่งสำคัญคือต้องสังเกตว่านี่ไม่ใช่__init__.pyไฟล์เฉพาะ หากคุณมีไฟล์dir/other.pyที่มีบางอย่างเช่นfrom datetime import datetimeคุณสามารถโทรdir.other.datetime.now()หรือแม้แต่from dir.other import datetimeไฟล์.
Carles Sala

37

เป็นเพียงความชอบส่วนบุคคลจริงๆและเกี่ยวข้องกับรูปแบบของโมดูลหลามของคุณ

erikutilsสมมติว่าคุณมีโมดูลที่เรียกว่า มีสองวิธีที่สามารถเป็นโมดูลได้ไม่ว่าคุณจะมีไฟล์ชื่อerikutils.pyในไดเร็กทอรีของคุณsys.pathหรือคุณมีไดเร็กทอรีชื่อerikutilsในไฟล์ของคุณsys.pathโดยมี__init__.pyไฟล์ว่างอยู่ แล้วสมมติว่าคุณมีพวงของโมดูลที่เรียกว่าfileutils, procutils, และคุณต้องการผู้ที่จะเป็นโมดูลย่อยภายใต้parseutils erikutilsคุณจึงสร้างไฟล์. py ชื่อfileutils.py , procutils.pyและparseutils.py :

erikutils
  __init__.py
  fileutils.py
  procutils.py
  parseutils.py

บางทีคุณอาจจะมีฟังก์ชั่นบางอย่างที่เพียงแค่ไม่ได้อยู่ในfileutils, procutilsหรือparseutilsโมดูล miscutilsและขอบอกว่าคุณไม่รู้สึกเหมือนการสร้างโมดูลใหม่ที่เรียกว่า และคุณต้องการเรียกใช้ฟังก์ชันดังนี้:

erikutils.foo()
erikutils.bar()

แทนที่จะทำ

erikutils.miscutils.foo()
erikutils.miscutils.bar()

ดังนั้นเนื่องจากerikutilsโมดูลเป็นไดเร็กทอรีไม่ใช่ไฟล์เราจึงต้องกำหนดฟังก์ชันภายในไฟล์__init__.pyไฟล์

ใน Django django.db.models.fieldsตัวอย่างที่ดีที่สุดที่ฉันสามารถคิดคือ ALL Django * การเรียนสนามกำหนดไว้ใน__init__.pyแฟ้มในDjango / DB / รุ่น / เขตไดเรกทอรี ผมคิดว่าพวกเขาทำอย่างนี้เพราะพวกเขาไม่ต้องการที่จะยัดทุกอย่างเป็นสมมุติDjango / DB / รุ่น / fields.pyรูปแบบเพื่อให้พวกเขาแยกมันออกไปไม่กี่ submodules A ( related.py , files.pyตัวอย่างเช่น) และ พวกเขาติดคำนิยามฟิลด์ * ที่สร้างไว้ในโมดูลฟิลด์เอง (ด้วยเหตุนี้__init__.py)


1
dgrant สิ่งที่ฉันหมายถึงคือsomethingอาจเป็นโมดูลภายนอก dir บางสิ่งจะใช้งานได้ ขอบคุณสำหรับความคิดเห็นฉันจะแก้ไขโพสต์ของฉันเพื่อให้ชัดเจนยิ่งขึ้น
Alexander Kojevnikov

29

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

โปรดทราบว่าคุณสามารถใช้delคำสั่งได้ดังนั้นโดยทั่วไป__init__.pyอาจมีลักษณะดังนี้:

from somemodule import some_function1, some_function2, SomeObject

del somemodule

ตอนนี้ถ้าคุณตัดสินใจที่จะแยกsomemoduleใหม่__init__.pyอาจเป็น:

from somemodule1 import some_function1, some_function2
from somemodule2 import SomeObject

del somemodule1
del somemodule2

จากภายนอกแพ็คเกจยังดูเป๊ะเหมือนเดิม


1
@ Arlen: ประเด็นคือมันไม่ได้เป็นส่วนหนึ่งของ API สาธารณะ หากคุณเปลี่ยนชื่อโมดูลคุณจะมั่นใจได้ว่าไม่มีการแบ่งโค้ดที่อ้างอิง นอกจากนี้ยังช่วยให้มั่นใจได้ว่าองค์ประกอบ API จะปรากฏเพียงครั้งเดียวเช่นเมื่อใช้วิปัสสนาเพื่อสร้างเอกสาร API โดยอัตโนมัติ
nikow

5
@Arlen: การลบโมดูลจะป้องกันไม่ให้โมดูลimport <pack>.somemodule1โดยตรง คุณสามารถนำเข้าจากอ<pack>อบเจ็กต์ที่กำหนดหรืออิมพอร์ตใน__init__.pyโมดูลย่อยที่ไม่ถูกลบ
MestreLion
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.