โมดูล __file__ แอตทริบิวต์เป็นค่าสัมบูรณ์หรือสัมพัทธ์?


107

__file__ฉันมีปัญหาในการทำความเข้าใจ จากสิ่งที่ฉันเข้าใจ__file__ส่งคืนเส้นทางสัมบูรณ์ที่โมดูลถูกโหลด

ฉันมีปัญหาการผลิตนี้: ฉันมีabc.pyมีคำสั่งอย่างใดอย่างหนึ่งprint __file__วิ่งออกมาจากผลตอบแทน/d/projects/ python abc.py abc.pyวิ่งออกมาจากผลตอบแทน/d/ projects/abc.pyเหตุผลใด?


10
นี่คือสิ่งที่กุยได้กล่าวเกี่ยวกับเรื่องนี้: mail.python.org/pipermail/python-dev/2010-February/097461.html
kindall

เกี่ยวข้อง: stackoverflow.com/q/9271464/1959808
Ioannis Filippidis

คำตอบ:


98

จากเอกสารประกอบ :

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

จากเธรดรายชื่ออีเมลที่เชื่อมโยงโดย @kindall ในความคิดเห็นของคำถาม:

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

สิ่งที่เราทำแทนคือโค้ดใน site.py ที่เดินผ่านองค์ประกอบของ sys.path และเปลี่ยนเป็นเส้นทางที่แน่นอน อย่างไรก็ตามโค้ดนี้ทำงานก่อนที่จะแทรก "" ที่ด้านหน้า sys.path เพื่อให้ค่าเริ่มต้นของ sys.path คือ ""

สำหรับส่วนที่เหลือนี้sys.pathไม่ควรรวม''ไว้

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

หากคุณโหลดโมดูลในไดเร็กทอรีปัจจุบันและไม่ได้อยู่ในไดเร็กทอรีปัจจุบันsys.pathคุณจะได้รับพา ธ สัมบูรณ์

หากคุณโหลดโมดูลในไดเร็กทอรีปัจจุบันและไดเร็กทอรีปัจจุบันอยู่ในsys.pathคุณจะได้รับพา ธ สัมพัทธ์


หมายความว่าถ้ามีเส้นทางจาก '' ไปยังโมดูลจะใช้พา ธ สัมพัทธ์ถ้าไม่ใช่พา ธ สัมบูรณ์จะใช้เนื่องจาก sys.path ที่เหลือเป็นค่าสัมบูรณ์ ..
goh

4
หากคุณโหลดโมดูลในไดเร็กทอรีปัจจุบันและไม่ได้อยู่ในไดเร็กทอรีปัจจุบันsys.pathคุณจะได้รับพา ธ สัมบูรณ์ หากคุณโหลดโมดูลในไดเร็กทอรีปัจจุบันและไดเร็กทอรีปัจจุบันอยู่ในsys.pathคุณจะได้รับพา ธ สัมพัทธ์
agf

โปรดจำไว้เพื่อจุดประสงค์นี้ไม่รวมถึงsys.path ''
agf

เข้าใจแล้ว แต่ @agf ถ้าฉันใช้ python /foo/abc.py จาก / home ฉันคิดว่าส่วนของ sys.path ที่มีโมดูลคือ / home / foo และไดเร็กทอรีปัจจุบันของฉันคือ / home / ทำไมจึงพิมพ์ ไฟล์ให้เส้นทางสัมพัทธ์กับฉัน?
goh

55

__file__เป็นค่าสัมบูรณ์ตั้งแต่ Python 3.4ยกเว้นเมื่อเรียกใช้สคริปต์โดยตรงโดยใช้เส้นทางสัมพัทธ์:

__file__แอตทริบิวต์ของโมดูล(และค่าที่เกี่ยวข้อง) ควรมีพา ธ สัมบูรณ์ตามค่าเริ่มต้นเสมอโดยมีข้อยกเว้นเพียงอย่างเดียวคือ__main__.__file__เมื่อสคริปต์ถูกเรียกใช้โดยตรงโดยใช้พา ธ สัมพัทธ์ (สนับสนุนโดย Brett Cannon ในbpo-18416 )

ไม่แน่ใจว่ามันแก้ไข symlinks หรือไม่

ตัวอย่างการส่งผ่านเส้นทางสัมพัทธ์:

$ python script.py

1
ขอบคุณ. นี่เป็นความจริงที่ยากจะติดตาม!
meawoppl

4
สิ่งนี้ไม่เป็นความจริงสำหรับ Python 3.4.0 ( Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux) และลิงก์สัญลักษณ์ไม่ได้รับการแก้ไขในการทดลองของฉัน
Frozen Flame

