Python: นำเข้าโมดูลจากไดเร็กทอรีอื่นที่ระดับเดียวกันในลำดับชั้นของโปรเจ็กต์


89

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

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |
        |------- Scripts/
        |           |
        |           |----- __init__.py
        |           |----- CreateUser.py
        |           |----- FindUser.py

ถ้าฉันย้าย "CreateUser.py" ไปยังไดเร็กทอรี user_management หลักฉันสามารถใช้: "import Modules.LDAPManager"เพื่อนำเข้า LDAPManager.py --- วิธีนี้ใช้ได้ผล สิ่งที่ฉันทำไม่ได้ (ซึ่งฉันต้องการทำ) คือเก็บ CreateUser.py ไว้ในโฟลเดอร์ย่อย Scripts และนำเข้า LDAPManager.py ฉันหวังว่าจะทำสิ่งนี้ให้สำเร็จโดยใช้"import user_management.Modules.LDAPManager.py". วิธีนี้ใช้ไม่ได้ ในระยะสั้นฉันสามารถรับไฟล์ Python เพื่อให้มองลึกลงไปในลำดับชั้นได้อย่างง่ายดาย แต่ฉันไม่สามารถรับสคริปต์ Python เพื่ออ้างอิงไดเร็กทอรีหนึ่งและลงไปที่ไดเร็กทอรีอื่นได้

โปรดทราบว่าฉันสามารถแก้ปัญหาได้โดยใช้:

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import Modules.LDAPManager as LDAPManager

ฉันได้ยินมาว่านี่เป็นการปฏิบัติที่ไม่ดีและทำให้ท้อแท้

ไฟล์ในสคริปต์มีไว้เพื่อเรียกใช้งานโดยตรง ( init .py ในสคริปต์จำเป็นหรือไม่) ฉันได้อ่านแล้วว่าในกรณีนี้ฉันควรเรียกใช้ CreateUser.py ด้วยแฟล็ก -m ฉันได้ลองใช้รูปแบบต่างๆแล้วและดูเหมือนว่า CreateUser.py จะรับรู้ LDAPManager.py ไม่ได้

คำตอบ:


67

ถ้าฉันย้ายCreateUser.pyไปที่ไดเร็กทอรี user_management หลักฉันสามารถใช้: import Modules.LDAPManagerเพื่อนำเข้าLDAPManager.py - ใช้งานได้

กรุณาทำไม่ได้ ด้วยวิธีนี้LDAPManagerโมดูลที่ใช้CreateUserจะไม่เหมือนกับโมดูลที่นำเข้าผ่านการนำเข้าอื่น ๆ สิ่งนี้สามารถสร้างปัญหาได้เมื่อคุณมีสถานะโกลบอลบางอย่างในโมดูลหรือระหว่างการดอง / การแกะไม่ออก หลีกเลี่ยงการนำเข้าที่ใช้ได้เฉพาะเนื่องจากโมดูลอยู่ในไดเร็กทอรีเดียวกัน

เมื่อคุณมีโครงสร้างแพ็คเกจคุณควรทำดังนี้

  • ใช้การนำเข้าแบบสัมพัทธ์กล่าวคือถ้าCreateUser.pyอยู่ในScripts/:

     from ..Modules import LDAPManager
    

    ทราบว่านี้เป็น (หมายเหตุที่ผ่านมาเครียด) กำลังใจจากPEP 8เพียงเพราะรุ่นเก่าของงูหลามไม่สนับสนุนพวกเขาดีมาก แต่ปัญหานี้ถูกแก้ไขปีที่ผ่านมา ปัจจุบันรุ่นของ PEP 8 ไม่แนะนำให้พวกเขาเป็นทางเลือกที่ได้รับการยอมรับนำเข้าแน่นอน ฉันชอบพวกมันในแพ็คเกจจริงๆ

  • ใช้การนำเข้าแบบสัมบูรณ์โดยใช้ชื่อแพ็กเกจทั้งหมด ( CreateUser.pyในScripts/):

     from user_management.Modules import LDAPManager
    

เพื่อให้ชุดที่สองทำงานได้user_managementควรติดตั้งแพ็คเกจภายในไฟล์PYTHONPATH. ในระหว่างการพัฒนาคุณสามารถกำหนดค่า IDE เพื่อให้สิ่งนี้เกิดขึ้นได้โดยไม่ต้องเพิ่มการโทรด้วยตนเองไปsys.path.appendที่ใดก็ได้

นอกจากนี้ฉันพบว่ามันแปลกที่Scripts/เป็นแพ็คเกจย่อย เนื่องจากในการติดตั้งจริงuser_managementโมดูลจะถูกติดตั้งภายใต้site-packagesfound ในlib/ไดเร็กทอรี (ไดเร็กทอรีใดที่ใช้ในการติดตั้งไลบรารีใน OS ของคุณ) ในขณะที่ควรติดตั้งสคริปต์ภายใต้bin/ไดเร็กทอรี (ซึ่งมีไฟล์ปฏิบัติการสำหรับ OS ของคุณ)

