• Python 模块调用的变量与路径


    自己编写的python代码经常需要分模块文件以及包,梳理一下调用顺序、执行顺序、工作路径、函数与变量等

    工作路径

    首先是工作路径,当模块代码放在统一的包内的时候,其路径和外层的包路径不同,当作为主调用时,工作路径是该模块所在的路径,而作为模块import进时,工作路径是主调用的路径

    这是PyTest/utils/utils.py文件,输出路径,并定义了一个输出路径函数

     1 import sys
     2   
     3 def printpath():
     4     print('function')
     5     print(sys.path[0])
     6     print('function end')
     7 
     8  9 
    10 print('utils main')
    11 print(sys.path[0])
    12 print('utils main end')

    在该目录下运行它,得到的是:

    1 zry@seupalm:~/PyTest/utils$ python utils.py 
    2 utils main
    3 /home/zry/PyTest/utils
    4 utils main end

    毫无疑问,路径就是当前路径。

    在外层的文件PyTest/fun.py:

    1 import sys
    2 from utils.utils import *
    3 
    4 print('fun main')
    5 print(sys.path[0])
    6 printpath()
    7 8 print('fun main end')

    这里该代码调用了utils/utils.py的内容,运行结果:

     1 zry@seupalm:~/PyTest$ python fun.py 
     2 utils main
     3 /home/zry/PyTest
     4 utils main end
     5 fun main
     6 /home/zry/PyTest
     7 function
     8 /home/zry/PyTest
     9 function end
    10 fun main end

    可以发现,所有的路径输出都是主调用的路径了。因此需要注意的其实是,在工具包的模块里,考虑避免设置固定路径,因为作为模块被调用时,并不确定工作路径是什么

    执行顺序

    此外,还可以发现import时utils.py先于fun.py的内容运行,因此,在import时其实是将utils.py这个模块运行了一遍的,如果仅希望引入函数,而不希望运行代码,则可以在不希望运行的代码前加上if __name__ == '__main__':

    即仅在作为主调用时才运行,注意此处name和main两边都是两个下划线

    修改PyTest/utils/utils.py:

     1 import sys
     2   
     3 def printpath():
     4     print('function')
     5     print(sys.path[0])
     6     print('function end')
     7 
     8 if __name__ == '__main__':
     9     print('utils main')
    10     print(sys.path[0])
    11     print('utils main end')

    此时重新运行PyTest/fun.py:

    1 zry@seupalm:~/PyTest$ python fun.py 
    2 fun main
    3 /home/zry/PyTest
    4 function
    5 /home/zry/PyTest
    6 function end
    7 fun main end

    就不会运行utils.py的主函数部分了

    模块内变量

    如果没有加上判断__name__的语句,实际是在import时就会运行一遍,所以对于from utils.utils import *这种操作同样会将模块里的变量也引入进来

    仍然修改PyTest/utils/utils.py:

     1 import sys
     2   
     3 def printpath():
     4     print('function')
     5     print(sys.path[0])
     6     print('function end')
     7 
     8 a = 'hello world'
     9 print('utils main')
    10 print(sys.path[0])
    11 print('utils main end')

    并在fun.py中输出a得到:

     1 zry@seupalm:~/PyTest$ python fun.py 
     2 utils main
     3 /home/zry/PyTest
     4 utils main end
     5 fun main
     6 /home/zry/PyTest
     7 function
     8 /home/zry/PyTest
     9 function end
    10 hello world
    11 fun main end

    可以看到hello world被输出了。

    引入指定函数方法

    但如果是仅引入一个函数方法呢,即使用from utils/utils import printpath:

    修改PyTest/fun.py:

    1 import sys
    2 from utils.utils import printpath
    3 
    4 print('fun main')
    5 print(sys.path[0])
    6 printpath()
    7 print(a)
    8 print('fun main end')

    运行:

     1 zry@seupalm:~/PyTest$ python fun.py 
     2 utils main
     3 /home/zry/PyTest
     4 utils main end
     5 fun main
     6 /home/zry/PyTest
     7 function
     8 /home/zry/PyTest
     9 function end
    10 Traceback (most recent call last):
    11   File "fun.py", line 7, in <module>
    12     print(a)
    13 NameError: name 'a' is not defined

    可以看到PyTest/utils/utils.py仍然被运行了一遍,但是过程中的变量则并不会被引入,因为只引入了指定的函数方法。

    跨级调用

    当需要跨级import时,如果是主调用直接import进来,则并不需要担心

    编写PyTest/utils/src/uu.py

    1 import sys
    2   
    3 def uufun():
    4     print('uufun')
    5     print(sys.path[0])
    6     print('uufun end')

    PyTest/fun.py

    1 import sys
    2 from utils.src.uu import *
    3 
    4 print('fun main')
    5 uufun()
    6 print('fun main end')

    运行fun.py

    1 zry@seupalm:~/PyTest$ python fun.py 
    2 fun main
    3 uufun
    4 /home/zry/PyTest
    5 uufun end
    6 fun main end

    可以看出是成功调用了的

    但如果希望用PyTest/utils/utils.py调用PyTest/utils/src/uu.py,再用PyTest/fun.py调用PyTest/utils/utils.py,则会出现问题

    修改PyTest/utils/utils.py,增加一行:

    1 from src.uu import uufun

    此时运行PyTest/utils/utils.py仍然没有问题

    但修改PyTest/fun.py来调用PyTest/utils/utils.py,import改为:

    1 from utils.utils import *

    1 from utils.utils import printpath

    运行结果都会出现报错

    1 zry@seupalm:~/PyTest$ python fun.py 
    2 Traceback (most recent call last):
    3   File "fun.py", line 2, in <module>
    4     from utils.utils import printpath
    5   File "/home/zry/PyTest/utils/utils.py", line 2, in <module>
    6     from src.uu import uufun
    7 ModuleNotFoundError: No module named 'src'

    此时很显然就是import语句的路径也是根据主调用得来的,所以在二级调用时路径也会出现问题

    如果用比较暴力的解决办法,就将PyTest/utils/utils.py里import的路径变为utils.src.uu,这样就能够二级调用了,而如果是import *的形式,则会将PyTest/utils/utils.py里import得到的PyTest/utils/src/uu.py内函数一并引入主调用。

    但如果遇到不希望改动内层的代码,毕竟对于内层来说,文件目录层级并不是这样的,那么可以怎么办呢?

    由于开发经历仍然有限,我采取的是如下方法:

    此时PyTest是主目录,PyTest/utils是一级目录,PyTest/utils/src是二级目录,首先在一级目录下放置一个__init__.py空文件,这是为了让主目录下的代码import一级目录的内容时,可以将整个一级目录视为一个包,当然import一级目录的内容时首先会运行__init__.py,此处因为是空文件,所以只是让一级目录能够作为包被识别。

    然后在主目录下的函数调用一级目录下的内容时,将一级目录的路径加入系统路径,这样就不用修改一级目录内部的东西了。

    具体是将PyTest/fun.py进行如下修改:

    1 import sys
    2 sys.path.append('utils')
    3 from utils.utils import *
    4 
    5 print('fun main')
    6 uufun()
    7 print('fun main end')

    此时就能够正常运行,并通过一级目录的模块调用到二级目录的内容了。当然,添加路径的语句也可以放在一级目录的__init__.py里,毕竟运行顺序是一样的。

    不过如果后续开发又把主目录当成模块,进行了三级调用呢……这样utils这个路径也是相对路径,是不是仍然需要修改此时的一级目录内容呢……emmmm没有测试过,我觉得还是好好设计好项目结构比较好23333

  • 相关阅读:
    第十二周总结
    第十一周总结
    第十周总结
    人月神话阅读笔记04
    第九周总结
    第八周总结
    人月神话阅读笔记03
    主成分分析(PCA)算法介绍及matlab实现案例
    Cross-entropy
    压缩算法--TP(1978)
  • 原文地址:https://www.cnblogs.com/cenariusxz/p/10618886.html
Copyright © 2020-2023  润新知