นำเข้าไฟล์จากไดเรกทอรีย่อยหรือไม่


456

ฉันมีไฟล์ที่เรียกว่าตั้งอยู่บนtester.py/project

/projectมีไดเรกทอรีย่อยที่เรียกว่าlibโดยมีไฟล์ชื่อBoxTime.py:

/project/tester.py
/project/lib/BoxTime.py

ฉันต้องการที่จะนำเข้าจากBoxTime testerฉันได้ลองสิ่งนี้แล้ว:

import lib.BoxTime

ซึ่งส่งผลให้:

Traceback (most recent call last):
  File "./tester.py", line 3, in <module>
    import lib.BoxTime
ImportError: No module named lib.BoxTime

แนวคิดใดที่นำเข้าBoxTimeจากไดเรกทอรีย่อย?

แก้ไข

__init__.pyเป็นปัญหา แต่ไม่ลืมที่จะอ้างถึงBoxTimeในฐานะlib.BoxTimeหรือการใช้งาน:

import lib.BoxTime as BT
...
BT.bt_function()

คำตอบ:


536

ดูเอกสารแพคเกจ (มาตรา 6.4) ได้ที่นี่: http://docs.python.org/tutorial/modules.html

ในระยะสั้นคุณจะต้องใส่ชื่อไฟล์เปล่า

__init__.py

ในไดเรกทอรี "lib"


59
ทำไมถึงรู้สึกแฮ็ค ? มันเป็นวิธีที่หลามทำเครื่องหมายไดเรกทอรีนำเข้า / ปลอดภัย
IAbstract

7
ไม่เพียง แต่ทำเครื่องหมายไดเรกทอรีนำเข้าที่ปลอดภัย / พร้อมใช้งาน แต่ยังมีวิธีเรียกใช้รหัสเริ่มต้นบางอย่างเมื่อนำเข้าชื่อไดเรกทอรี
Sadjad

32
ใช่นี่มันแฮ็กและสกปรกและในความคิดของฉันภาษาไม่ควรกำหนดวิธีการโหลดไฟล์ข้ามระบบไฟล์ ใน PHP เราแก้ปัญหาโดยให้รหัสผู้ใช้ลงทะเบียนฟังก์ชั่นการโหลดอัตโนมัติหลายฟังก์ชั่นที่เรียกว่าเมื่อ namespace / class หายไป จากนั้นชุมชนก็สร้างมาตรฐาน PSR-4 และนักแต่งเพลงก็ใช้มันและทุกวันนี้ไม่มีใครต้องกังวล และไม่มี__init__ไฟล์hardcoded โง่ ๆ(แต่ถ้าคุณต้องการเพียงแค่ลงทะเบียนตะขอ autoloading! นี่คือความแตกต่างระหว่างแฮ็คและแฮ็ค )
Morgan Touverey Quilling

4
@ AurélienOomsimport sys, os; sys.path.insert(0, os.path.abspath('..')); from sibling_package.hacks import HackyHackHack
jbowman

4
หลามเป็นระเบียบหนึ่ง :)
จิมมี่ Pettersson

174
  • libสร้างไดเรกทอรีย่อยชื่อ
  • lib\__init__.pyสร้างไฟล์ที่ว่างเปล่าชื่อ
  • ในlib\BoxTime.pyเขียนฟังก์ชันfoo()ดังนี้:

    def foo():
        print "foo!"
  • ในรหัสลูกค้าของคุณในไดเรกทอรีด้านบนlibเขียน:

    from lib import BoxTime
    BoxTime.foo()
  • เรียกใช้รหัสลูกค้าของคุณ คุณจะได้รับ:

    foo!

มากในภายหลัง - ใน linux จะมีลักษณะเช่นนี้:

% cd ~/tmp
% mkdir lib
% touch lib/__init__.py
% cat > lib/BoxTime.py << EOF
heredoc> def foo():
heredoc>     print "foo!"
heredoc> EOF
% tree lib
lib
├── BoxTime.py
└── __init__.py

0 directories, 2 files
% python 
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from lib import BoxTime
>>> BoxTime.foo()
foo!

2
คุณสามารถให้ลิงค์ไปยังเอกสารของ Python ซึ่งมีการอธิบายได้ไหม? ขอบคุณ!
Zenon

5
มาทำให้ลิงก์นั้นคลิกได้: docs.python.org/3/tutorial/modules.html#packages
Gabriel Staples

คำแนะนำที่ดีสำหรับการใช้งานแพคเกจlib
MasterControlProgram

โปรดทราบ: เขตย่อยจะต้องไม่มีเครื่องหมายขีดคั่นหรือจุด แต่ขีดเส้นใต้นั้นถูกต้อง สำหรับฉันที่ดูเหมือนข้อ จำกัด เช่นเดียวกับชื่อสัญลักษณ์อื่น ๆ แต่ฉันยังไม่ได้ขุดลงไปถึงระดับเอกสาร
Alexander Stohr

