เส้นทางสัมพัทธ์ใน Python


245

ฉันกำลังสร้างสคริปต์ตัวช่วยอย่างง่ายสำหรับงานที่จะคัดลอกไฟล์เทมเพลตสองไฟล์ในฐานรหัสของเราไปยังไดเรกทอรีปัจจุบัน อย่างไรก็ตามฉันไม่มีเส้นทางที่แน่นอนไปยังไดเรกทอรีที่เก็บแม่แบบ ฉันมีเส้นทางสัมพัทธ์จากสคริปต์ แต่เมื่อฉันเรียกสคริปต์มันถือว่าเป็นเส้นทางที่สัมพันธ์กับไดเรกทอรีการทำงานปัจจุบัน มีวิธีระบุว่า URL สัมพัทธ์นี้มาจากตำแหน่งของสคริปต์หรือไม่


คำตอบ:


325

ในไฟล์ที่มีสคริปต์คุณต้องการทำสิ่งนี้:

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

นี่จะให้เส้นทางที่แน่นอนไปยังไฟล์ที่คุณกำลังมองหา โปรดทราบว่าหากคุณใช้ setuptools คุณควรใช้API ทรัพยากรของแพ็คเกจแทน

UPDATE : ฉันตอบกลับความคิดเห็นที่นี่เพื่อให้ฉันสามารถวางตัวอย่างรหัส :-)

ฉันคิดถูกต้องว่า__file__ไม่สามารถใช้งานได้ตลอดเวลา (เช่นเมื่อคุณเรียกใช้ไฟล์โดยตรงแทนที่จะนำเข้า)?

ฉันสมมติว่าคุณหมายถึง__main__สคริปต์เมื่อคุณพูดถึงการเรียกใช้ไฟล์โดยตรง ถ้าเป็นเช่นนั้นมันไม่ได้เกิดขึ้นในระบบของฉัน (python 2.5.1 บน OS X 10.5.7):

#foo.py
import os
print os.getcwd()
print __file__

#in the interactive interpreter
>>> import foo
/Users/jason
foo.py

#and finally, at the shell:
~ % python foo.py
/Users/jason
foo.py

อย่างไรก็ตามฉันรู้ว่ามีนิสัยใจคอกับ__file__ส่วนขยาย C ตัวอย่างเช่นฉันสามารถทำได้บน Mac ของฉัน:

>>> import collections #note that collections is a C extension in Python 2.5
>>> collections.__file__
'/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-
dynload/collections.so'

อย่างไรก็ตามสิ่งนี้ทำให้เกิดข้อยกเว้นในเครื่อง Windows ของฉัน


1
ฉันคิดถูกต้องว่าไฟล์นั้นไม่สามารถใช้งานได้เสมอ (เช่นเมื่อคุณเรียกใช้ไฟล์โดยตรงแทนที่จะนำเข้า)?
สตีเฟ่นเอ็ดมันด์

@Stephen Edmonds ฉันใช้ไฟล์ที่ฉันเรียกใช้แทนที่จะนำเข้าและใช้งานได้ดี
277291 baudtack

22
หมายเหตุคุณควรใช้ os.path.join เพื่อความสะดวกในการพกพา:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
ฟอร์ด

22
os.path.dirname(__file__)สามารถให้สตริงว่างใช้os.path.dirname(os.path.abspath(__file__))แทน
Dmitry Trofimov

14
มันเป็นเรื่องเล็กน้อย แต่โปรดอย่าใช้ dir เป็นชื่อตัวแปรเนื่องจากเป็น builtin
เดวิด

63

คุณต้องการos.path.realpath(ตัวอย่างด้านล่างเพิ่มไดเรกทอรีแม่สู่เส้นทางของคุณ)

import sys,os
sys.path.append(os.path.realpath('..'))

2
os.path.dirname(__file__)ให้สตริงที่ว่าง มันทำงานได้อย่างสมบูรณ์แบบ
Darragh Enright

3
ดูเหมือนว่าจะให้พาเรนต์ของไดเรกทอรีที่เรียกใช้สคริปต์ไม่ใช่ตำแหน่งของสคริปต์
Coquelicot

10
os.path.realpath('..')จะช่วยให้คุณไดเรกทอรีแม่ของdir ทำงานปัจจุบัน นั่นไม่ใช่สิ่งที่คุณต้องการ
Martijn Pieters

1
@DarraghEnright: เกิดขึ้นในสภาพแวดล้อมการบรรจุ Python-script-to-exe เท่านั้น นั่นเป็นหนึ่งในข้อยกเว้นที่หายากซึ่งการพึ่งพา dir ที่ทำงานในปัจจุบันจะเป็นทางเลือก
Martijn Pieters

