วิธีอ้างถึงพา ธ สัมพัทธ์ของทรัพยากรเมื่อทำงานกับที่เก็บรหัส


188

เรากำลังทำงานกับที่เก็บรหัสซึ่งปรับใช้กับทั้ง Windows และ Linux - บางครั้งในไดเรกทอรีที่แตกต่างกัน หนึ่งในโมดูลในโครงการควรอ้างถึงหนึ่งในแหล่งข้อมูลที่ไม่ใช่ Python ในโครงการ (ไฟล์ CSV, ฯลฯ ) อย่างไร

ถ้าเราทำสิ่งที่ชอบ:

thefile=open('test.csv')

หรือ:

thefile=open('../somedirectory/test.csv')

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

สิ่งที่ฉันต้องการจะทำคือ:

path=getBasePathOfProject()+'/somedirectory/test.csv'
thefile=open(path)

เป็นไปได้ไหม?

คำตอบ:


255

ลองใช้ชื่อไฟล์ที่สัมพันธ์กับเส้นทางของไฟล์ปัจจุบัน ตัวอย่างสำหรับ './my_file':

fn = os.path.join(os.path.dirname(__file__), 'my_file')

ใน Python 3.4+ คุณสามารถใช้pathlib :

fn = pathlib.Path(__file__).parent / 'my_file'

3
ฉันคิดว่าวิธีการแก้ปัญหานี้จะทำงานเฉพาะถ้าทรัพยากรอยู่ในไดเรกทอรีเดียวกันของไฟล์หลามหรือในไดเรกทอรีย่อยของมัน คุณจะแก้ปัญหาอย่างไรเมื่อคุณมีโครงสร้างแบบต้นไม้ต่อไปนี้: / Project_Root_dir / python_files_dir / บางส่วนย่อยเพิ่มเติมที่นี่ py_file.py / resources / บางส่วนย่อยที่นี่ resource_file.csv
olamundo

1
ขออภัยต้นไม้ไฟล์ได้อ่านไม่ออกในข้อความที่ผ่านมาว่า ... สองลอง: คุณมีไฟล์ของคุณที่ /Project_Root_dir/python_files_dir/some_subdirs/py_file.py และคุณมีแฟ้มทรัพยากรของคุณใน /Project_Root_dir/resources/some_subdirs/resource_file.csv
olamundo

28
คุณควรไปที่ไดเรกทอรีหลักโดยใช้การเข้าร่วม (foo, '.. ') ดังนั้นจาก / root / python_files / module / myfile ให้ใช้ os.path.join (os.path.dirname ( __file__), '.. ', '.. ', 'resources')
c089

7
os.pardirดีกว่าเล็กน้อย'..'แม้ว่าทั้งสองจะเทียบเท่ากันทั้ง POSIX และ Windows
davidchambers

4
@cedbeu: มันเทียบเท่ากับทุกระบบที่ฉันเคยเจอและฉันคิดว่าหลามระบบทุกระบบทำงานในวันนี้ (โปรดแก้ไขให้ฉันด้วยถ้าฉันผิดที่นี่) อย่างไรก็ตามหากคุณคาดว่าจะมีการจัดวางไพ ธ อนไปยังระบบโดยใช้ตัวคั่นพา ธ อื่นในอนาคตและต้องการให้โค้ดของคุณพร้อมใช้งาน os.pardir จะพกพาได้มากกว่า ฉันจะทำให้กรณีที่โปรแกรมเมอร์ทุกคนแม้แต่คนที่ไม่เคยอ่านหลามรู้ถึงความหมายของ ".. " ในขณะที่ "os.pardir" เป็นระดับหนึ่งที่จะต้องดูในเอกสารเพื่อเป็นการส่วนตัว d ติดกับ ".. "
c089

40

หากคุณกำลังใช้เครื่องมือตั้งค่าหรือแจกจ่าย (ติดตั้ง setup.py) วิธีการ "เข้าถึง" ที่ถูกต้องในการเข้าถึงทรัพยากรที่จัดทำแพ็กเกจเหล่านี้ดูเหมือนจะใช้ package_resources

ในกรณีของคุณตัวอย่างจะเป็น

import pkg_resources
my_data = pkg_resources.resource_string(__name__, "foo.dat")

หลักสูตรใดที่อ่านทรัพยากรและข้อมูลไบนารีที่อ่านจะเป็นค่าของ my_data

หากคุณต้องการชื่อไฟล์คุณสามารถใช้

resource_filename(package_or_requirement, resource_name)

ตัวอย่าง:

resource_filename("MyPackage","foo.dat")

ข้อดีคือมันรับประกันว่าจะทำงานแม้ว่าจะมีการกระจายการเก็บถาวรเช่นไข่

ดูhttp://packages.python.org/distribute/pkg_resources.html#resourcemanager-api


3
ฉันรู้ว่านี่เป็นคำตอบเก่าวิธีที่ฉันชอบคือ (/ อาจจะ?) เพื่อใช้ pkg_resources แต่ด้วยการหายตัวไปของไข่ซิปจะมีอันตรายใด ๆ ในการใช้__file__เช่นวันเก่าดีหรือไม่?
Pykler