underscores => python3 (สายเกินไปสำหรับการแก้ไขความคิดเห็น)
Alexander Stohr

68

คุณสามารถลองแทรกในsys.path:

sys.path.insert(0, './lib')
import BoxTime

11
นี้เป็นที่ดีถ้าคุณด้วยเหตุผลบางอย่างไม่สามารถหรือจะไม่สร้างinitไฟล์ .py
jpihl

1
มันทำงานได้ถ้าคุณเรียกใช้ python จากไดเรกทอรี "project" ส่วน "." ถูกแปลความสัมพันธ์กับไดเรกทอรีการทำงานปัจจุบันของคุณไม่ใช่ความสัมพันธ์กับไดเรกทอรีที่ไฟล์ที่คุณใช้งานอยู่ สมมติว่าคุณ,cd /data python ../project/tester.pyจากนั้นมันจะไม่ทำงาน
Morningstar

2
สิ่งนี้ใช้ได้สำหรับฉัน ฉันชอบสิ่งนี้มากกว่าไฟล์init .py มันทำขึ้นเพื่อแถลงการณ์การนำเข้าที่สะอาดยิ่งขึ้น
Taylor Evanson

5
วิธีนี้ใช้งานได้ดีกว่ามากและเป็นโซลูชันที่ "ถูกต้อง" init .py ทำให้แพคเกจยุ่งเช่น boto ที่มีโฟลเดอร์ย่อยของตนเองพร้อมโมดูล
Dave Dopson

1
@jpihl คุณต้องสร้าง (อย่างน้อย) ไฟล์ empy ชื่อ__init__.pyเพื่ออนุญาตให้ python นำเข้าโมดูลจากโฟลเดอร์นั้น ฉันได้ลองวิธีการแก้ปัญหานั้นและทำงานได้อย่างสมบูรณ์ (v2.7.6)
m3nda

31

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

libคุณไม่จำเป็นที่จะตั้งชื่อไดเรกทอรีย่อยของคุณ คุณสามารถตั้งชื่อมันanythingให้คุณใส่__init__.pyมัน

คุณสามารถทำได้โดยการป้อนคำสั่งต่อไปนี้ใน linux shell:

$ touch anything/__init__.py 

ดังนั้นตอนนี้คุณมีโครงสร้างนี้:

$ ls anything/
__init__.py
mylib.py

$ ls
main.py

จากนั้นคุณสามารถนำmylibเข้าmain.pyเช่นนี้:

from anything import mylib 

mylib.myfun()

คุณสามารถนำเข้าฟังก์ชั่นและชั้นเรียนเช่นนี้:

from anything.mylib import MyClass
from anything.mylib import myfun

instance = MyClass()
result = myfun()

__init__.pyสามารถเข้าถึงฟังก์ชั่นตัวแปรหรือคลาสที่คุณวางไว้ภายใน:

import anything

print(anything.myvar)

หรือเช่นนี้

from anything import myvar

print(myvar)

โครงสร้างโฟลเดอร์ของฉันคือ และutils\__init__.py utils\myfile.py(Utils มีไฟล์ทั้งสอง) from utils.myfile import myMethodนี่คือวิธีที่ฉันพยายามที่จะนำเข้า ModuleNotFoundError: No module named 'utils'แต่ฉันได้รับ มีอะไรผิดปกติ? PS: ฉันใช้Djangoและพยายามที่จะนำเข้าviews.pyซึ่งอยู่ในระดับเดียวกันกับutilsโฟลเดอร์
Jagruti

เป็นไปได้ที่จะใช้พา ธ สัมบูรณ์เมื่ออิมพอร์ตโมดูลและรันโปรแกรมของคุณด้วยPYTHONPATH=. python path/to/program.py
nurettin

21

ไดเร็กทอรี lib ของคุณมี__init__.pyไฟล์หรือไม่?

Python ใช้__init__.pyเพื่อกำหนดว่าไดเรกทอรีเป็นโมดูลหรือไม่


16

ลองimport .lib.BoxTimeดู สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการอ่านญาตินำเข้าในPEP 328


2
ฉันไม่คิดว่าฉันเคยเห็นไวยากรณ์ที่ใช้มาก่อน มีเหตุผลที่ดี (ไม่) ในการใช้วิธีนี้หรือไม่
tgray

2
ทำไมคำตอบนี้ถึงไม่ได้ แน่นอนว่าถ้าคุณต้องการทำสิ่งต่าง ๆ ในแพ็คเกจทั้งหมดคุณควรทำอย่างนั้น แต่นั่นไม่ใช่สิ่งที่เป็นคำถามเดิม
Travis Griggs