52

ตามที่ระบุไว้ในคำตอบที่ยอมรับ

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, '/relative/path/to/file/you/want')

ฉันแค่อยากจะเพิ่ม

สตริงหลังไม่สามารถเริ่มต้นด้วยแบ็กสแลชได้

มันควรจะเป็นสิ่งที่ชอบ

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, 'relative','path','to','file','you','want')

คำตอบที่ยอมรับอาจทำให้เข้าใจผิดในบางกรณีโปรดดูที่ ลิงก์นี้สำหรับรายละเอียด


4
ใช่ใช้os.path.joinดีกว่าเพราะเชื่อมต่อกับตัวคั่นเฉพาะระบบปฏิบัติการ
Farshid T

'/relative/path...'ไม่ใช่เส้นทางสัมพัทธ์ เป็นความตั้งใจหรือไม่?
สตีฟ

os.path.join()คำตอบนี้ล้าสมัยตอนนี้เป็นคำตอบด้านบนได้รับการแก้ไขจะใช้เส้นทางสัมพันธ์ที่เหมาะสมในการ สิ่งที่เหลืออยู่คือการกำหนดค่าตามความชอบเพื่อใช้สตริงแยกสำหรับแต่ละองค์ประกอบพา ธ บนฮาร์ดโค้ดตัวแยกพา ธ
Martijn Pieters

@MartijnPieters ใช่คำตอบยอดนิยมได้รับการแก้ไขเพื่อให้ตรงกับส่วนนี้ แต่สตริงที่แยกต่างหากไม่ใช่การตั้งค่า - แยก stings เช่นนี้ทำให้ระบบปฏิบัติการอิสระ
jshrimp29

26

ตอนนี้เป็นปี 2018 และ Python ได้พัฒนาไปเป็น__future__เวลานานแล้ว ดังนั้นวิธีการเกี่ยวกับการใช้ที่น่าตื่นตาตื่นใจpathlibมากับงูหลาม 3.4 ในการทํางานแทนการดิ้นรนกับos, os.path,glob , shutilฯลฯ

ดังนั้นเราจึงมี 3 เส้นทางที่นี่ (อาจซ้ำซ้อน):

  • mod_path: ซึ่งเป็นเส้นทางของสคริปต์ตัวช่วยอย่างง่าย
  • src_path: ซึ่งมีไฟล์เทมเพลตสองไฟล์ที่รอการคัดลอก
  • cwd: ไดเรกทอรีปัจจุบันปลายทางของไฟล์เทมเพลตเหล่านั้น

และปัญหาที่เกิดขึ้นคือเราไม่ได้มีเส้นทางแบบเต็มของsrc_pathเพียงรู้ว่าทางญาติของมันmod_pathไป

ตอนนี้เรามาแก้ปัญหาด้วยสิ่งมหัศจรรย์pathlib:

# Hope you don't be imprisoned by legacy Python code :)
from pathlib import Path

# `cwd`: current directory is straightforward
cwd = Path.cwd()

# `mod_path`: According to the accepted answer and combine with future power
# if we are in the `helper_script.py`
mod_path = Path(__file__).parent
# OR if we are `import helper_script`
mod_path = Path(helper_script.__file__).parent

# `src_path`: with the future power, it's just so straightforward
relative_path_1 = 'same/parent/with/helper/script/'
relative_path_2 = '../../or/any/level/up/'
src_path_1 = (mod_path / relative_path_1).resolve()
src_path_2 = (mod_path / relative_path_2).resolve()

ในอนาคตมันง่ายเพียงแค่นั้น : D


นอกจากนี้เราสามารถเลือกและตรวจสอบและคัดลอก / ย้ายไฟล์เทมเพลตเหล่านั้นด้วยpathlib:

if src_path != cwd:
    # When we have different types of files in the `src_path`
    for template_path in src_path.glob('*.ini'):
        fname = template_path.name
        target = cwd / fname
        if not target.exists():
            # This is the COPY action
            with target.open(mode='wb') as fd:
                fd.write(template_path.read_bytes())
            # If we want MOVE action, we could use:
            # template_path.replace(target)

14

พิจารณารหัสของฉัน:

import os


def readFile(filename):
    filehandle = open(filename)
    print filehandle.read()
    filehandle.close()



fileDir = os.path.dirname(os.path.realpath('__file__'))
print fileDir

#For accessing the file in the same folder
filename = "same.txt"
readFile(filename)

#For accessing the file in a folder contained in the current folder
filename = os.path.join(fileDir, 'Folder1.1/same.txt')
readFile(filename)