1
นี่เป็นแนวทางที่มั่นคง แม้ว่าการประชุมไข่จะหายไป setuptools ไม่ได้และหลายคนยังคงติดตั้ง depos กับ git repos ที่ไข่ถูกสร้างขึ้นที่รันไทม์
deepelement

18

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

การใช้พา ธ สัมบูรณ์ควรเป็นทางออกที่ดีที่สุด:

import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')

15

ฉันมักจะใช้สิ่งที่คล้ายกับสิ่งนี้:

import os
DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 'datadir'))

# if you have more paths to set, you might want to shorten this as
here = lambda x: os.path.abspath(os.path.join(os.path.dirname(__file__), x))
DATA_DIR = here('datadir') 

pathjoin = os.path.join
# ...
# later in script
for fn in os.listdir(DATA_DIR):
    f = open(pathjoin(DATA_DIR, fn))
    # ...

ตัวแปร

__file__

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

  • เส้นทางเป็นแบบสัมบูรณ์ แต่ยังคงสัมพันธ์
  • โครงการยังสามารถปรับใช้ในคอนเทนเนอร์ที่สัมพันธ์กัน

แต่คุณต้องระวังความเข้ากันได้ของแพลตฟอร์ม - Windows 'os.pathsep นั้นแตกต่างจาก UNIX


5
import os
cwd = os.getcwd()
path = os.path.join(cwd, "my_file")
f = open(path)

นอกจากนี้คุณยังพยายามที่จะทำให้ปกติของคุณโดยใช้cwd os.path.abspath(os.getcwd())ข้อมูลเพิ่มเติมที่นี่


3
กรณีการใช้งานน้อยมากซึ่งcwdเป็นเส้นทางของโมดูล แต่
cedbeu

มันไม่ทำงานภายในแพ็คเกจเพียงจาก dir เดียวกัน (หรือ dir ทำงาน) ที่กำหนดโดยสคริปต์
alexandra

สิ่งนี้จะไม่ทำงานหากผู้ใช้รันโปรแกรมโดยใช้พา ธ สัมบูรณ์จากไดเรกทอรีอื่น เช่น python3 /usr/someone/test.py
sgrpwr

2

คุณสามารถใช้บิลด์ใน__file__ตัวแปร มันมีเส้นทางของไฟล์ปัจจุบัน ฉันจะใช้ getBaseOfProject ในโมดูลในรูทของโครงการของคุณ ที่นั่นฉันจะได้ส่วนหนึ่งของเส้นทาง__file__และจะคืนสิ่งนั้น วิธีนี้สามารถใช้ได้ทุกที่ในโครงการของคุณ


0

ฉันงงที่นี่เล็กน้อย ต้องการแพ็กเกจไฟล์รีซอร์สบางไฟล์ลงในไฟล์ wheel และเข้าถึงไฟล์ ทำแพ็กเกจโดยใช้ไฟล์รายการ แต่การติดตั้ง pip ไม่ได้ติดตั้งเว้นแต่เป็นไดเรกทอรีย่อย หวังว่าภาพ sceen เหล่านี้จะช่วยได้

├── cnn_client
   ├── image_preprocessor.py
   ├── __init__.py
   ├── resources
      ├── mscoco_complete_label_map.pbtxt
      ├── retinanet_complete_label_map.pbtxt
      └── retinanet_label_map.py
   ├── tf_client.py

MANIFEST.in

recursive-include cnn_client/resources *

สร้าง weel โดยใช้ setup.py มาตรฐาน pip ติดตั้งไฟล์ล้อ หลังจากการติดตั้งตรวจสอบว่ามีการติดตั้งทรัพยากร พวกเขาเป็น

ls /usr/local/lib/python2.7/dist-packages/cnn_client/resources

mscoco_complete_label_map.pbtxt
retinanet_complete_label_map.pbtxt 
 retinanet_label_map.py  

ใน tfclient.py เพื่อเข้าถึงไฟล์เหล่านี้ จาก

templates_dir = os.path.join(os.path.dirname(__file__), 'resources')
 file_path = os.path.join(templates_dir, \
            'mscoco_complete_label_map.pbtxt')
        s = open(file_path, 'r').read()

และมันใช้งานได้


-5

ฉันใช้เวลานานในการหาคำตอบสำหรับเรื่องนี้ แต่ในที่สุดฉันก็เข้าใจ (และจริง ๆ แล้วมันง่ายจริงๆ):

import sys
import os
sys.path.append(os.getcwd() + '/your/subfolder/of/choice')

# now import whatever other modules you want, both the standard ones,
# as the ones supplied in your subfolders

สิ่งนี้จะผนวกเส้นทางสัมพัทธ์ของโฟลเดอร์ย่อยของคุณไปยังไดเรกทอรีเพื่อให้ไพ ธ อนมองมันค่อนข้างเร็วและสกปรก แต่ก็ใช้งานได้เหมือนเครื่องราง :)


6
วิธีนี้จะใช้งานได้หากคุณใช้งานโปรแกรม Python จากไดเรกทอรีเดียวกับไฟล์. py และในกรณีที่คุณก็สามารถทำได้open('your/subfolder/of/choice')อยู่แล้ว
พอลฟิชเชอร์

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