สิ่งนี้ให้ฉัน: ValueError: พยายามนำเข้าที่สัมพันธ์กันในแพคเกจที่ไม่ใช่
Alex

5
ใช้งานได้เฉพาะเมื่อไฟล์ที่คุณนำเข้ามานั้นเป็นส่วนหนึ่งของแพ็คเกจ หากไม่คุณจะได้รับข้อผิดพลาดที่ @Alex ชี้ให้เห็น
Jonathon Reinhart

8

ฉันทำซึ่งโดยทั่วไปครอบคลุมทุกกรณี (ให้แน่ใจว่าคุณมี__init__.pyในญาติ / path / to / your / lib / โฟลเดอร์):

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/your/lib/folder")
import someFileNameWhichIsInTheFolder
...
somefile.foo()


ตัวอย่าง:
คุณมีในโฟลเดอร์โครงการของคุณ:

/root/myproject/app.py

คุณมีในโฟลเดอร์โครงการอื่น:

/root/anotherproject/utils.py
/root/anotherproject/__init__.py

คุณต้องการใช้ /root/anotherproject/utils.pyและเรียกใช้ฟังก์ชัน foo ซึ่งอยู่ในนั้น

ดังนั้นคุณเขียนใน app.py:

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../anotherproject")
import utils

utils.foo()

2
หากคุณใช้os.pathคุณอาจต้องการใช้os.path.join((os.path.dirname(os.path.realpath(__file__)),'..','anotherproject')แทนการเข้ารหัส '/' ในการต่อข้อมูลพา ธ
cowbert

ทำไมคุณไม่ทำ"../anotherproject"โดยไม่ต้องทำos.path.dirname()?
Moshe Rabaev

@MosheRabaev - เป็นวิธีปฏิบัติที่ดีในการใช้ฟังก์ชั่น os.path ในกรณีที่บิด "../anotherproject" และย้ายรหัสไปยัง Windows OS รหัสจะแตก! os.path utils รู้วิธีส่งคืนพา ธ ที่ถูกต้องเนื่องจากระบบปฏิบัติการใช้งานโค้ด สำหรับข้อมูลเพิ่มเติมdocs.python.org/2/library/os.path.html
Mercury

@MosheRabaev และถ้าคุณใช้ ".. " โดยไม่ต้องใช้dirname(realpath(__file__))มันจะคำนวณพา ธ ที่สัมพันธ์กับไดเรกทอรีการทำงานปัจจุบันของคุณเมื่อคุณเรียกใช้สคริปต์ไม่ใช่สัมพันธ์กับตำแหน่งที่สคริปต์ใช้
TJ Ellis

5

สร้างไฟล์เปล่า __init__.pyในไดเรกทอรีย่อย / lib และเพิ่มที่จุดเริ่มต้นของรหัสหลัก

from __future__ import absolute_import 

แล้วก็

import lib.BoxTime as BT
...
BT.bt_function()

หรือดีกว่า

from lib.BoxTime import bt_function
...
bt_function()

0

นอกเหนือจากคำตอบเหล่านี้

หากคุณต้องการนำเข้าไฟล์ทั้งหมดจากไดเรกทอรีย่อยทั้งหมดคุณสามารถเพิ่มสิ่งนี้ลงในรูทของไฟล์ของคุณ

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

จากนั้นคุณสามารถนำเข้าไฟล์จากไดเรกทอรีย่อยเช่นเดียวกับไฟล์เหล่านี้อยู่ในไดเรกทอรีปัจจุบัน

ตัวอย่างการทำงาน

หากฉันมีไดเรกทอรีต่อไปนี้พร้อมไดเรกทอรีย่อยในโครงการของฉัน ...

.
├── a.py
├── b.py
├── c.py
├── subdirectory_a
   ├── d.py
   └── e.py
├── subdirectory_b
   └── f.py
├── subdirectory_c
   └── g.py
└── subdirectory_d
    └── h.py

ฉันสามารถใส่รหัสต่อไปนี้ไว้ในa.pyไฟล์ของฉัน

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

# And then you can import files just as if these files are inside the current directory

import b
import c
import d
import e
import f
import g
import h

กล่าวอีกนัยหนึ่งรหัสนี้จะเป็นนามธรรมจากไดเรกทอรีที่ไฟล์มาจาก


-1

/project/tester.py

/project/lib/BoxTime.py

สร้างไฟล์เปล่า__init__.pyลงบรรทัดจนกว่าคุณจะไปถึงไฟล์

/project/lib/somefolder/BoxTime.py

#lib- ความต้องการมีสองรายการหนึ่ง__init__.pyและไดเรกทอรีชื่อ somefolder #somefolderมีสองรายการboxtime.pyและ__init__.py


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