จะนำเข้าสัมพัทธ์ใน Python ได้อย่างไร


527

ลองนึกภาพโครงสร้างไดเรกทอรีนี้:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

ฉันเข้ารหัสและผมจำเป็นต้องนำเข้าจากบางสิ่งบางอย่างmod1 mod2ฉันควรทำอย่างไร

ฉันพยายามfrom ..sub2 import mod2แต่ฉันได้รับ "พยายามนำเข้าที่เกี่ยวข้องในแพคเกจที่ไม่ใช่"

ฉัน googled ไปรอบ ๆ แต่พบsys.path"แฮ็ค" การแฮ็ก ไม่มีวิธีที่สะอาดใช่มั้ย


แก้ไข: ทั้งหมดของฉัน__init__.pyว่างเปล่าในขณะนี้

Edit2: ฉันพยายามที่จะทำเช่นนี้เพราะ sub2 มีชั้นเรียนที่จะใช้ร่วมกันในแพคเกจย่อย ( sub1, subXฯลฯ )

แก้ไข 3: พฤติกรรมที่ฉันกำลังมองหาเหมือนกับที่อธิบายไว้ในPEP 366 (ขอบคุณ John B)


8
ฉันขอแนะนำให้อัปเดตคำถามของคุณเพื่อให้ชัดเจนยิ่งขึ้นว่าคุณกำลังอธิบายปัญหาที่ระบุไว้ใน PEP 366
John B

2
มันเป็นคำอธิบายที่ยืดยาว แต่ตรวจสอบที่นี่: stackoverflow.com/a/10713254/1267156ฉันตอบคำถามที่คล้ายกันมาก ฉันมีปัญหาเดียวกันนี้จนกระทั่งเมื่อคืนนี้
Sevvy325

3
สำหรับผู้ที่ต้องการโหลดโมดูลที่ตั้งอยู่ในเส้นทางที่กำหนดเองให้ดูที่: stackoverflow.com/questions/67631/…
Evgeni Sergeev

2
ในบันทึกที่เกี่ยวข้อง Python 3 จะเปลี่ยนการจัดการการนำเข้าเริ่มต้นให้เป็นค่าเริ่มต้นโดยสมบูรณ์ การนำเข้าที่เกี่ยวข้องจะต้องมีการระบุไว้อย่างชัดเจน
Ross

คำตอบ:


337

ทุกคนดูเหมือนจะต้องการบอกคุณว่าคุณควรทำอะไรมากกว่าแค่ตอบคำถาม

ปัญหาคือคุณใช้โมดูลเป็น '__main__' โดยส่ง mod1.py เป็นอาร์กิวเมนต์ให้กับล่าม

จากPEP 328 :

การอิมพอร์ตสัมพัทธ์ใช้แอ็ตทริบิวต์ __name__ ของโมดูลเพื่อกำหนดตำแหน่งของโมดูลนั้นในลำดับชั้นของแพ็คเกจ หากชื่อของโมดูลไม่มีข้อมูลแพ็กเกจใด ๆ (เช่นถูกตั้งค่าเป็น '__main__') การอิมพอร์ตสัมพัทธ์จะได้รับการแก้ไขราวกับว่าโมดูลนั้นเป็นโมดูลระดับบนสุดไม่ว่าโมดูลนั้นจะอยู่ที่ใดในระบบไฟล์

ใน Python 2.6 พวกเขากำลังเพิ่มความสามารถในการอ้างอิงโมดูลที่สัมพันธ์กับโมดูลหลัก PEP 366อธิบายการเปลี่ยนแปลง

Update : ตาม Nick Coghlan ทางเลือกที่แนะนำคือการเรียกใช้โมดูลภายในแพ็คเกจโดยใช้สวิตช์ -m


2
คำตอบที่นี่เกี่ยวข้องกับ messing กับ sys.path ทุกจุดเข้าสู่โปรแกรม ฉันเดาว่าเป็นวิธีเดียวที่จะทำ
Nick Retallack

76
ทางเลือกที่แนะนำคือการเรียกใช้โมดูลภายในแพ็คเกจโดยใช้-mสวิตช์แทนการระบุชื่อไฟล์โดยตรง
ncoghlan

