模块__file__属性是绝对的还是相对的?

问题:模块__file__属性是绝对的还是相对的?

我在理解上有困难__file__。据我了解,__file__返回加载模块的绝对路径。

我在制作这个问题:我有abc.py一个说法print __file__,从运行/d/projects/ python abc.py的回报abc.py。从/d/收益出发projects/abc.py。有什么原因吗?

I’m having trouble understanding __file__. From what I understand, __file__ returns the absolute path from which the module was loaded.

I’m having problem producing this: I have a abc.py with one statement print __file__, running from /d/projects/ python abc.py returns abc.py. running from /d/ returns projects/abc.py. Any reasons why?


回答 0

文档中

__file__是从中加载模块的文件的路径名(如果它是从文件加载的)。__file__对于静态链接到解释器的C模块,该属性不存在。对于从共享库动态加载的扩展模块,它是共享库文件的路径名。

从@kindall链接的邮件列表线程中,对该问题进行评论:

我没有尝试对这个特定示例进行复制,但是原因是我们不想在每次导入时都调用getpwd(),也不想拥有某种进程内变量来缓存当前目录。(getpwd()相对较慢,有时可能会完全失败,并且尝试对其进行缓存有一定的错误风险。)

相反,我们要做的是site.py中的代码,该代码遍历sys.path的元素并将其转换为绝对路径。但是,此代码在将”插入sys.path的开头之前运行,因此sys.path的初始值为。

对于其余的内容,请考虑sys.path不包括''

因此,如果您不在sys.path包含模块的那一部分内,则将获得一条绝对路径。如果您位于sys.path包含模块的那一部分内,则会得到一个相对路径

如果您在当前目录中加载模块,而当前目录不在sys.path,则将获得绝对路径。

如果将模块加载到当前目录中,并且当前目录位于sys.path,则将获得相对路径。

From the documentation:

__file__ is the pathname of the file from which the module was loaded, if it was loaded from a file. The __file__ attribute is not present for C modules that are statically linked into the interpreter; for extension modules loaded dynamically from a shared library, it is the pathname of the shared library file.

From the mailing list thread linked by @kindall in a comment to the question:

I haven’t tried to repro this particular example, but the reason is that we don’t want to have to call getpwd() on every import nor do we want to have some kind of in-process variable to cache the current directory. (getpwd() is relatively slow and can sometimes fail outright, and trying to cache it has a certain risk of being wrong.)

What we do instead, is code in site.py that walks over the elements of sys.path and turns them into absolute paths. However this code runs before ” is inserted in the front of sys.path, so that the initial value of sys.path is ”.

For the rest of this, consider sys.path not to include ''.

So, if you are outside the part of sys.path that contains the module, you’ll get an absolute path. If you are inside the part of sys.path that contains the module, you’ll get a relative path.

If you load a module in the current directory, and the current directory isn’t in sys.path, you’ll get an absolute path.

If you load a module in the current directory, and the current directory is in sys.path, you’ll get a relative path.


回答 1

__file__从Python 3.4开始是绝对的,除了直接使用相对路径执行脚本时除外:

__file__现在,默认情况下,模块属性(和相关值)应始终包含绝对路径,唯一的exceptions是__main__.__file__何时使用相对路径直接执行了脚本。(由Brett Cannon在bpo-18416中贡献。)

虽然不确定它是否解析符号链接。

传递相对路径的示例:

$ python script.py

__file__ is absolute since Python 3.4, except when executing a script directly using a relative path:

Module __file__ attributes (and related values) should now always contain absolute paths by default, with the sole exception of __main__.__file__ when a script has been executed directly using a relative path. (Contributed by Brett Cannon in bpo-18416.)

Not sure if it resolves symlinks though.

Example of passing a relative path:

$ python script.py

回答 2

后面的简单示例:

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__返回绝对路径,因此可以很好地工作。

Late simple example:

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()

Under Python-2.*, the second call incorrectly determines the path.abspath(__file__) based on the current directory:

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

As noted by @techtonik, in Python 3.4+, this will work fine since __file__ returns an absolute path.


回答 3

借助@kindall提供的Guido邮件的帮助,我们可以理解标准的导入过程,即试图在其中的每个成员中查找模块sys.path,并以此查找的结果进行归档(更多详细信息,请参见PyMOTW Modules and Imports。)。因此,如果模块位于绝对路径中,sys.path则结果为绝对,但如果模块位于相对路径中,sys.path则结果为相对。

现在,site.py启动文件将只在in中提供绝对路径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,如果添加,/tmpPYTHONPATH将得到:

>>> 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()相对较慢,有时可能会完全失败,

因此,您的路径将按原样使用。

With the help of the of Guido mail provided by @kindall, we can understand the standard import process as trying to find the module in each member of sys.path, and file as the result of this lookup (more details in PyMOTW Modules and Imports.). So if the module is located in an absolute path in sys.path the result is absolute, but if it is located in a relative path in sys.path the result is relative.

Now the site.py startup file takes care of delivering only absolute path in sys.path, except the initial '', so if you don’t change it by other means than setting the PYTHONPATH (whose path are also made absolute, before prefixing sys.path), you will get always an absolute path, but when the module is accessed through the current directory.

Now if you trick sys.path in a funny way you can get anything.

As example if you have a sample module foo.py in /tmp/ with the code:

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

If you go in /tmp you get:

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

When in in /home/user, if you add /tmp your PYTHONPATH you get:

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

Even if you add ../../tmp, it will be normalized and the result is the same.

But if instead of using PYTHONPATH you use directly some funny path you get a result as funny as the cause.

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

Guido explains in the above cited thread, why python do not try to transform all entries in absolute paths:

we don’t want to have to call getpwd() on every import …. getpwd() is relatively slow and can sometimes fail outright,

So your path is used as it is.