ฉันจะเขียนไฟล์ __init__.py แพ็คเกจที่ดี / ถูกต้องได้อย่างไร


188

แพ็คเกจของฉันมีโครงสร้างดังต่อไปนี้:

mobilescouter/
    __init__.py #1
    mapper/
        __init__.py  #2
        lxml/
            __init__.py #3
            vehiclemapper.py
            vehiclefeaturemapper.py
            vehiclefeaturesetmapper.py
        ...
        basemapper.py
   vehicle/
        __init__.py #4
        vehicle.py
        vehiclefeature.py
        vehiclefeaturemapper.py
   ...

ฉันไม่แน่ใจว่า__init__.pyควรเขียนไฟล์อย่างไรให้ถูกต้อง ดูเหมือนว่า:
__init__.py #1

__all__ = ['mapper', 'vehicle']
import mapper
import vehicle

แต่ตัวอย่างควร__init__.py #2มีลักษณะอย่างไร ของฉันคือ:

__all__ = ['basemapper', 'lxml']
from basemaper import *
import lxml

ควร__all__ใช้เมื่อใด


3
โปรดทราบว่าการใช้การอิมพอร์ต * ในโค้ดนั้นเป็นวิธีปฏิบัติที่แย่มากและควรหลีกเลี่ยงหากเป็นไปได้ มีกรณีการใช้งานที่ดีน้อยมาก แต่สิ่งเหล่านี้หายากจริงๆ
Mayou36

PSA: หากคุณสนใจที่จะเรียนรู้วิธีการเขียนแพ็คเกจเนมสเปซที่ดี (แพ็คเกจใหม่) ลองดูแพ็คเกจตัวอย่างนี้: github.com/pypa/sample-namespace-packages
Kyle

คำตอบ:


146

__all__ดีมาก - ช่วยแนะนำคำสั่งการนำเข้าโดยไม่ต้องนำเข้าโมดูลโดยอัตโนมัติ http://docs.python.org/tutorial/modules.html#importing-from-a-package

การใช้__all__และimport *ซ้ำซ้อน__all__เป็นสิ่งจำเป็นเท่านั้น

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

ตัวอย่างเช่น:

foo.py - contains classes related to foo such as fooFactory, tallFoo, shortFoo

จากนั้นแอปก็ขยายตัวและตอนนี้มันก็เป็นทั้งโฟลเดอร์

foo/
    __init__.py
    foofactories.py
    tallFoos.py
    shortfoos.py
    mediumfoos.py
    santaslittlehelperfoo.py
    superawsomefoo.py
    anotherfoo.py

จากนั้นสคริปต์ init สามารถพูดได้

__all__ = ['foofactories', 'tallFoos', 'shortfoos', 'medumfoos',
           'santaslittlehelperfoo', 'superawsomefoo', 'anotherfoo']
# deprecated to keep older scripts who import this from breaking
from foo.foofactories import fooFactory
from foo.tallfoos import tallFoo
from foo.shortfoos import shortFoo

ดังนั้นสคริปต์ที่เขียนเพื่อทำสิ่งต่อไปนี้จะไม่หยุดพักระหว่างการเปลี่ยนแปลง:

from foo import fooFactory, tallFoo, shortFoo

3
ฉันสับสนมากเกี่ยวกับ ' ทั้งหมด ' และการนำเข้าทีละบรรทัด ตัวอย่างของคุณส่องสว่างมาก
Junchen

2
ฉันสับสนโดย " __all__และimport *ซ้ำซ้อน" __all__ถูกใช้โดยผู้บริโภคของโมดูลและfrom foo import *ถูกใช้โดยโมดูลตัวเองเพื่อใช้คนอื่น ๆ ....
Nick T

using __all__ and import * is redundant, only __all__ is needed สิ่งเหล่านี้ซ้ำซ้อนกันอย่างไร พวกเขาทำสิ่งต่าง ๆ
endolith

113

__init__.pyไฟล์ของฉันว่างเปล่าบ่อยกว่าไม่ โดยเฉพาะอย่างยิ่งผมไม่เคยมีfrom blah import *เป็นส่วนหนึ่งของ__init__.py- ถ้า "นำเข้าแพคเกจ" หมายถึงการเรียงลำดับของการเรียน, ฟังก์ชั่นอื่น ๆ ที่กำหนดไว้โดยตรงเป็นส่วนหนึ่งของแพคเกจแล้วฉันจะ lexically คัดลอกเนื้อหาของblah.pyเป็นแพคเกจของ__init__.pyแทนและลบblah.py( การคูณไฟล์ต้นฉบับนั้นทำได้ไม่ดีเลย)

