ฉันจะรับพา ธ ของไฟล์ที่เรียกใช้ปัจจุบันใน Python ได้อย่างไร


193

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

sys.argv [0]

นี่หมายถึงการใช้path = os.path.abspath(os.path.dirname(sys.argv[0]))แต่สิ่งนี้ไม่ได้ผลถ้าคุณกำลังเรียกใช้จากสคริปต์ Python อื่นในไดเรกทอรีอื่นและสิ่งนี้สามารถเกิดขึ้นได้ในชีวิตจริง

__ไฟล์__

หมายความว่าใช้path = os.path.abspath(os.path.dirname(__file__))แต่ฉันพบว่าใช้งานไม่ได้:

  • py2exeไม่มี__file__แอตทริบิวต์ แต่มีวิธีแก้ไขเฉพาะหน้า
  • เมื่อคุณเรียกใช้จาก IDLE โดยexecute()ไม่มี__file__แอตทริบิวต์
  • OS X 10.6 ที่ฉันได้รับ NameError: global name '__file__' is not defined

คำถามที่เกี่ยวข้องกับคำตอบที่ไม่สมบูรณ์:

ฉันกำลังมองหาโซลูชันทั่วไปซึ่งสามารถใช้ได้ในทุกกรณีการใช้งานด้านบน

ปรับปรุง

นี่คือผลลัพธ์ของ testcase:

ผลลัพธ์ของ python a.py (บน Windows)

a.py: __file__= a.py
a.py: os.getcwd()= C:\zzz

b.py: sys.argv[0]= a.py
b.py: __file__= a.py
b.py: os.getcwd()= C:\zzz

a.py

#! /usr/bin/env python
import os, sys

print "a.py: sys.argv[0]=", sys.argv[0]
print "a.py: __file__=", __file__
print "a.py: os.getcwd()=", os.getcwd()
print

execfile("subdir/b.py")

subdir / b.py

#! /usr/bin/env python
import os, sys

print "b.py: sys.argv[0]=", sys.argv[0]
print "b.py: __file__=", __file__
print "b.py: os.getcwd()=", os.getcwd()
print

ต้นไม้

C:.
|   a.py
\---subdir
        b.py

คำตอบ:


80

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

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

some_path / module_locator.py:

def we_are_frozen():
    # All of the modules are built-in to the interpreter, e.g., by py2exe
    return hasattr(sys, "frozen")

def module_path():
    encoding = sys.getfilesystemencoding()
    if we_are_frozen():
        return os.path.dirname(unicode(sys.executable, encoding))
    return os.path.dirname(unicode(__file__, encoding))

some_path / main.py:

import module_locator
my_path = module_locator.module_path()

หากคุณมีสคริปต์หลักหลายตัวในไดเรกทอรีที่แตกต่างกันคุณอาจต้องมากกว่าหนึ่งสำเนาของ module_locator

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


2
ฉันพูดถึงว่าใน OS 10.6 ฉันNameError: global name '__file__' is not definedใช้ไฟล์และนี่ไม่ได้อยู่ใน IDLE คิดว่า__file__ถูกกำหนดไว้เฉพาะในโมดูล
sorin

1
@ โซริน Sbarnea: ฉันปรับปรุงคำตอบของฉันด้วยวิธีที่ฉันได้รับรอบ
Daniel Stutzbach

2
ขอบคุณ แต่ที่จริงแล้วปัญหาที่ขาดหายไป__file__นั้นไม่เกี่ยวกับ Unicode ฉันไม่ทราบสาเหตุที่__file__ไม่ได้กำหนด แต่ฉันกำลังมองหาวิธีการแก้ปัญหาทั่วไปนี้จะทำงานในทุกกรณี
sorin

1
ขออภัยไม่สามารถทำได้ในทุกกรณี ตัวอย่างเช่นฉันพยายามทำสิ่งนี้ใน waf.googlecode.com จากภายในไฟล์ wscript (python) ไฟล์เหล่านี้จะถูกดำเนินการ แต่ไม่ใช่โมดูลและคุณไม่สามารถสร้างโมดูลได้ (สามารถเป็นไดเรกทอรีย่อยใด ๆ จากแผนผังต้นไม้)
โซริน

1
จะไม่ให้ที่ตั้งsome_path/module_locator.pyแทนหรือไม่
Casebash

68

ก่อนอื่นคุณต้องนำเข้าจากinspectและos

from inspect import getsourcefile
from os.path import abspath

ถัดไปทุกที่ที่คุณต้องการค้นหาไฟล์ต้นฉบับจากคุณเพียงใช้

abspath(getsourcefile(lambda:0))

คำตอบที่ดีที่สุด ขอบคุณ.
Devan Williams

