Python 引用路径问题

前言

这两天由于要接触一个基于 Python 的开源项目,顺便重温下,毕竟 Python 也是最近最火的语言了,数据处理和机器学习的项目都是 Python。然后,在 GitHubclone 了一个画画的工程,没运行起来,搞了半天,原因主要是 Python 的环境变量问题,把解决方法放上来。


1、正文



GitHub 上的项目就是这个:neural-style,大家可以去这个项目的链接上看看介绍,是一个学习绘画风格的项目,蛮有趣的

clone 下来后,就直接运行了这个:

1
xxxMac:~ xx$ python /Python/neural-style/neural_style.py


然后,嗯,是的。项目报错 = =…… 来,让我们先看看报错信息吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
RuntimeError: module compiled against API version 0xa but this version of numpy is 0x9
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Python/2.7/site-packages/tensorflow/__init__.py", line 24, in <module>
from tensorflow.python import *
File "/Library/Python/2.7/site-packages/tensorflow/python/__init__.py", line 60, in <module>
raise ImportError(msg)
ImportError: Traceback (most recent call last):
File "/Library/Python/2.7/site-packages/tensorflow/python/__init__.py", line 49, in <module>
from tensorflow.python import pywrap_tensorflow
File "/Library/Python/2.7/site-packages/tensorflow/python/pywrap_tensorflow.py", line 28, in <module>
_pywrap_tensorflow = swig_import_helper()
File "/Library/Python/2.7/site-packages/tensorflow/python/pywrap_tensorflow.py", line 24, in swig_import_helper
_mod = imp.load_module('_pywrap_tensorflow', fp, pathname, description)
ImportError: numpy.core.multiarray failed to import
Error importing tensorflow. Unless you are using bazel,
you should not try to import tensorflow from its source directory;
please exit the tensorflow source tree, and relaunch your python interpreter
from there.

看了一下报错,这一句 “module compiled against API version 0xa but this version of numpy is 0x9 ”,如果你去搜一下,很多帖子都是说你需要升级下 numpy 的版本,可是我在下 tensorflow 这个点时候,如果你仔细看的话,其实就下了很多依赖的,pillownumpy 都在其中,而且已经是最新版本了。有的帖子的思路是卸载了 numpy 或者是 tensorflow 然后重新安装。在我这里都不管用。然后,我在一个帖子里看到了下面这个解决方法。

1
2
3
4
5
>>> import numpy
>>> print numpy.__path__
['/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/numpy']
>>> exit()
Haitao-WongdeiMac:~ haitao$ sudo mv /System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/numpy /System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/numpy_old

通过打印 numpy 的路径,发现这个路径不是我下载的位置(我通过 pip install 下载的位置是 /Library/Python/2.7/site-packages/),然后继续运行之前的 Python 命令运行项目,发现 numpy 这个不报错了,但是有了新的问题,定位到这个一句 type object 'NewBase' has no attribute 'is_abstract',找不到方法的错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Python/2.7/site-packages/tensorflow/__init__.py", line 24, in <module>
from tensorflow.python import *
File "/Library/Python/2.7/site-packages/tensorflow/python/__init__.py", line 106, in <module>
from tensorflow.python.platform import test
File "/Library/Python/2.7/site-packages/tensorflow/python/platform/test.py", line 67, in <module>
from tensorflow.python.framework import test_util as _test_util
File "/Library/Python/2.7/site-packages/tensorflow/python/framework/test_util.py", line 43, in <module>
from tensorflow.python.platform import googletest
File "/Library/Python/2.7/site-packages/tensorflow/python/platform/googletest.py", line 32, in <module>
from tensorflow.python.platform import benchmark # pylint: disable=unused-import
File "/Library/Python/2.7/site-packages/tensorflow/python/platform/benchmark.py", line 119, in <module>
class Benchmark(six.with_metaclass(_BenchmarkRegistrar, object)):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/six.py", line 566, in with_metaclass
return meta("NewBase", bases, {})
File "/Library/Python/2.7/site-packages/tensorflow/python/platform/benchmark.py", line 114, in __new__
if not newclass.is_abstract():
AttributeError: type object 'NewBase' has no attribute 'is_abstract'

查了下,是属于 six 这个包的。然后我发现这个我也有下载过了该包的最新版本,然后结合刚才那个 numpy 版本的错误,我就查了下这个路径下面 /System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/,对的,没错,是之前那个打印的 numpy 的路径。发现下面就有 six,而且是个老版本的。于是乎就懂了,所有的问题就是 tensorflowimport 的包的路径的问题,然后打印了下面这个 sys.path,就是 Python 的环境变量,这有点类似于我们配置的 Java 环境,我发现我下载的包的路径在最后,而旧包的路径在前面被提前加载了,于是乎就报了 api低no attribute 的问题。