หากคุณยืนยันในการสนับสนุนimport *สำนวน (eek) ดังนั้นการใช้__all__(โดยย่อรายการรายชื่อให้มากที่สุดเท่าที่คุณสามารถนำมาใช้ได้) อาจช่วยควบคุมความเสียหายได้ โดยทั่วไปเนมสเปซและการนำเข้าที่ชัดเจนเป็นสิ่งที่ดีและฉันขอแนะนำให้ทบทวนวิธีการใด ๆ โดยอาศัยแนวคิดใดแนวคิดหนึ่งหรือทั้งสองอย่าง -!


9
โดยส่วนตัวแล้วฉันชอบแยกสิ่งต่าง ๆ แล้วนำเข้า * เหตุผลก็คือแม้ว่าจะมีการพับและสิ่งของฉันยังคงเกลียดที่จะเรียกดูไฟล์ที่มีคลาสมากเกินไปแม้ว่าจะเกี่ยวข้อง
Stefano Borini

5
@stefano คิดเกี่ยวกับกรอบใหญ่ ถ้ามันใช้import *คุณต้องยอมรับกรอบทั้งหมดในทั้งหมดโดยไม่มีเงื่อนไขแม้คุณสมบัติที่คุณจะไม่ใช้ การ__init__.pyว่างเปล่าทำให้คุณมีโอกาสมากกว่าแค่ความหมายทั้งหมดหรือไม่มีอะไรเลย คิดเกี่ยวกับการบิด
มก.

หากปล่อยว่างไว้แม้หลังจากนำเข้า mobilescouter แล้วก็ยังไม่สามารถใช้ mobilescouter.mapper หรือ mobilescouter.vehicle หรือ mobilescouter.whether ไม่ได้นำเข้า mobilescouter.A, mobilescouter.B ..... เกินไป verbose?
sunqiang

6
@ sunqiang นี้เป็นส่วนตัว แต่ฉันไม่คิดอย่างนั้น from mobilescouter import A, Bเป็นเพียงบรรทัดของรหัสและคุณไม่มีโครงการที่มี 666 คลาสและทุกคนมีไฟล์ของตัวเองใช่มั้ย หากคุณมีสองหรือมากกว่าimport *ในรหัสของคุณคุณกำลังกรอก namespace ด้วยขยะที่มีศักยภาพและรวดเร็วคุณจะลืมว่าAมาจากไหน และถ้าเป็นแพคเกจบนทำเช่นเดียวกัน? คุณกำลังคว้าแพ็คเกจย่อยและแพ็คเกจย่อยทั้งหมด เช่นเซนของ python บอกว่าชัดเจนดีกว่าโดยนัย
มก.

1
@ mg หากมีบรรทัด "import A, B" ในไฟล์init .py ฉันสามารถเรียก A (หรือ B) ด้วยไวยากรณ์: mobilescouter.A; ถ้าเราใช้ "จาก mobilescouter import A, B" มันก็แค่ A.something บางครั้งเพียงแค่บรรทัดนี้ฉันจำไม่ได้ว่า A เป็น pacakge ย่อยของ mobilescouter และฉันคิดว่าสิ่งนี้มีส่วนทำให้เกิดมลภาวะ namespace (แม้ว่าจะดีกว่า "" จากการนำเข้า mobilescouter * "ฉันยังคงชอบ" import pkgname "ให้ผู้ใช้ อินเทอร์เฟซสาธารณะที่เหมือนกันดังนั้นinit .py จะทำสิ่งที่นำเข้า sub_pkgname
sunqiang

1

คุณ__init__.pyควรจะมีdocstring

แม้ว่าฟังก์ชั่นทั้งหมดจะมีการใช้งานในโมดูลและแพ็คเกจย่อย docstring แพ็กเกจของคุณเป็นสถานที่ในการเริ่มต้นเอกสาร ตัวอย่างเช่นพิจารณาแพ็คเกจงูemailใหญ่ เอกสารประกอบแพ็กเกจเป็นการแนะนำที่อธิบายถึงวัตถุประสงค์เบื้องหลังและส่วนประกอบต่างๆภายในแพ็คเกจทำงานร่วมกันอย่างไร หากคุณสร้างเอกสารโดยอัตโนมัติจาก docstrings โดยใช้สฟิงซ์หรือแพคเกจอื่น docstring แพคเกจเป็นสถานที่ที่เหมาะสมที่จะอธิบายการแนะนำดังกล่าว

สำหรับเนื้อหาอื่น ๆ โปรดดูคำตอบที่ดีเยี่ยมโดยfirecrowและอเล็กซ์เทล


แพคเกจที่เกิดขึ้นจริง__init__.pyเป็นemailไปตามแนวทางนี้หรือไม่? ฉันเห็น docstring บรรทัดเดียวที่ไม่ได้อธิบายอะไรมากมาย "ส่วนประกอบต่างๆภายในแพ็คเกจทำงานร่วมกันได้อย่างไร"
Gertlex

@Gertlex อาจเป็นเพียงเอกสารเว็บ
gerrit
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.