4
คำตอบที่ดี ขอบคุณ นอกจากนี้ยังดูเหมือนว่าจะเป็นคำตอบที่สั้นและพกพาได้มากที่สุด (ทำงานบนระบบปฏิบัติการที่แตกต่างกัน) และไม่พบปัญหาเช่นNameError: global name '__file__' is not defined(โซลูชันอื่นเป็นสาเหตุของปัญหานี้)
Edward

ความเป็นไปได้อีกข้อหนึ่งซึ่งสั้นพอ ๆ กัน: lambda:_. มันใช้งานได้สำหรับฉัน - ไม่แน่ใจว่ามันจะใช้ได้เสมอ lambda:0อาจทำงานได้เร็วขึ้นด้วยจำนวนเล็กน้อยเล็กน้อย (หรืออาจไม่เล็ก ... อาจเป็นภาระทันทีหรืออะไรที่เร็วกว่าสำหรับ0โหลดทั่วโลกสำหรับ_?) เป็นที่ถกเถียงกันว่ามันสะอาดหรืออ่านง่ายกว่าหรือฉลาดกว่าหรือคลุมเครือ
ArtOfWarfare

เช่นเดียวกับข้อเสนอเกือบทุกข้อที่ฉันได้ลองมันแค่คืนค่า cwd ให้ฉันไม่ใช่ไฟล์ไดเรกทอรีที่ฉันกำลังทำงานอยู่ (การดีบัก)
James

1
@James - รหัสนี้ไปในไฟล์ที่คุณกำลังเรียกใช้ ... กำลังทำงานgetsourcefile(lambda:0)จะไม่มีความหมายและเพียงแค่ส่งคืนNoneถ้าคุณลองเรียกใช้ที่พรอมต์แบบโต้ตอบ (เนื่องจากlambdaจะไม่อยู่ในไฟล์ต้นฉบับ) หากคุณต้องการทราบ ฟังก์ชันหรือวัตถุอื่นมาจากที่ใดในสภาพแวดล้อมแบบอินเทอร์แอคทีฟอาจabspath(getsourcefile(thatFunctionOrObject))มีประโยชน์มากกว่าสำหรับคุณ
ArtOfWarfare

16

วิธีการแก้ปัญหานี้มีประสิทธิภาพแม้ใน executables

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))

2
นี่ควรเป็นคำตอบที่ถูกต้อง สิ่งนี้ใช้ได้แม้ในentry_point: console_scriptแต่ไม่มีคำตอบอื่นใด
Polv

15

ฉันพบปัญหาที่คล้ายกันและฉันคิดว่านี่อาจช่วยแก้ปัญหาได้:

def module_path(local_function):
   ''' returns the module path without the use of __file__.  Requires a function defined
   locally in the module.
   from http://stackoverflow.com/questions/729583/getting-file-path-of-imported-module'''
   return os.path.abspath(inspect.getsourcefile(local_function))

มันใช้งานได้กับสคริปต์ปกติและไม่ได้ใช้งาน ทั้งหมดที่ฉันพูดได้คือลองทำเพื่อคนอื่น!

การใช้งานทั่วไปของฉัน:

from toolbox import module_path
def main():
   pass # Do stuff

global __modpath__
__modpath__ = module_path(main)

ตอนนี้ฉันใช้ __modpath__ แทน __file__


2
ตามคู่มือการเขียนโค้ดสไตล์PEP8เราไม่ควรสร้างชื่อที่มีเครื่องหมายขีดเส้นใต้คู่และขีดล่าง - ดังนั้น__modpath__ควรเปลี่ยนชื่อ คุณอาจไม่ต้องการglobalคำสั่ง มิฉะนั้น +1!
martineau

4
module_path()ที่จริงคุณสามารถกำหนดฟังก์ชั่นในท้องถิ่นที่ถูกต้องในการเรียกร้องให้ เช่นmodule_path(lambda _: None)ซึ่งไม่ได้ขึ้นอยู่กับเนื้อหาอื่น ๆ ของสคริปต์ที่มีอยู่ในมัน
martineau

@ Martineau: ฉันเอาข้อเสนอแนะของคุณlambda _: Noneและใช้มันมาเกือบสองปีที่ผ่านมา แต่ตอนนี้ฉันรู้แล้วว่าฉันสามารถย่อมันลงไปlambda:0ได้ มีเหตุผลใดบ้างที่คุณแนะนำแบบฟอร์มที่คุณทำโดยมีอาร์กิวเมนต์ที่ถูกเพิกเฉย_แทนที่จะเป็นไม่มีอาร์กิวเมนต์เลย? มีบางสิ่งที่เหนือกว่าเกี่ยวกับNoneคำนำหน้าด้วยเว้นวรรคมากกว่าแค่0? ฉันคิดว่าพวกเขาทั้งสองมีความลับไม่แพ้กันเพียงคนเดียวคือ 8 ตัวอักษรในขณะที่อีกคนยาว 14 ตัวอักษร
ArtOfWarfare