127
ฉันไม่เข้าใจ: คำตอบที่นี่อยู่ที่ไหน วิธีหนึ่งนำเข้าโมดูลในโครงสร้างไดเรกทอรีเช่น?
ทอม

27
@ Tom: from sub2 import mod2ในกรณีนี้จะ จากนั้นจะเรียกใช้ mod1 จากภายใน app python -m sub1.mod1ทำ
Xiong Chiamiov

11
@XiongChiamiov: นี่หมายความว่าคุณไม่สามารถทำได้ถ้างูหลามของคุณฝังอยู่ในแอปพลิเคชันดังนั้นคุณจึงไม่มีสิทธิ์เข้าถึงสวิตช์บรรทัดคำสั่งของหลาม?
LarsH

130

นี่คือวิธีแก้ปัญหาที่เหมาะกับฉัน:

ฉันทำการนำเข้าญาติ from ..sub2 import mod2 แล้วถ้าผมต้องการที่จะทำงานmod1.pyแล้วผมไปที่ไดเรกทอรีแม่ของappและเรียกใช้โมดูลโดยใช้สวิทช์หลาม -m python -m app.sub1.mod1เป็น

เหตุผลที่แท้จริงว่าทำไมปัญหานี้เกิดขึ้นกับการนำเข้าแบบสัมพัทธ์นั่นคือการนำเข้าแบบสัมพัทธ์ใช้งานได้โดยการรับ__name__คุณสมบัติของโมดูล หากโมดูลกำลังถูกเรียกใช้โดยตรง__name__จะถูกตั้งค่าเป็น__main__และไม่มีข้อมูลใด ๆ เกี่ยวกับโครงสร้างแพ็คเกจ และนั่นเป็นเหตุผลที่งูหลามบ่นเกี่ยวกับrelative import in non-packageข้อผิดพลาด

ดังนั้นโดยใช้สวิตช์ -m คุณให้ข้อมูลโครงสร้างแพ็กเกจแก่ไพ ธ อนซึ่งสามารถแก้ไขการอิมพอร์ตสัมพัทธ์ได้สำเร็จ

ฉันพบปัญหานี้หลายครั้งในขณะที่ทำการนำเข้าที่สัมพันธ์กัน และหลังจากอ่านคำตอบก่อนหน้าทั้งหมดแล้วฉันก็ยังไม่สามารถหาวิธีแก้ไขได้อย่างสะอาดโดยไม่ต้องใส่รหัสสำเร็จรูปในไฟล์ทั้งหมด (แม้ว่าความคิดเห็นบางส่วนจะมีประโยชน์จริงๆขอบคุณ @ncoghlan และ @XiongChiamiov)

หวังว่านี่จะช่วยให้ใครบางคนที่กำลังต่อสู้กับปัญหาการนำเข้าที่เกี่ยวข้องเนื่องจากการผ่าน PEP ไม่สนุก


9
IMHO คำตอบที่ดีที่สุด: ไม่เพียง แต่อธิบายว่าทำไม OP มีปัญหา แต่ยังพบวิธีที่จะแก้มันได้โดยไม่ต้องเปลี่ยนวิธีการโมดูลของเขาทำการนำเข้า หลังจากนั้นการนำเข้าที่เกี่ยวข้องของ OP นั้นดี ผู้ร้ายคือการขาดการเข้าถึงแพคเกจด้านนอกเมื่อทำงานเป็นสคริปต์โดยตรงสิ่งที่-mออกแบบมาเพื่อแก้ไข
MestreLion

26
รับทราบด้วย: คำตอบนี้คือ 5 ปีหลังจากคำถาม คุณสมบัติเหล่านี้ไม่สามารถใช้ได้ในเวลานั้น
JeremyKun

1
from . import some_moduleหากคุณต้องการที่จะนำเข้าโมดูลจากไดเรกทอรีเดียวกันที่คุณสามารถทำได้
Rotareti

124
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. python main.pyคุณเรียก
  2. main.py ทำ: import app.package_a.module_a
  3. module_a.py ทำ import app.package_b.module_b

