การนำเข้าจากเส้นทางสัมพัทธ์ใน Python


113

ฉันมีโฟลเดอร์สำหรับรหัสไคลเอ็นต์โฟลเดอร์สำหรับรหัสเซิร์ฟเวอร์ของฉันและโฟลเดอร์สำหรับรหัสที่แชร์ระหว่างกัน

Proj/
    Client/
        Client.py
    Server/
        Server.py
    Common/
        __init__.py
        Common.py

ฉันจะนำเข้า Common.py จาก Server.py และ Client.py ได้อย่างไร


ที่เกี่ยวข้อง: stackoverflow.com/q/72852/1025391
moooeeeep

คำตอบ:


150

แก้ไขพฤศจิกายน 2014 (3 ปีต่อมา):

Python 2.6 และ 3.x รองรับการนำเข้าสัมพัทธ์ที่เหมาะสมซึ่งคุณสามารถหลีกเลี่ยงการทำอะไรแฮ็คได้ ด้วยวิธีนี้คุณจะรู้ว่าคุณได้รับการนำเข้าแบบสัมพัทธ์แทนที่จะเป็นการนำเข้าแบบสัมบูรณ์ '.. ' หมายถึงไปที่ไดเร็กทอรีด้านบนของฉัน:

from ..Common import Common

ตามเงื่อนไขนี้จะใช้ได้เฉพาะเมื่อคุณเรียกใช้ python เป็นโมดูลจากภายนอกแพ็กเกจ ตัวอย่างเช่น:

python -m Proj

วิธีแฮ็กดั้งเดิม

วิธีนี้ยังคงใช้กันทั่วไปในบางสถานการณ์โดยที่คุณไม่เคย 'ติดตั้ง' แพ็กเกจของคุณ ตัวอย่างเช่นเป็นที่นิยมในหมู่ผู้ใช้ Django

คุณสามารถเพิ่ม Common / ไปยัง sys.path ของคุณ (รายการพา ธ ที่ python ดูเพื่อนำเข้าสิ่งต่างๆ):

import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'Common'))
import Common

os.path.dirname(__file__) เพียงแค่ให้ไดเร็กทอรีที่มีไฟล์ python ปัจจุบันของคุณอยู่จากนั้นเราไปที่ไดเร็กทอรี 'Common /' และนำเข้า 'Common' โมดูล


2
อย่าแก้ไขเส้นทางโมดูล python ด้วยตนเองอาจเป็นเพียงการแฮ็กด่วนเท่านั้น การเรียนรู้การจัดการแพคเกจ Python โดยใช้ distutils, setuptools และอื่น ๆ มักเป็นทักษะที่จำเป็นซึ่งจะช่วยแก้ปัญหาเช่นนั้นได้
Sascha Gottfried

1
@SaschaGottfried เห็นด้วยอย่างยิ่งแม้ว่าคุณจะไม่ได้ทำแพ็คเกจที่สามารถแจกจ่ายได้ แต่ก็อาจไม่สำคัญ ตัวอย่างเช่นใน Django คุณจะไม่เคยติดตั้งแอปของคุณด้วย distutils ดังนั้นวิธีการข้างต้นจึงเป็นการแฮ็กที่ง่าย แต่อย่างไรก็ตามฉันได้แก้ไขคำตอบด้วยสิ่งที่ฉันจะทำในวันนี้
Dave

36
ขอบคุณที่ตอบคำถามจริงแทนการเทศนาเกี่ยวกับเทคนิคที่เหมาะสม มีเหตุผลที่ดีมากมายในการนำเข้าแบบสัมพัทธ์
shrewmouse

คุณจะขึ้นไปมากกว่าหนึ่งระดับได้อย่างไร
jxramos

10
เพื่อขึ้นไปอีกหนึ่งระดับให้ใช้จุดเพิ่มเติมสำหรับแต่ละระดับ @jxramos ex: from ...myfileไปที่../../myfile
WattsInABox

11

ตลกดีปัญหาเดียวกับที่ฉันเพิ่งพบและฉันได้รับงานนี้ด้วยวิธีต่อไปนี้:

เมื่อรวมกับคำสั่ง linux lnเราสามารถทำให้มันง่ายขึ้นมาก:

1. cd Proj/Client
2. ln -s ../Common ./

3. cd Proj/Server
4. ln -s ../Common ./

และตอนนี้หากคุณต้องการนำเข้าsome_stuffจากไฟล์: Proj/Common/Common.pyไปยังไฟล์ของคุณ: Proj/Client/Client.pyเช่นนี้:

# in Proj/Client/Client.py
from Common.Common import some_stuff

และเช่นเดียวกันกับProj/Serverยังใช้ได้กับsetup.pyกระบวนการ คำถามเดียวกันที่กล่าวถึงที่นี่หวังว่าจะช่วยได้!


