ฉันต้องการรับช่วงจากคลาสในไฟล์ที่อยู่ในไดเรกทอรีเหนือคลาสปัจจุบัน
เป็นไปได้หรือไม่ที่จะนำเข้าไฟล์นั้น?
ฉันต้องการรับช่วงจากคลาสในไฟล์ที่อยู่ในไดเรกทอรีเหนือคลาสปัจจุบัน
เป็นไปได้หรือไม่ที่จะนำเข้าไฟล์นั้น?
คำตอบ:
from ..subpkg2 import mod
สำหรับเอกสาร Python: เมื่ออยู่ในลำดับชั้นของแพคเกจให้ใช้จุดสองจุดดังที่doc statement importกล่าวว่า:
เมื่อระบุโมดูลที่จะนำเข้าคุณไม่จำเป็นต้องระบุชื่อแบบสัมบูรณ์ของโมดูล เมื่อโมดูลหรือแพ็กเกจมีอยู่ในแพคเกจอื่นมันเป็นไปได้ที่จะทำให้การนำเข้าที่เกี่ยวข้องภายในแพคเกจด้านบนเดียวกันโดยไม่ต้องพูดถึงชื่อแพคเกจ โดยการใช้จุดนำในโมดูลหรือแพ็กเกจที่
from
ระบุหลังจากที่คุณสามารถระบุระดับความสูงของการสำรวจลำดับชั้นของแพ็คเกจปัจจุบันโดยไม่ต้องระบุชื่อที่แน่นอน จุดนำหนึ่งจุดหมายถึงแพคเกจปัจจุบันที่มีโมดูลที่นำเข้าอยู่ สองจุดหมายถึงระดับหนึ่งแพคเกจ จุดสามจุดขึ้นสองระดับ ฯลฯ ดังนั้นหากคุณดำเนินการfrom . import mod
จากโมดูลในส่วนแพคเกจแล้วคุณจะจบลงด้วยการนำเข้าpkg
pkg.mod
หากคุณดำเนินการfrom ..subpkg2 import mod
จากภายในpkg.subpkg1
คุณจะนำเข้าpkg.subpkg2.mod
. ข้อกำหนดสำหรับการนำเข้าญาติอยู่ภายในPEP 328
PEP 328เกี่ยวข้องกับการนำเข้าแบบสัมบูรณ์ / สัมพัทธ์
import sys
sys.path.append("..") # Adds higher directory to python modules path.
คำตอบของ @ gimel นั้นถูกต้องหากคุณสามารถรับประกันลำดับชั้นของแพ็คเกจที่เขากล่าวถึง หากคุณไม่สามารถ - ถ้าต้องการที่แท้จริงของคุณในขณะที่คุณแสดงมันผูกเฉพาะไดเรกทอรีและไม่มีความสัมพันธ์ใด ๆ ที่จำเป็นบรรจุภัณฑ์ - แล้วคุณจะต้องทำงานในการ__file__
ที่จะหาไดเรกทอรีแม่ (คู่ของos.path.dirname
สายจะทำ; -) แล้ว (ถ้าไดเรกทอรีที่ไม่ได้อยู่ในsys.path
) ย่อหน้าชั่วคราวแทรกกล่าวว่าผบที่เริ่มต้นมากsys.path
, __import__
ลบกล่าวว่าผบอีกครั้ง - งานยุ่งแน่นอน แต่ "เมื่อคุณต้องคุณต้อง" (และมุ่งมั่นที่จะ Pyhon อย่าหยุดโปรแกรมเมอร์จากการทำสิ่งที่ต้องทำเช่นเดียวกับมาตรฐาน ISO C ที่ระบุไว้ในส่วน "วิญญาณแห่ง C" ในคำนำ! -)
นี่คือตัวอย่างที่อาจเหมาะกับคุณ:
import sys
import os.path
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
import module_in_parent_dir
sys.path
ดังนั้นจึงทำให้โมดูลเดียวกันพร้อมใช้งานภายใต้ชื่อที่แตกต่างกันและข้อบกพร่องที่เกี่ยวข้องทั้งหมด autopath.py ใน pypy หรือ _preamble.py ใน twisted แก้ปัญหาโดยใช้เกณฑ์การค้นหาที่ระบุแพ็คเกจระดับบนสุดขณะสำรวจไดเรกทอรีขึ้นด้านบน
sys.path.remove(pathYouJustAdded)
หลังจากการนำเข้าที่คุณต้องการเพื่อไม่ให้รักษาเส้นทางใหม่นี้ไว้
คำนำ: ฉันเขียนคำตอบก่อนหน้านี้อย่างมีนัยสำคัญด้วยความหวังว่าจะช่วยให้ผู้คนเข้าสู่ระบบนิเวศของงูใหญ่และหวังว่าจะทำให้ทุกคนประสบความสำเร็จในการเปลี่ยนแปลงที่ดีที่สุดด้วยระบบการนำเข้าของงูใหญ่
สิ่งนี้จะครอบคลุมการนำเข้าที่เกี่ยวข้องภายในแพ็คเกจซึ่งฉันคิดว่าเป็นกรณีที่น่าจะเป็นไปได้มากที่สุดสำหรับคำถามของ OP
นี่คือเหตุผลที่เราเขียนimport foo
เพื่อโหลดโมดูล "foo" จากรูทเนมสเปซแทนที่จะเขียน:
foo = dict(); # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh: # please avoid doing this
exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo) # please avoid doing this
นี่คือเหตุผลที่เราสามารถฝัง python ในสภาพแวดล้อมที่ไม่มีระบบไฟล์ defacto โดยไม่ต้องมี virtual virtual เช่น Jython
การแยกออกจากระบบไฟล์ช่วยให้การนำเข้ามีความยืดหยุ่นการออกแบบนี้ทำให้สามารถนำเข้าสิ่งต่าง ๆ เช่นการนำเข้าจากไฟล์เก็บถาวร / ไฟล์ซิปนำเข้าซิงเกิลตันการแคช bytecode ส่วนขยาย cffi หรือแม้แต่การกำหนดนิยามรหัสระยะไกล
ดังนั้นหากการนำเข้าไม่ได้เชื่อมโยงกับระบบไฟล์ "หนึ่งไดเรกทอรีขึ้น" หมายความว่าอย่างไร เราต้องเลือกฮิวริสติกบางอย่าง แต่เราสามารถทำได้ตัวอย่างเช่นเมื่อทำงานภายในแพ็คเกจฮิวริสติกบางตัวได้ถูกกำหนดไว้แล้วซึ่งทำให้การนำเข้าแบบสัมพัทธ์เหมือน.foo
และ..foo
ทำงานภายในแพ็คเกจเดียวกัน เย็น!
หากคุณต้องการจับคู่รูปแบบการโหลดซอร์สโค้ดของคุณกับระบบไฟล์อย่างจริงใจคุณสามารถทำได้ คุณจะต้องเลือกฮิวริสติกของคุณเองและใช้เครื่องจักรนำเข้าบางชนิดฉันขอแนะนำimportlib
ตัวอย่างการนำเข้าของไพ ธ อนมีลักษณะดังนี้:
import importlib.util
import sys
# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'
foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)
foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton
มีโครงการตัวอย่างที่ยอดเยี่ยมให้บริการอย่างเป็นทางการที่นี่: https://github.com/pypa/sampleproject
แพคเกจหลามคือชุดของข้อมูลเกี่ยวกับซอร์สโค้ดของคุณซึ่งสามารถแจ้งเครื่องมืออื่น ๆ ถึงวิธีการคัดลอกซอร์สโค้ดของคุณไปยังคอมพิวเตอร์เครื่องอื่นและวิธีรวมซอร์สโค้ดของคุณลงในพา ธ ของระบบนั้นเพื่อให้import foo
ทำงานกับคอมพิวเตอร์เครื่องอื่น ๆ ระบบปฏิบัติการโฮสต์ ฯลฯ )
ให้มีชื่อแพ็กเกจfoo
ในบางไดเร็กทอรี (ควรเป็นไดเร็กทอรีว่าง)
some_directory/
foo.py # `if __name__ == "__main__":` lives here
การตั้งค่าของฉันคือการสร้างsetup.py
เป็นพี่น้องfoo.py
เพราะมันทำให้การเขียนไฟล์ setup.py ง่ายขึ้น แต่คุณสามารถเขียนการกำหนดค่าเพื่อเปลี่ยน / เปลี่ยนเส้นทางทุกสิ่งที่ setuptools ทำได้ตามค่าเริ่มต้นหากคุณต้องการ ตัวอย่างเช่นการวางfoo.py
ภายใต้ไดเรกทอรี "src /" ค่อนข้างเป็นที่นิยมไม่ครอบคลุมที่นี่
some_directory/
foo.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
py_modules=['foo'],
)
.
python3 -m pip install --editable ./ # or path/to/some_directory/
"แก้ไขได้" aka -e
จะเปลี่ยนเส้นทางเครื่องจักรนำเข้าอีกครั้งเพื่อโหลดไฟล์ต้นฉบับในไดเรกทอรีนี้แทนที่จะคัดลอกไฟล์ที่แน่นอนปัจจุบันไปยังไลบรารีของสภาพแวดล้อมการติดตั้ง สิ่งนี้อาจทำให้เกิดความแตกต่างทางพฤติกรรมในเครื่องของนักพัฒนาตรวจสอบให้แน่ใจว่าได้ทดสอบโค้ดของคุณแล้ว! มีเครื่องมืออื่นที่ไม่ใช่ pip แต่ฉันขอแนะนำ pip ให้เป็นคนเกริ่นนำ :)
ฉันยังต้องการสร้างfoo
"แพ็คเกจ" (ไดเรกทอรีที่มี__init__.py
) แทนโมดูล (ไฟล์ ".py" เดียว) ทั้ง "แพ็คเกจ" และ "โมดูล" สามารถโหลดลงในรูทเนมสเปซโมดูลอนุญาตให้เนมสเปซซ้อน ซึ่งมีประโยชน์ถ้าเราต้องการนำเข้า "สัมพันธ์หนึ่งไดเรกทอรีขึ้น"
some_directory/
foo/
__init__.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
)
ฉันยังต้องการที่จะทำให้การfoo/__main__.py
นี้จะช่วยให้งูหลามที่จะดำเนินการแพคเกจเป็นโมดูลเช่นpython3 -m foo
จะดำเนินการเป็นfoo/__main__.py
__main__
some_directory/
foo/
__init__.py
__main__.py # `if __name__ == "__main__":` lives here, `def main():` too!
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
...
entry_points={
'console_scripts': [
# "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
'foo=foo.__main__:main',
]
},
)
ให้เนื้อมีส่วนร่วมกับโมดูลอื่น ๆ : โดยทั่วไปคุณสามารถมีโครงสร้างไดเรกทอรีดังนี้:
some_directory/
bar.py # `import bar`
foo/
__init__.py # `import foo`
__main__.py
baz.py # `import foo.baz
spam/
__init__.py # `import foo.spam`
eggs.py # `import foo.spam.eggs`
setup.py
setup.py
ตามปกติเก็บข้อมูลเมตาดาต้าเกี่ยวกับซอร์สโค้ดภายในเช่น:
foo
แม้ว่าการแทนที่ขีดล่างสำหรับยัติภังค์เป็นที่นิยมpython ./setup.py test
มันกว้างขวางมากมันสามารถรวบรวมคอมไพล์ได้ทันทีหากโมดูลซอร์สถูกติดตั้งบนเครื่องพัฒนา สำหรับตัวอย่างทุกวันฉันขอแนะนำsetup.py ของ PYPA Sample Repository
หากคุณกำลังปล่อยสิ่งประดิษฐ์สร้างเช่นสำเนาของรหัสที่มีวัตถุประสงค์เพื่อเรียกใช้คอมพิวเตอร์เกือบเหมือนกันไฟล์ requirements.txt เป็นวิธีที่นิยมในการถ่ายภาพข้อมูลการพึ่งพาที่แน่นอนโดยที่ "install_requires" เป็นวิธีที่ดีในการจับภาพขั้นต่ำและ รุ่นที่รองรับสูงสุด อย่างไรก็ตามเนื่องจากเครื่องเป้าหมายเกือบเหมือนกันอยู่แล้วฉันขอแนะนำให้สร้าง tarball ของคำนำหน้าหลามทั้งหมด นี่อาจเป็นเรื่องยุ่งยากและละเอียดเกินไปที่จะเข้าไปที่นี่ ตรวจสอบpip install
's --target
ตัวเลือกหรือ virtualenv aka venv สำหรับโอกาสในการขาย
กลับไปที่ตัวอย่าง
จาก foo / spam / eggs.py หากเราต้องการรหัสจาก foo / baz เราสามารถขอได้จาก namespace ที่แน่นอน:
import foo.baz
หากเราต้องการสำรองความสามารถในการย้ายไข่ไปสู่ไดเรกทอรีอื่น ๆ ในอนาคตด้วยbaz
การใช้งานแบบสัมพัทธ์อื่น ๆเราสามารถใช้การนำเข้าแบบสัมพัทธ์เช่น:
import ..baz
ในการโหลดรหัสไพ ธ อนได้อย่างน่าเชื่อถือให้มีรหัสนั้นในโมดูลและโมดูลนั้นติดตั้งในห้องสมุดของงูใหญ่
โมดูลที่ติดตั้งสามารถโหลดได้จากเนมสเปซระดับบนด้วยเสมอ import <name>
มีโครงการตัวอย่างที่ยอดเยี่ยมให้บริการอย่างเป็นทางการที่นี่: https://github.com/pypa/sampleproject
โดยทั่วไปคุณสามารถมีโครงสร้างไดเรกทอรีดังนี้:
the_foo_project/
setup.py
bar.py # `import bar`
foo/
__init__.py # `import foo`
baz.py # `import foo.baz`
faz/ # `import foo.faz`
__init__.py
daz.py # `import foo.faz.daz` ... etc.
.
ให้แน่ใจว่าการประกาศของคุณsetuptools.setup()
ในsetup.py
,
ตัวอย่างเป็นทางการ: https://github.com/pypa/sampleproject/blob/master/setup.py
ในกรณีของเราเราอาจต้องการส่งออกbar.py
และfoo/__init__.py
ตัวอย่างสั้น ๆ ของฉัน:
#!/usr/bin/env python3
import setuptools
setuptools.setup(
...
py_modules=['bar'],
packages=['foo'],
...
entry_points={},
# Note, any changes to your setup.py, like adding to `packages`, or
# changing `entry_points` will require the module to be reinstalled;
# `python3 -m pip install --upgrade --editable ./the_foo_project
)
.
ตอนนี้เราสามารถติดตั้งโมดูลของเราลงในห้องสมุดหลาม ด้วย pip คุณสามารถติดตั้งthe_foo_project
ในไลบรารี python ของคุณในโหมดแก้ไขเพื่อให้เราสามารถทำงานในเวลาจริง
python3 -m pip install --editable=./the_foo_project
# if you get a permission error, you can always use
# `pip ... --user` to install in your user python library
.
ตอนนี้จากบริบทหลามใด ๆ เราสามารถโหลด py_modules และแพ็คเกจที่ใช้ร่วมกันของเรา
#!/usr/bin/env python3
import bar
import foo
print(dir(bar))
print(dir(foo))
pip install --edit foo
เกือบตลอดเวลาใน virtualenv ฉันแทบไม่เคยเขียนโมดูลที่ไม่ได้ตั้งใจจะติดตั้ง ถ้าฉันเข้าใจผิดบางอย่างฉันก็อยากจะรู้
editable
ลิงก์ไข่และโมดูลหลามที่ติดตั้งนั้นไม่เหมือนกันทุกประการ ตัวอย่างเช่นการเพิ่มเนมสเปซใหม่ไปยังโมดูลที่แก้ไขได้จะพบได้ในการค้นหาพา ธ ของคุณ แต่หากไม่ได้เอ็กซ์พอร์ตในไฟล์ setup.py จะไม่ถูกบรรจุ / ติดตั้ง! ทดสอบกรณีการใช้ของคุณ :)