发现问题
在pycharm上优化毕业设计demo的时候,发现一句话:
from tensorflow.nn import conv2d
其中nn
和conv2d
均被警告,查其定义均显示’“Cannot find declaration to go to”
。但是相关代码确实能够正常运行,且没有报错。
初探问题
为了找到原因,尝试人工寻找conv2d方法。
先找到tensorflow
模块中的__init__.py
,其中有这样一行:
from tensorflow._api.v1 import nn
在此进一步寻找到nn
模块的__init__.py
文件,并发现了这样一行:
from tensorflow.python.ops.gen_nn_ops import conv2d
在gen_nn_ops.py
下确实找到了conv2d
这一方法。
到此,说明了两件事:
- 确实存在
conv2d
这一方法,并且程序运行时执行的应该就是该方法。(验证过程省略) - 既然能够成功找到这一方法,为什么pycharm会报错,不能直接定位该方法?
复现问题
根据以上的分析,现提出一个猜想:在上述情况下,pycharm找不到相关方法或模块,会发出警告,但是程序在运行时会根据from tensorflow._api.v1 import nn
和from tensorflow.python.ops.gen_nn_ops import conv2d
找到相关方法或模块。为了验证这一猜想,开始复现问题。
首先分析一下tensorflow模块的结构,如下:
- tensorflow
- init.py
- _api
- init.py
- v1
- init.py
- nn
- init.py
现复刻以上结构,加入到自己的项目,如下:
- test
- bp
- init.py
- _api
- init.py
- v1
- init.py
- nn
- init.py
- a.py
- bp
其中
bp
的初始化模块仿照tensorflow
:from bp._api.v1 import nn
_api
和v1
的初始化模块与代码保持一致,为空。- 在
nn
的初始化模块中补上一个函数func_b
以替代conv2d
:def func_b(text): print(text)
a.py
负责测试,写上:from bp.nn import func_b func_b('This is a.')
到此,内容和结构复现完成,开始测试。
测试结果如下:
-
pycharm在
nn
和func_b
上均出现警告信息,符合猜想。 -
运行
a.py
,系统报错,不符合猜想:No module named 'bp.nn'
据此可知,系统并不能根据from tensorflow._api.v1 import nn
和from tensorflow.python.ops.gen_nn_ops import conv2d
找到相关模块和方法,猜想是错的。
由此,目前需要解决两个问题:
- 既然
from tensorflow._api.v1 import nn
不能让程序找到nn
模块,那么它的意义在哪? - 程序在运行时是如何找到
nn
这一模块,进而寻找到conv2d
这一方法的?
在尝试修改a.py
后,第一个问题得到了解答:
from bp import nn
nn.func_b('This is a.')
运行上述代码,不仅pycharm没有出现警告,程序也正常运行。若注释from bp._api.v1 import nn
,上述代码将会报错。
这说明:在bp
的初始化模块中加入from bp._api.v1 import nn
,可以让其他程序能够直接从bp
中导入nn
模块。如果不加,那么其他程序需要从bp._api.v1
中导入nn
模块。
再次复现
如果将程序寻找nn
模块和conv2d
方法所需要的信息串比作一条“路”,那么我们不难知道:
- 复现失败,说明这条“路”不在当前的程序中;
- 复现成功,说明这条“路”在当前的程序中。
由于第一次复现失败,说明这条“路”不在第一次复现的程序中。为了找到这条“路”,目前所需要做的是:保证能够成功复现问题,使复现程序包含这条“路”。然后进一步简化信息,找出哪一部分代码充当了这条“路”。处理步骤如下:
- 首先保证复现成功:将原
tensorflow
整包拷贝到test
文件夹下,代替原来的bp
包。为了防止冲突,将原tensorflow
包名改为_tensorflow
。 - 在拷贝之后,pycharm出现警告,运行
a.py
,没有报错。复现成功。 - 将注意力集中在
tensorflow
的初始化模块上。将它的其它内容全部删除,仅留下:
运行from tensorflow._api.v1 import nn
a.py
,系统报错:No module named 'tensorflow.nn'
。说明“路”在删除的信息中。 - 还原
tensorflow
的初始化模块,依次注释每一个模块,找到了根源:
其中# Make sure directory containing top level submodules is in # the __path__ so that "from tensorflow.foo import bar" works. _tf_api_dir = _os.path.dirname(_os.path.dirname(app.__file__)) # pylint: disable=undefined-variable if _tf_api_dir not in __path__: __path__.append(_tf_api_dir)
_os.path.dirname(_os.path.dirname(app.__file__))
就是xxx/tensorflow/api/v1
,即nn
所在目录。
因此总结如下:
- 一般而言,在
from xx.xxx import xxxx
中,xxx
需要在xx
模块的__path__
中。 - 一般情况下,
__path__
为当前所在目录,可以往里添加新的目录。
重新复现
为了验证上述结论,在第一次复现的基础上,修改bp
的初始化模块:
from bp._api.v1 import nn
print(__path__)
__path__.append(_os.path.dirname(_os.path.dirname(nn.__file__)))
print(__path__)
在a.py
中,pycharm对nn
和func_b
提出警告,程序运行成功。打印结果如下:
['C:\\Users\\zhazha\\Desktop\\test\\bp']
['C:\\Users\\zhazha\\Desktop\\test\\bp', 'C:\\Users\\zhazha\\Desktop\\test\\bp\\_api\\v1']
This is a.