@FrozenFlame อย่าลังเลที่จะรายงานไปที่bugs.python.orgหาก 3.4.1 ไม่สามารถแก้ไขได้
anatoly techtonik

2
เป็นos.path.realpath(__file__)วิธีที่ถูกต้องในการแก้ปัญหาการเชื่อมโยงสัญลักษณ์?
kevinarpe


16

ตัวอย่างง่ายๆตอนปลาย:

from os import path, getcwd, chdir

def print_my_path():
    print('cwd:     {}'.format(getcwd()))
    print('__file__:{}'.format(__file__))
    print('abspath: {}'.format(path.abspath(__file__)))

print_my_path()

chdir('..')

print_my_path()

ภายใต้ Python-2. * การเรียกครั้งที่สองกำหนดpath.abspath(__file__)ตามไดเร็กทอรีปัจจุบันไม่ถูกต้อง:

cwd:     C:\codes\py
__file__:cwd_mayhem.py
abspath: C:\codes\py\cwd_mayhem.py
cwd:     C:\codes
__file__:cwd_mayhem.py
abspath: C:\codes\cwd_mayhem.py

ตามที่ระบุไว้โดย @techtonik ใน Python 3.4+ สิ่งนี้จะทำงานได้ดีเนื่องจาก__file__ส่งคืนเส้นทางสัมบูรณ์


... ยกเว้น__main__โมดูลซึ่ง__file__ อาจเป็นพา ธ สัมพัทธ์
0xC0000022L

5

ด้วยความช่วยเหลือของอีเมล Guido ที่จัดทำโดย @kindall เราสามารถเข้าใจกระบวนการนำเข้ามาตรฐานขณะพยายามค้นหาโมดูลในสมาชิกแต่ละคนsys.pathและไฟล์อันเป็นผลมาจากการค้นหานี้ (รายละเอียดเพิ่มเติมในโมดูล PyMOTW และการนำเข้า ) ดังนั้นหากโมดูลตั้งอยู่ในพา ธ สัมบูรณ์ในsys.pathผลลัพธ์จะเป็นค่าสัมบูรณ์ แต่ถ้าโมดูลนั้นอยู่ในพา ธสัมพัทธ์ในsys.pathผลลัพธ์จะสัมพันธ์กัน

ตอนนี้site.pyไฟล์เริ่มต้นดูแลการส่งเฉพาะพา ธ สัมบูรณ์ในsys.pathยกเว้นค่าเริ่มต้น''ดังนั้นหากคุณไม่เปลี่ยนด้วยวิธีอื่นนอกเหนือจากการตั้งค่า PYTHONPATH (ซึ่งพา ธ ถูกสร้างเป็นค่าสัมบูรณ์ก่อนที่จะนำหน้าsys.path) คุณจะได้ค่าสัมบูรณ์เสมอ เส้นทาง แต่เมื่อโมดูลถูกเข้าถึงผ่านไดเร็กทอรีปัจจุบัน

ตอนนี้ถ้าคุณหลอก sys.path ด้วยวิธีตลก ๆ คุณจะได้อะไรก็ได้

ตัวอย่างเช่นถ้าคุณมีโมดูลตัวอย่างfoo.pyใน/tmp/กับรหัส:

import sys
print(sys.path)
print (__file__)

หากคุณเข้าไป / tmp คุณจะได้รับ:

>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
./foo.py

เมื่ออยู่ในใน/home/userถ้าคุณเพิ่ม/tmpของคุณPYTHONPATHคุณจะได้รับ:

>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
/tmp/foo.py

แม้ว่าคุณจะเพิ่ม../../tmpมันจะถูกทำให้เป็นมาตรฐานและผลลัพธ์ก็เหมือนกัน

แต่ถ้าแทนที่จะใช้PYTHONPATHคุณใช้เส้นทางตลก ๆ โดยตรงคุณจะได้ผลลัพธ์ที่ตลกพอ ๆ กับสาเหตุ

>>> import sys
>>> sys.path.append('../../tmp')
>>> import foo
['', '/usr/lib/python3.3', .... , '../../tmp']
../../tmp/foo.py

Guido อธิบายในหัวข้อที่อ้างถึงข้างต้นทำไม python ไม่พยายามแปลงรายการทั้งหมดในเส้นทางที่แน่นอน:

เราไม่ต้องการเรียก getpwd () ในทุกการนำเข้า .... getpwd () ค่อนข้างช้าและบางครั้งอาจล้มเหลวทันที

ดังนั้นเส้นทางของคุณจะใช้มันเป็น

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