1
2
3
4
5
6
7
8
>>> import sys
>>> sys.path
['', '/Library/Python/2.7/site-packages/pip-9.0.1-py2.7.egg', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC', '/Library/Python/2.7/site-packages']
>>> sys.path =
['', '/Library/Python/2.7/site-packages', '/Library/Python/2.7/site-packages/pip-9.0.1-py2.7.egg', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC']
>>> import tensorflow
>>> exit()
xxxMac:~ xx$ python /Python/neural-style/neural_style.py

于是乎,我在 Python 命令行里修改了 sys.path 的值,然后执行了 import tensorflow,发现并没有报错。似乎大功告成了,然而当我执行 exit() 后,发现在 Terminal 中运行项目失败了。最后发现在 Python 命令行里修改了 sys.path 的值,退出后并不能保存。其他有永久保存的解决的方法,比如在 sys.path 中任意一个路径下建立 .pth 文件去新加路径,但是都不能解决问题,因为新加的路径还是 append 在最后面,仍然加载不到最新版本的包。那么有没有类似 Windows 中在 path 里配置 Java 变量的那种方式吗?

爱慕骚瑞,找了一下,发现如果你搜 sys.path 大部分文章都是讲的上面的那种类似添加 .pth 文件到方法,终于还是在 StackOverflow 上,我们找到了一个解决的方法,也不是方法了,是一个思路。链接在下面:

What sets up sys.path with Python, and when?

通过这篇文章,我们知道,Python 是如何构建自己的 sys.path 中的那些路径呢?答案是通过下面这个 site.py 文件。

1
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.py

找到这一段代码,看下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def addsitepackages(known_paths):
"""Add site-packages (and possibly site-python) to sys.path"""
for sitedir in getsitepackages():
if os.path.isdir(sitedir):
addsitedir(sitedir, known_paths)
return known_paths
def getsitepackages():
"""Returns a list containing all global site-packages directories
(and possibly site-python).
For each directory present in the global ``PREFIXES``, this function
will find its `site-packages` subdirectory depending on the system
environment, and will return a list of full paths.
"""
sitepackages = []
seen = set()
for prefix in PREFIXES:
if not prefix or prefix in seen:
continue
seen.add(prefix)
if sys.platform in ('os2emx', 'riscos'):
sitepackages.append(os.path.join(prefix, "Lib", "site-packages"))
elif sys.platform == 'darwin' and prefix == sys.prefix:
sitepackages.append(os.path.join(prefix, "Extras", "lib", "python"))
elif os.sep == '/':
sitepackages.append(os.path.join(prefix, "lib",
"python" + sys.version[:3],
"site-packages"))
sitepackages.append(os.path.join(prefix, "lib", "site-python"))
else:
sitepackages.append(prefix)
sitepackages.append(os.path.join(prefix, "lib", "site-packages"))
if sys.platform == "darwin":
# for framework builds *only* we add the standard Apple
# locations.
from sysconfig import get_config_var
framework = get_config_var("PYTHONFRAMEWORK")
if framework:
sitepackages.append(
os.path.join("/Library", framework,
sys.version[:3], "site-packages"))
return sitepackages

我们先看这个方法 getsitepackages(),跳到这个方法的代码中,通过下面命令行得出的 sys 的信息,我们可以得知, getsitepackages() 方法就是通过你的 OSPython 版本等信息来拼接一些路径,他们都被添加到一个列表中。

1
2
3
4
5
6
7
>>> import sys
>>> sys.prefix
'/System/Library/Frameworks/Python.framework/Versions/2.7'
>>> sys.platform
'darwin'
>>> sys.version[:3]
'2.7'

最后我们看这个方法 addsitedir(sitedir, known_paths),跳到这个方法的代码中,修改了 sys.path.append(sitedir) 这一句,改为了 sys.path.insert(0, sitedir)。改完之后,需要重启 Mac 来重新生成 sys.path 的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def addsitedir(sitedir, known_paths=None):
"""Add 'sitedir' argument to sys.path if missing and handle .pth files in
'sitedir'"""
if known_paths is None:
known_paths = _init_pathinfo()
reset = 1
else:
reset = 0
sitedir, sitedircase = makepath(sitedir)
if not sitedircase in known_paths:
sys.path.append(sitedir) # Add path component
try:
names = os.listdir(sitedir)
except os.error:
return
dotpth = os.extsep + "pth"
names = [name for name in names if name.endswith(dotpth)]
for name in sorted(names):
addpackage(sitedir, name, known_paths)
if reset:
known_paths = None
return known_paths

开机之后,打开 Terminal ,查一下sys.path,嗯,就是下面这样,'/Library/Python/2.7/site-packages' 这个路径在前面了,运行项目也OK了。

1
2
3
4
5
6
>>> import sys
>>> sys.path
['', '/Library/Python/2.7/site-packages/pip-9.0.1-py2.7.egg', '/Library/Python/2.7/site-packages', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC']
>>> import tensorflow
>>> exit()
xxxMac:~ xx$ python /Python/neural-style/neural_style.py


2、补充



还有两个问题,补充一下:

  • 除了这个方式,还有一个一个就是通过打印报错的包的 path 信息来确认在哪个位置,然后删除旧版本,理论上应该是可行的,最后都加载你下载的最新版本的包就OK了,不过就是比较麻烦而已。
  • 可能你修改了 site.py 文件,不能保存,这是由于苹果在 OS X El Capitan 版本之后引入的 SIP (Sytem Integrity Protection,系统完整性保护)] 的机制,你需要重启时按住 Command + R 进入 Recovery Model,通过 Terminal 输入 csrutil disable 关闭就OK了。