ในความเป็นจริงผมเชื่อว่าจะไม่ได้อยู่ภายใต้การScript/ user_managementควรอยู่ในระดับเดียวกันของuser_management. ด้วยวิธีนี้คุณไม่จำเป็นต้องใช้-mแต่คุณต้องตรวจสอบให้แน่ใจว่าสามารถพบแพ็คเกจได้ (อีกครั้งเป็นเรื่องของการกำหนดค่า IDE การติดตั้งแพ็คเกจอย่างถูกต้องหรือใช้PYTHONPATH=. python Scripts/CreateUser.pyเพื่อเรียกใช้สคริปต์ด้วยพา ธ ที่ถูกต้อง)


โดยสรุปลำดับชั้นที่ฉันจะใช้คือ:

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |

 Scripts/  (*not* a package)
        |  
        |----- CreateUser.py
        |----- FindUser.py

จากนั้นรหัสของCreateUser.pyและFindUser.pyควรใช้การนำเข้าแบบสัมบูรณ์เพื่อนำเข้าโมดูล:

from user_management.Modules import LDAPManager

ในระหว่างการติดตั้งคุณต้องแน่ใจว่าuser_managementลงท้ายที่ใดที่หนึ่งในPYTHONPATHและสคริปต์ภายในไดเร็กทอรีสำหรับไฟล์ปฏิบัติการเพื่อให้สามารถค้นหาโมดูลได้ ในระหว่างการพัฒนาคุณต้องอาศัยคอนฟิกูเรชัน IDE หรือคุณเปิดCreateUser.pyการเพิ่มScripts/ไดเร็กทอรีหลักในPYTHONPATH(ฉันหมายถึงไดเร็กทอรีที่มีทั้งสองuser_managementและScripts):

PYTHONPATH=/the/parent/directory python Scripts/CreateUser.py

หรือคุณสามารถแก้ไขPYTHONPATHทั่วโลกเพื่อที่คุณจะได้ไม่ต้องระบุสิ่งนี้ในแต่ละครั้ง ในระบบปฏิบัติการยูนิกซ์ (linux, Mac OS X เป็นต้น) คุณสามารถแก้ไขเชลล์สคริปต์เพื่อกำหนดPYTHONPATHตัวแปรภายนอกได้บน Windows คุณต้องเปลี่ยนการตั้งค่าตัวแปรสภาพแวดล้อม


ภาคผนวกฉันเชื่อว่าหากคุณใช้ python2 จะเป็นการดีกว่าที่จะหลีกเลี่ยงการนำเข้าสัมพัทธ์โดยปริยายโดยใส่:

from __future__ import absolute_import

ที่ด้านบนสุดของโมดูลของคุณ วิธีนี้import X มักจะหมายถึงการที่จะนำเข้าระดับบนสุดโมดูลXและจะไม่พยายามที่จะนำเข้าX.pyไฟล์ที่อยู่ในไดเรกทอรีเดียวกัน (ถ้าไดเรกทอรีที่ไม่ได้อยู่ในPYTHONPATH) ด้วยวิธีนี้วิธีเดียวที่จะทำการอิมพอร์ตแบบสัมพัทธ์คือการใช้Explicit syntax (the from . import X) ซึ่งดีกว่า ( Explicit ดีกว่า implicit )

วิธีนี้จะช่วยให้แน่ใจว่าคุณจะไม่ใช้การนำเข้าสัมพัทธ์โดยนัย "ปลอม" เนื่องจากสิ่งเหล่านี้จะImportErrorส่งสัญญาณอย่างชัดเจนว่ามีบางอย่างผิดปกติ มิฉะนั้นคุณสามารถใช้โมดูลที่ไม่ใช่สิ่งที่คุณคิดได้


หากคุณใช้การนำเข้าแบบสัมพัทธ์คุณควรดำเนินการpython -m user_management.Scripts.CreateUser
mononoke

14

ตั้งแต่ Python 2.5 เป็นต้นไปคุณสามารถใช้ไฟล์

from ..Modules import LDAPManager

ช่วงเวลาสำคัญจะนำคุณ "ขึ้น" ระดับหนึ่งในมรดกของคุณ

ดูเอกสาร Python เกี่ยวกับการอ้างอิงภายในแพ็คเกจสำหรับการนำเข้า


3

ใน "root" __init__.pyคุณสามารถทำไฟล์

import sys
sys.path.insert(1, '.')

ซึ่งควรทำให้ทั้งสองโมดูลสามารถนำเข้าได้


0

ฉันประสบปัญหาเดียวกัน เพื่อแก้ปัญหานี้ฉันใช้export PYTHONPATH="$PWD". อย่างไรก็ตามในกรณีนี้คุณจะต้องแก้ไขการนำเข้าในScriptsdir ของคุณขึ้นอยู่กับด้านล่าง:

กรณีที่ 1: หากคุณอยู่ในuser_managementdir คุณscriptsควรใช้ลักษณะนี้from Modules import LDAPManagerเพื่อนำเข้าโมดูล

กรณีที่ 2: หากคุณไม่อยู่ในuser_managementระดับ 1 mainคุณscriptsควรใช้ลักษณะนี้from user_management.Modules import LDAPManagerเพื่อนำเข้าโมดูล

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