#For accessing the file in the parent folder of the current folder
filename = os.path.join(fileDir, '../same.txt')
readFile(filename)

#For accessing the file inside a sibling folder.
filename = os.path.join(fileDir, '../Folder2/same.txt')
filename = os.path.abspath(os.path.realpath(filename))
print filename
readFile(filename)

เมื่อฉันเรียกใช้สิ่งนี้ใน windows ฉันได้รับข้อผิดพลาด: FileNotFoundError: [Errno 2] ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว: '<path>' โดยที่ <path> มีเซ็กเมนต์เส้นทางที่ถูกต้อง แต่ใช้ \\ สำหรับตัวคั่น
lonstar

11

ดู sys.path เมื่อเริ่มต้นโปรแกรมเมื่อรายการเริ่มต้นของรายการนี้เส้นทาง [0] เป็นไดเรกทอรีที่มีสคริปต์ที่ใช้ในการเรียก Python interpreter

ใช้พา ธ นี้เป็นโฟลเดอร์รูทที่คุณใช้พา ธ สัมพัทธ์

>>> import sys
>>> import os.path
>>> sys.path[0]
'C:\\Python25\\Lib\\idlelib'
>>> os.path.relpath(sys.path[0], "path_to_libs") # if you have python 2.6
>>> os.path.join(sys.path[0], "path_to_libs")
'C:\\Python25\\Lib\\idlelib\\path_to_libs'

3
ไม่จำเป็นต้องเป็นเรื่องจริง โดยปกติ sys.path [0] เป็นสตริงว่างหรือจุดซึ่งเป็นเส้นทางสัมพัทธ์กับไดเรกทอรีปัจจุบัน ถ้าคุณต้องการไดเรกทอรีปัจจุบันใช้ os.getcwd
เจสันเบเกอร์

โปสเตอร์ต้นฉบับแสดงความคิดเห็นว่าไดเรกทอรีการทำงานปัจจุบันเป็นตำแหน่งที่ไม่ถูกต้องในการอ้างอิงเส้นทางที่สัมพันธ์กัน คุณพูดถูกแล้วว่า sys.path [0] นั้นไม่ถูกต้องเสมอไป
Tom Leys

ไม่sys.path[0]ไม่ได้ถูกตั้งค่าเป็นไดเรกทอรีหลักเสมอไป รหัส Python สามารถเรียกใช้ด้วย-cหรือ-mหรือผ่านล่ามที่ฝังตัวซึ่งsys.path[0]เป็นจุดที่ตั้งค่าเป็นสิ่งที่แตกต่างกันโดยสิ้นเชิง
Martijn Pieters

6

แทนที่จะใช้

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

เช่นเดียวกับในคำตอบที่ยอมรับจะมีประสิทธิภาพมากกว่าเมื่อใช้:

import inspect
import os
dirname = os.path.dirname(os.path.abspath(inspect.stack()[0][1]))
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

เนื่องจากการใช้ __file__ จะส่งคืนไฟล์ที่โมดูลถูกโหลดถ้ามันถูกโหลดจากไฟล์ดังนั้นหากไฟล์ที่มีสคริปต์ถูกเรียกใช้จากที่อื่นไดเร็กทอรีที่ส่งคืนจะไม่ถูกต้อง

คำตอบเหล่านี้ให้รายละเอียดเพิ่มเติม: https://stackoverflow.com/a/31867043/5542253และhttps://stackoverflow.com/a/50502/5542253


5
inspect.stack()ฟังก์ชั่นการโทรที่มีราคาแพง มันดึงข้อมูลสำหรับสแต็กเฟรมทั้งหมดซึ่งคุณจะละทิ้งและรับเฉพาะเฟรมที่ดีที่สุดเท่านั้น มันเป็นพื้นเรียกinspect.getfile()วัตถุโมดูลซึ่งเพิ่งกลับmodule.__file__มา คุณใช้งานได้ดีกว่า__file__มาก
Martijn Pieters

4

สวัสดีก่อนอื่นคุณควรเข้าใจฟังก์ชั่นos.path.abspath (เส้นทาง)และos.path.relpath (เส้นทาง)

ในระยะสั้นos.path.abspath (เส้นทาง)ทำให้เส้นทางสัมพัทธ์กับเส้นทางที่แน่นอนเส้นทางที่แน่นอนและถ้าเส้นทางที่ให้มานั้นเป็นเส้นทางสัมบูรณ์แล้วฟังก์ชันจะส่งกลับเส้นทางเดียวกัน

ในทำนองเดียวกันos.path.relpath (เส้นทาง)ทำให้เส้นทางที่แน่นอนไปทางญาติ และหากเส้นทางที่ระบุเป็นเส้นทางสัมพัทธ์แล้วฟังก์ชันจะส่งกลับเส้นทางเดียวกัน