@ArtOfWarfare: ความแตกต่างระหว่างทั้งสองก็lambda _:คือฟังก์ชั่นที่ใช้เวลาหนึ่งอาร์กิวเมนต์และlambda:เป็นหนึ่งที่ไม่ใช้ใด ๆ มันไม่สำคัญเนื่องจากฟังก์ชั่นไม่เคยถูกเรียก ในทำนองเดียวกันมันไม่สำคัญว่าจะใช้มูลค่าส่งคืนเท่าใด ฉันเดาว่าฉันเลือกNoneเพราะในขณะนั้นดูเหมือนว่าจะบ่งบอกว่ามันเป็นฟังก์ชั่นที่ไม่ทำอะไรเลยที่ไม่เคยถูกเรียกว่าดีกว่า พื้นที่ด้านหน้ามันเป็นทางเลือกและที่นั่นมีไว้เพื่อการอ่านที่ดีขึ้นเท่านั้น (การพยายามติดตาม PEP8 เป็นการสร้างนิสัย)
martineau

@martineau: เห็นได้ชัดว่าเป็นการละเมิดlambdaว่าใช้มันเพื่อสิ่งที่ไม่เคยตั้งใจทำ หากคุณกำลังจะติดตาม PEP8 ฉันคิดว่าเนื้อหาที่เหมาะสมสำหรับมันpassไม่ใช่Noneแต่มันไม่ถูกต้องที่จะใส่คำสั่งใน a lambdaดังนั้นคุณต้องใส่อะไรที่มีค่า มีสิ่งที่ถูกต้อง 2 ตัวที่คุณสามารถใส่ได้ แต่ฉันคิดว่าสิ่งเดียวที่ถูกต้องที่คุณสามารถใส่ได้คือ 0-9 (หรือชื่อตัวแปรอักขระเดียวที่ได้รับมอบหมายนอกlambda) ฉันคิดว่า0สิ่งที่ดีที่สุดของ 0- 9
ArtOfWarfare

6

คำตอบสั้น ๆคือไม่มีวิธีที่รับประกันได้ว่าจะได้รับข้อมูลที่คุณต้องการอย่างไรก็ตามมีฮิวริสติกที่ทำงานเกือบตลอดเวลาในทางปฏิบัติ คุณอาจดูที่ฉันจะหาที่ตั้งของปฏิบัติการใน C ได้อย่างไร . มันกล่าวถึงปัญหาจากมุมมอง C แต่การแก้ปัญหาที่เสนอจะถูกคัดลอกลงใน Python ได้อย่างง่ายดาย


สวัสดีธงของฉันถูกปฏิเสธดังนั้นฉันจึงเริ่มการสนทนาเกี่ยวกับเมตา: meta.stackoverflow.com/questions/277272/…
ArtOfWarfare

มีคำตอบที่ใช้งานง่ายอยู่แล้วในหน้านี้ ทางออกของฉันทำงานได้ดี: stackoverflow.com/a/33531619/3787376
Edward

5

ดูคำตอบของฉันสำหรับคำถามการนำเข้าโมดูลจากโฟลเดอร์พาเรนต์สำหรับข้อมูลที่เกี่ยวข้องรวมถึงสาเหตุที่คำตอบของฉันไม่ได้ใช้__file__ตัวแปรที่ไม่น่าเชื่อถือ โซลูชันที่เรียบง่ายนี้ควรใช้งานร่วมกับระบบปฏิบัติการที่แตกต่างกันได้เช่นเดียวกับโมดูลosและinspectเป็นส่วนหนึ่งของ Python

ก่อนอื่นคุณต้องนำเข้าชิ้นส่วนของโมดูลตรวจสอบและระบบปฏิบัติการ

from inspect import getsourcefile
from os.path import abspath

ถัดไปใช้บรรทัดต่อไปนี้ทุกที่ที่จำเป็นในรหัส Python ของคุณ:

abspath(getsourcefile(lambda:0))

มันทำงานอย่างไร:

จากโมดูลในตัวos(คำอธิบายด้านล่าง) abspathเครื่องมือจะถูกนำเข้า

รูทีนระบบปฏิบัติการสำหรับ Mac, NT หรือ Posix ขึ้นอยู่กับระบบที่เราใช้

แล้วgetsourcefile(รายละเอียดด้านล่าง) inspectจะถูกนำเข้ามาจากในตัวโมดูล

รับข้อมูลที่เป็นประโยชน์จากวัตถุ Python สด

  • abspath(path) ส่งคืนพา ธ ไฟล์เวอร์ชันสมบูรณ์ / เวอร์ชันเต็ม
  • getsourcefile(lambda:0)อย่างใดได้รับไฟล์ต้นฉบับภายในของวัตถุฟังก์ชั่นแลมบ์ดาดังนั้นกลับ'<pyshell#nn>'ในเปลือกงูหลามหรือส่งกลับเส้นทางไฟล์ของรหัสงูหลามในขณะนี้กำลังดำเนินการ