อีกทางเลือกที่ 2 หรือ 3 สามารถใช้: from app.package_a import module_a

มันจะทำงานได้นานเท่าที่คุณมีappใน PYTHONPATH main.pyอาจจะเป็นทุกที่แล้ว

ดังนั้นคุณจึงเขียน a setup.pyเพื่อคัดลอก (ติดตั้ง) ทั้งแพคเกจแอปและแพ็คเกจย่อยไปยังโฟลเดอร์หลามของระบบเป้าหมายและmain.pyไปยังโฟลเดอร์สคริปต์ของระบบเป้าหมาย


3
คำตอบที่ยอดเยี่ยม มีวิธีนำเข้าด้วยวิธีดังกล่าวโดยไม่ติดตั้งแพคเกจใน PYTHONPATH หรือไม่?
auraham

4
แนะนำให้อ่านเพิ่มเติม: blog.habnab.it/blog/2013/07/21/python-packages-and-you
nosklo

6
จากนั้นวันหนึ่งต้องเปลี่ยนชื่อแอปเป็น test_app อะไรจะเกิดขึ้น? คุณจะต้องเปลี่ยนรหัสที่มาทั้งหมดนำเข้า app.package_b.module_b -> test_app.package_b.module_b นี่คือการปฏิบัติที่ไม่ดีอย่างแน่นอน ... และเราควรพยายามใช้การนำเข้าที่เกี่ยวข้องภายในแพ็คเกจ
Spybdai

49

"Guido เรียกใช้สคริปต์ภายในแพ็คเกจเป็นรูปแบบต่อต้าน" (ปฏิเสธ PEP-3122 )

ฉันใช้เวลามากมายในการพยายามหาทางแก้ไขการอ่านโพสต์ที่เกี่ยวข้องใน Stack Overflow และพูดกับตัวเองว่า "ต้องมีวิธีที่ดีกว่านี้!" ดูเหมือนว่าจะไม่มี