ตัวอย่างด้านล่างสามารถให้คุณเข้าใจแนวคิดด้านบนได้อย่างถูกต้อง :

สมมติว่าฉันมีไฟล์input_file_list.txtซึ่งมีรายการไฟล์อินพุตที่ต้องดำเนินการโดยสคริปต์ python ของฉัน

D: \ เข้มข้น \ input1.dic

D: \ เข้มข้น \ input2.dic

D: \ Copyioconc \ input_file_list.txt

ถ้าคุณดูด้านบนโครงสร้างโฟลเดอร์input_file_list.txtอยู่ในCopyofconcโฟลเดอร์และไฟล์ที่ต้องดำเนินการโดยสคริปต์หลามที่มีอยู่ในความเข้มข้นโฟลเดอร์

แต่เนื้อหาของไฟล์input_file_list.txtดังแสดงด้านล่าง:

.. \ เข้มข้น \ input1.dic

.. \ เข้มข้น \ input2.dic

และสคริปต์ python ของฉันมีอยู่ในไดรฟ์D:

และพา ธ สัมพัทธ์ที่ให้ไว้ในไฟล์input_file_list.txtนั้นสัมพันธ์กับพา ธ ของไฟล์input_file_list.txt

ดังนั้นเมื่อสคริปต์ python จะดำเนินการไดเรกทอรีการทำงานปัจจุบัน (ใช้os.getcwd ()เพื่อรับเส้นทาง)

ในฐานะที่เป็นทางญาติของฉันคือเทียบกับinput_file_list.txtที่เป็น"D: \ Copyofconc"ผมต้องเปลี่ยนไดเรกทอรีการทำงานปัจจุบันที่จะ"D: \ Copyofconc"

ดังนั้นฉันต้องใช้os.chdir ('D: \ Copyofconc')ดังนั้นไดเรกทอรีการทำงานปัจจุบันจะต้องเป็น"D: \ Copyofconc"Copyofconc"

ตอนนี้เพื่อรับไฟล์input1.dicและinput2.dicฉันจะอ่านบรรทัด ".. \ conc \ input1.dic" จากนั้นจะใช้คำสั่ง

input1_path = os.path.abspath ('.. \ conc \ input1.dic') (เพื่อเปลี่ยนพา ธ สัมพัทธ์เป็นพา ธ สัมบูรณ์นี่เป็นไดเรกทอรีการทำงานปัจจุบันคือ "D: \ Copyofconc", ไฟล์ ". \ conc \ input1 dic "จะสามารถเข้าถึงได้เมื่อเทียบกับ" D: \ Copyofconc ")

ดังนั้นinput1_pathจะเป็น "D: \ conc \ input1.dic"


4

รหัสนี้จะคืนค่าพา ธ สัมบูรณ์ไปยังสคริปต์หลัก

import os
def whereAmI():
    return os.path.dirname(os.path.realpath(__import__("__main__").__file__))

สิ่งนี้จะทำงานได้แม้ในโมดูล


sys.modules['__main__'].__file__แต่ของใหม่นำเข้าที่คุณต้องการใช้
Martijn Pieters

3

ทางเลือกที่เหมาะกับฉัน:

this_dir = os.path.dirname(__file__) 
filename = os.path.realpath("{0}/relative/file.path".format(this_dir))

0

sys.path.insertสิ่งที่ทำงานสำหรับฉันคือการใช้ จากนั้นฉันระบุไดเรกทอรีที่ฉันต้องการจะไป ตัวอย่างเช่นฉันต้องการเพิ่มไดเรคทอรีเดียว

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

1
สิ่งนี้ขึ้นอยู่กับไดเรกทอรีการทำงานปัจจุบันซึ่งอาจแตกต่างอย่างสิ้นเชิงจากสิ่งที่คุณต้องการ
Martijn Pieters

-2

ฉันไม่แน่ใจว่าสิ่งนี้ใช้ได้กับเวอร์ชั่นเก่าบางรุ่นหรือไม่ แต่ฉันเชื่อว่า Python 3.3 มีการสนับสนุนเส้นทางแบบเนทีฟ

ตัวอย่างเช่นรหัสต่อไปนี้ควรสร้างไฟล์ข้อความในโฟลเดอร์เดียวกันกับสคริปต์หลาม:

open("text_file_name.txt", "w+t")

(โปรดทราบว่าไม่ควรมีการฟอร์เวิร์ดหรือแบ็กสแลชตั้งแต่ต้นหากเป็นพา ธ ที่สัมพันธ์กัน)


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