การใช้abspathผลลัพธ์getsourcefile(lambda:0)ควรตรวจสอบให้แน่ใจว่าเส้นทางของไฟล์ที่สร้างขึ้นเป็นเส้นทางไฟล์แบบเต็มของไฟล์ Python
วิธีการแก้ปัญหาที่อธิบายนี้เดิมนั้นใช้รหัสจากคำตอบที่ฉันจะหาเส้นทางของไฟล์ที่เรียกใช้ปัจจุบันใน Python ได้อย่างไร .


ใช่ฉันเพิ่งมาด้วยวิธีการแก้ปัญหาเดียวกัน ... วิธีที่ดีกว่าคำตอบที่เพิ่งบอกว่ามันไม่สามารถทำได้อย่างน่าเชื่อถือ ... เว้นแต่คำถามจะไม่ถามสิ่งที่ฉันคิดว่ามันคือ ...
Grady Player

5

คุณเรียกง่ายๆว่า:

path = os.path.abspath(os.path.dirname(sys.argv[0]))

แทน:

path = os.path.dirname(os.path.abspath(sys.argv[0]))

abspath()ให้เส้นทางที่แน่นอนของsys.argv[0](ชื่อไฟล์ของคุณใน) และdirname()ส่งกลับเส้นทางไดเรกทอรีโดยไม่ต้องชื่อไฟล์


4

สิ่งนี้ควรทำเคล็ดลับข้ามแพลตฟอร์ม (ตราบใดที่คุณไม่ได้ใช้ล่ามหรือบางอย่าง):

import os, sys
non_symbolic=os.path.realpath(sys.argv[0])
program_filepath=os.path.join(sys.path[0], os.path.basename(non_symbolic))

sys.path[0]เป็นไดเรกทอรีที่สคริปต์การโทรของคุณอยู่ (อันดับแรกจะค้นหาโมดูลที่สคริปต์นั้นจะใช้) เราสามารถนำชื่อของไฟล์ออกจากส่วนท้ายsys.argv[0](ซึ่งเป็นสิ่งที่ฉันทำด้วยos.path.basename) os.path.joinเพียงแค่ติดเข้าด้วยกันในลักษณะข้ามแพลตฟอร์ม os.path.realpathตรวจสอบให้แน่ใจว่าเราได้รับลิงก์สัญลักษณ์ที่มีชื่อแตกต่างจากสคริปต์ที่เรายังได้รับชื่อจริงของสคริปต์นั้น

ฉันไม่มี Mac ดังนั้นฉันไม่ได้ทดสอบสิ่งนี้ในที่เดียว โปรดแจ้งให้เราทราบหากใช้งานได้ตามที่ควรจะเป็น ฉันทดสอบสิ่งนี้ใน Linux (Xubuntu) ด้วย Python 3.4 โปรดทราบว่าการแก้ปัญหาหลายอย่างสำหรับปัญหานี้ใช้ไม่ได้บน Macs (เนื่องจากฉันเคยได้ยินมาแล้ว__file__ไม่มีใน Macs)

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


2

คุณสามารถใช้Pathจากpathlibโมดูล:

from pathlib import Path

# ...

Path(__file__)

คุณสามารถใช้การโทรไปparentเพื่อไปเพิ่มเติมในเส้นทาง:

Path(__file__).parent

คำตอบนี้ใช้__file__ตัวแปรที่ไม่น่าเชื่อถือ (ไม่ใช่เส้นทางไฟล์แบบเต็มเสมอไปไม่สามารถใช้งานได้กับทุกระบบปฏิบัติการ ฯลฯ ) เนื่องจากผู้ใช้ StackOverflow ได้กล่าวถึงบ่อยครั้ง การเปลี่ยนคำตอบเป็นไม่รวมจะทำให้เกิดปัญหาน้อยลงและเข้ากันได้มากขึ้น สำหรับข้อมูลเพิ่มเติมโปรดดูที่stackoverflow.com/a/33532002/3787376
Edward

@ mrroot5 ตกลงดังนั้นโปรดลบความคิดเห็นของคุณ
Gavriel Cohen

1

หากรหัสมาจากไฟล์คุณสามารถรับชื่อเต็มได้

sys._getframe().f_code.co_filename

คุณยังสามารถดึงชื่อฟังก์ชั่นเป็น f_code.co_name


0

เพียงเพิ่มรายการต่อไปนี้:

from sys import *
path_to_current_file = sys.argv[0]
print(path_to_current_file)

หรือ:

from sys import *
print(sys.argv[0])


-1
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))

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