有关python模块导入机制的思考

由“from tensorflow.nn import conv2d”引发的有关python模块导入机制的思考

发现问题

在pycharm上优化毕业设计demo的时候,发现一句话:

from tensorflow.nn import conv2d

其中nnconv2d均被警告,查其定义均显示’“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这一方法。

到此,说明了两件事:

  1. 确实存在conv2d这一方法,并且程序运行时执行的应该就是该方法。(验证过程省略)
  2. 既然能够成功找到这一方法,为什么pycharm会报错,不能直接定位该方法?

复现问题

根据以上的分析,现提出一个猜想:在上述情况下,pycharm找不到相关方法或模块,会发出警告,但是程序在运行时会根据from tensorflow._api.v1 import nnfrom 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

其中

  1. bp的初始化模块仿照tensorflow
    from bp._api.v1 import nn
    
  2. _apiv1的初始化模块与代码保持一致,为空。
  3. nn的初始化模块中补上一个函数func_b以替代conv2d
    def func_b(text):
        print(text)
    
  4. a.py负责测试,写上:
    from bp.nn import func_b
    func_b('This is a.')
    

到此,内容和结构复现完成,开始测试。

测试结果如下:

  1. pycharm在nnfunc_b上均出现警告信息,符合猜想。

  2. 运行a.py,系统报错,不符合猜想:

    No module named 'bp.nn'
    

据此可知,系统并不能根据from tensorflow._api.v1 import nnfrom tensorflow.python.ops.gen_nn_ops import conv2d找到相关模块和方法,猜想是错的。

由此,目前需要解决两个问题:

  1. 既然from tensorflow._api.v1 import nn不能让程序找到nn模块,那么它的意义在哪?
  2. 程序在运行时是如何找到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方法所需要的信息串比作一条“路”,那么我们不难知道:

  1. 复现失败,说明这条“路”不在当前的程序中;
  2. 复现成功,说明这条“路”在当前的程序中。

由于第一次复现失败,说明这条“路”不在第一次复现的程序中。为了找到这条“路”,目前所需要做的是:保证能够成功复现问题,使复现程序包含这条“路”。然后进一步简化信息,找出哪一部分代码充当了这条“路”。处理步骤如下:

  1. 首先保证复现成功:将原tensorflow整包拷贝到test文件夹下,代替原来的bp包。为了防止冲突,将原tensorflow包名改为_tensorflow
  2. 在拷贝之后,pycharm出现警告,运行a.py,没有报错。复现成功。
  3. 将注意力集中在tensorflow的初始化模块上。将它的其它内容全部删除,仅留下:
    from tensorflow._api.v1 import nn
    
    运行a.py,系统报错:No module named 'tensorflow.nn'。说明“路”在删除的信息中。
  4. 还原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所在目录。

因此总结如下:

  1. 一般而言,在from xx.xxx import xxxx中,xxx需要在xx模块的__path__中。
  2. 一般情况下,__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对nnfunc_b提出警告,程序运行成功。打印结果如下:

['C:\\Users\\zhazha\\Desktop\\test\\bp']
['C:\\Users\\zhazha\\Desktop\\test\\bp', 'C:\\Users\\zhazha\\Desktop\\test\\bp\\_api\\v1']
This is a.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值