10

อย่านำเข้าแบบสัมพัทธ์

จากPEP8 :

ไม่แนะนำการนำเข้าสัมพัทธ์สำหรับการนำเข้าภายในแพ็คเกจ

ใส่รหัสทั้งหมดของคุณลงในแพ็กเกจพิเศษ (เช่น "myapp") และใช้แพ็กเกจย่อยสำหรับไคลเอนต์เซิร์ฟเวอร์และรหัสทั่วไป

อัปเดต: " Python 2.6 และ 3.x รองรับการนำเข้าสัมพัทธ์ที่เหมาะสม (... ) " ดูคำตอบของ Daveสำหรับรายละเอียดเพิ่มเติม


1
ลองนึกภาพว่าคุณเพิ่มโค้ดที่ท้ายไคลเอนต์และเซิร์ฟเวอร์หลังif __name__ == "__main__":บรรทัด "" นั่นคือคุณต้องการใช้เป็นสคริปต์แบบสแตนด์อโลน ทำอย่างไรจึงจะเหมาะสม? ฉันคิดว่ามันเป็นกรณีการใช้งานทั่วไปที่ควรได้รับการสนับสนุน ทำไมถึงท้อถอย?
Jabba

85
ฉันกำลังประหลาดใจว่า "อย่าทำมันได้" เป็นคำตอบที่ได้รับการยอมรับสำหรับ "ฉันจะ ..." คำถาม (ดียกเว้น Rails <กรัม>.) มีมีเหตุผลบางครั้งการทำเช่นนี้ ฉันใช้วิธีแก้ปัญหาที่คล้ายกับที่เดฟแนะนำ
Tom Wilson

1
@ TomWilson: คำตอบ "ไม่ทำ" มันไม่บริสุทธิ์ มี "ทำแบบนี้" อยู่ด้านล่าง
MichałŠrajer

2
ใครบางคนควรบอกพวกเขาที่ Numpy! พวกเขาใช้การนำเข้าแบบสัมพัทธ์เป็นตัน!
Austin A

12
คำตอบนี้ใช้ไม่ได้กับ Python เวอร์ชันปัจจุบัน ส่วนที่ยกมาจะไม่พบใน PEP 8 อีกต่อไปปัจจุบันมีข้อความว่า"การนำเข้าแบบสัมพัทธ์อย่างชัดเจนเป็นทางเลือกที่ยอมรับได้สำหรับการนำเข้าแบบสัมบูรณ์โดยเฉพาะอย่างยิ่งเมื่อต้องจัดการกับรูปแบบบรรจุภัณฑ์ที่ซับซ้อนซึ่งการใช้การนำเข้าแบบสัมบูรณ์จะเป็นการใช้รายละเอียดโดยไม่จำเป็น"
moooeeeep

9

การนำเข้าแบบสัมพัทธ์นั้นโอเคอย่างแน่นอน! นี่คือสิ่งที่ฉันทำเล็กน้อย:

#first change the cwd to the script path
scriptPath = os.path.realpath(os.path.dirname(sys.argv[0]))
os.chdir(scriptPath)

#append the relative location you want to import from
sys.path.append("../common")

#import your module stored in '../common'
import common.py

1
แต่คุณควรทราบดีกว่าว่า sys.argv [0] ชี้ไปที่ใด - มัน (prolly) ไม่ใช่ไดเร็กทอรีที่คุณอยู่เมื่อคุณเริ่ม python
CarlH

นี่เป็นการแฮ็คที่รวดเร็วและมีข้อผิดพลาดมากมาย แต่คำถามก็ไม่ได้ดีขึ้น
Sascha Gottfried

1
สิ่งนี้เขียนไว้อย่างชัดเจน แต่แฮ็คดั้งเดิมในคำตอบของ Daveนั้นดีกว่าเพราะใช้__file__เพื่อรับความสัมพันธ์ที่เหมาะสมจากไฟล์ปัจจุบัน
John Neuhaus

4

วิธีการนำเข้าเริ่มต้นเป็น "สัมพัทธ์" อยู่แล้วจาก PYTHONPATH PYTHONPATH เป็นค่าเริ่มต้นสำหรับไลบรารีระบบบางส่วนพร้อมกับโฟลเดอร์ของไฟล์ต้นฉบับดั้งเดิม หากคุณรันด้วย -m เพื่อรันโมดูลไดเร็กทอรีปัจจุบันจะถูกเพิ่มลงใน PYTHONPATH ดังนั้นหากจุดเริ่มต้นของโปรแกรมของคุณอยู่ใน Proj การใช้import Common.Commonควรทำงานภายในทั้ง Server.py และ Client.py

อย่านำเข้าแบบสัมพัทธ์ มันจะไม่ได้ผลตามที่คุณต้องการ


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