10
หมายเหตุ: ได้กล่าวถึงpep-366 (สร้างขึ้นในเวลาเดียวกันกับpep-3122 ) ให้ความสามารถเดียวกัน แต่ใช้การใช้งานร่วมกันได้แบบย้อนหลังที่แตกต่างกันเช่นถ้าคุณต้องการเรียกใช้โมดูลภายในแพ็คเกจเป็นสคริปต์และใช้การนำเข้าที่สัมพันธ์อย่างชัดเจน ในนั้นคุณสามารถเรียกใช้โดยใช้-mสวิตช์: python -m app.sub1.mod1หรือเรียกใช้app.sub1.mod1.main()จากสคริปต์ระดับบนสุด (เช่นสร้างขึ้นจาก entry_points setuptools 'ที่กำหนดไว้ใน setup.py)
jfs

+1 สำหรับการใช้ setuptools และจุดเข้า - เป็นวิธีที่เหมาะสมในการตั้งค่าสคริปต์ที่จะทำงานจากภายนอกในสถานที่ที่กำหนดไว้อย่างชัดเจนเมื่อเทียบกับการแฮ็ค PYTHONPATH อย่างไม่สิ้นสุด
RecencyEffect

38

วิธีนี้แก้ไขได้ 100%:

  • app /
    • main.py
  • การตั้งค่า /
    • local_setings.py

นำเข้าการตั้งค่า / local_setting.py ในแอพ / main.py:

main.py:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')

2
ขอบคุณ! ppl ทั้งหมดถูกบังคับให้ฉันเรียกใช้สคริปต์ของฉันแตกต่างกันแทนที่จะบอกวิธีแก้ปัญหาภายในสคริปต์ แต่ฉันต้องเปลี่ยนรหัสเพื่อใช้sys.path.insert(0, "../settings")แล้วfrom local_settings import *
Vit Bernatik

25
def import_path(fullpath):
    """ 
    Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.append(path)
    module = __import__(filename)
    reload(module) # Might be out of date
    del sys.path[-1]
    return module

ฉันกำลังใช้ข้อมูลโค้ดนี้เพื่อนำเข้าโมดูลจากเส้นทางหวังว่าจะช่วยได้


2
ฉันกำลังใช้ข้อมูลโค้ดนี้รวมกับโมดูลภูตผีปีศาจ (ดังที่อธิบายไว้ที่นี่ [1]) เพื่อผลที่ยอดเยี่ยม [1]: stackoverflow.com/questions/1096216/ …
Xiong Chiamiov

7
อาจเปลี่ยน sys.path.append (พา ธ ) ด้วย sys.path.insert (0, พา ธ ) และ sys.path [-1] ควรแทนที่ด้วย sys.path [0] มิฉะนั้นฟังก์ชันจะนำเข้าโมดูลที่ไม่ถูกต้องหากมีโมดูลที่มีชื่อเดียวกันในเส้นทางการค้นหาอยู่แล้ว เช่นหากมี "some.py" ใน dir ปัจจุบัน import_path ("/ import / some.py") จะนำเข้าไฟล์ผิด
Alex Che

ฉันเห็นด้วย! บางครั้งการนำเข้าแบบญาติอื่น ๆ จะมาก่อน ใช้ sys.path.insert
iElectric

คุณจะทำซ้ำพฤติกรรมของจากการนำเข้า x y (หรือ *) ได้อย่างไร?
levesque

ยังไม่ชัดเจนโปรดระบุการใช้งานเต็มรูปแบบของสคริปต์นี้เพื่อแก้ปัญหา OP
mrgloom

21

คำอธิบายของnosklo'sคำตอบด้วยตัวอย่าง

หมายเหตุ: __init__.pyไฟล์ทั้งหมดว่างเปล่า

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

app / package_a / fun_a.py

def print_a():
    print 'This is a function in dir package_a'

app / package_b / fun_b.py

from app.package_a.fun_a import print_a
def print_b():
    print 'This is a function in dir package_b'
    print 'going to call a function in dir package_a'
    print '-'*30
    print_a()

main.py

from app.package_b import fun_b
fun_b.print_b()

ถ้าคุณเรียกใช้$ python main.pyมันกลับมา:

This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
  • main.py ทำ: from app.package_b import fun_b
  • fun_b.py ทำ from app.package_a.fun_a import print_a

ดังนั้นไฟล์ในโฟลเดอร์package_bไฟล์ที่ใช้ในโฟลเดอร์package_aซึ่งเป็นสิ่งที่คุณต้องการ ขวา??


12

น่าเสียดายที่แฮ็ค sys.path นั้นใช้งานได้ดี

ฉันพบปัญหานี้กับเลเยอร์อื่น: ฉันมีโมดูลชื่อที่ระบุอยู่แล้ว แต่มันเป็นโมดูลที่ผิด

สิ่งที่ฉันต้องการทำคือสิ่งต่อไปนี้ (โมดูลที่ฉันทำงานจากนั้นคือโมดูล 3):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

โปรดทราบว่าฉันได้ติดตั้ง mymodule แล้ว แต่ในการติดตั้งของฉันฉันไม่มี "mymodule1"

และฉันจะได้รับ ImportError เพราะพยายามนำเข้าจากโมดูลที่ติดตั้งของฉัน

ฉันพยายามทำ sys.path.append และไม่ได้ผล สิ่งที่ได้ผลคือsys.path.insert

if __name__ == '__main__':
    sys.path.insert(0, '../..')

แฮ็คชนิดหนึ่ง แต่ให้ทุกอย่างใช้งานได้! ดังนั้นโปรดจำไว้ว่าถ้าคุณต้องการให้การตัดสินใจของคุณแทนที่เส้นทางอื่นคุณต้องใช้ sys.path.insert (0, ชื่อพา ธ ) เพื่อให้มันทำงาน! นี่เป็นจุดที่น่าผิดหวังมากสำหรับฉันคนพูดว่าใช้ฟังก์ชั่น "ผนวก" เพื่อ sys.path แต่ไม่ได้ผลถ้าคุณมีโมดูลที่กำหนดไว้แล้ว (ฉันพบว่ามันมีพฤติกรรมที่แปลกมาก)


sys.path.append('../')ทำงานได้ดีสำหรับฉัน (Python 3.5.2)
Nister

ฉันคิดว่ามันดีเพราะมันทำให้แฮ็คไปทำงานได้ดีและไม่ส่งผลกระทบต่อโมดูลอื่นซึ่งอาจขึ้นอยู่กับแพ็คเกจของคุณ
Tom Russell

10

ขอผมใส่สิ่งนี้ไว้ที่นี่เพื่ออ้างอิงเอง ฉันรู้ว่ามันไม่ได้เป็นรหัส Python ที่ดี แต่ฉันต้องการสคริปต์สำหรับโครงการที่ฉันทำงานอยู่และฉันต้องการที่จะวางสคริปต์ในscriptsไดเรกทอรี

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

9

ในฐานะที่เป็น @EvgeniSergeev กล่าวว่าในความคิดเห็นที่จะ OP คุณสามารถนำเข้ารหัสจาก.pyไฟล์ที่ตำแหน่งโดยพลการด้วย:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

นี้นำมาจากคำตอบ SOนี้


4

ลองดูที่http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports คุณสามารถทำได้

from .mod1 import stuff

6
ยกเว้นสิ่งหนึ่งที่ไม่สามารถทำการนำเข้าแบบสัมพัทธ์จากโมดูล ' main ' เป็นคำตอบจาก John B. States
PuercoPop

2

จากงูหลาม doc ,

ใน Python 2.5 คุณสามารถสลับการทำงานของการนำเข้าเป็นการนำเข้าแบบสัมบูรณ์โดยใช้from __future__ import absolute_importคำสั่ง พฤติกรรมการนำเข้าสัมบูรณ์นี้จะกลายเป็นค่าเริ่มต้นในเวอร์ชันในอนาคต (อาจเป็น Python 2.7) เมื่อการนำเข้าแบบสัมบูรณ์เป็นค่าเริ่มต้นimport stringจะค้นหาเวอร์ชันของไลบรารีมาตรฐานเสมอ แนะนำว่าผู้ใช้ควรเริ่มใช้การนำเข้าแบบสัมบูรณ์ให้มากที่สุดดังนั้นจึงควรเริ่มเขียนfrom pkg import stringโค้ดของคุณ


1

ฉันพบว่าการตั้งค่าตัวแปรสภาพแวดล้อม "PYTHONPATH" เป็นเรื่องง่ายไปยังโฟลเดอร์ด้านบน:

bash$ export PYTHONPATH=/PATH/TO/APP

แล้ว:

import sub1.func1
#...more import

แน่นอน PYTHONPATH คือ "ทั่วโลก" แต่มันก็ไม่สร้างปัญหาให้ฉันเลย


นี่เป็นวิธีที่virtualenvช่วยให้คุณจัดการคำสั่งการนำเข้าของคุณได้
byxor

1

ด้านบนของสิ่งที่ John B กล่าวดูเหมือนว่าการตั้งค่า__package__ตัวแปรควรช่วยแทนการเปลี่ยน__main__ซึ่งสามารถทำให้สิ่งอื่น ๆ แต่เท่าที่ฉันสามารถทดสอบมันไม่ได้ทำงานอย่างสมบูรณ์ตามที่ควร

ฉันมีปัญหาเดียวกันและทั้ง PEP 328 หรือ 366 ไม่สามารถแก้ปัญหาได้อย่างสมบูรณ์ทั้งในตอนท้ายของวันต้องมีหัวหน้าของแพคเกจที่จะรวมอยู่ในsys.pathเท่าที่ฉันสามารถเข้าใจ

ฉันควรจะพูดถึงว่าฉันไม่พบวิธีการจัดรูปแบบสตริงที่ควรเข้าไปในตัวแปรเหล่านั้น มันคือ"package_head.subfolder.module_name"อะไรหรือ


0

คุณต้องผนวกเส้นทางของโมดูลไปที่PYTHONPATH:

export PYTHONPATH="${PYTHONPATH}:/path/to/your/module/"

1
นี่เป็นสิ่งเดียวกับการยักย้ายถ่ายเทsys.pathตั้งแต่sys.pathเริ่มต้นจากPYTHONPATH
Joril

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