如何设置多包Python应用程序?

时间:2016-03-15 12:20:32

标签: python

我的第一个Python应用程序相当小,所有代码都在一个目录中。所有模块都只是相互“导入local_module”。在主脚本上执行“chmod + x”并运行应用程序很容易。

但是现在我正在创建一个更大的命令行驱动的应用程序,我期望它将遇到数万行代码。所以我想将代码分散到各种包中。此应用程序仅在内部运行。目前我们仍在使用Python 2.6.6。看起来有几种方法来构建事物:

  1. 我已经通过让主脚本执行该应用程序了:

     import __future__ import absolute_import
    

    然后通过以下方式调用它:

     python -m main_dir.sub_dir.main_script
    
  2. 看起来我也可以更改Python路径环境变量,这样我就可以调用main_script.py,或者通过主脚本中的某些内容来调用:

     sys.path.insert(0, os.path.join(THIS_DIR,'..'))
    
  3. 我觉得我没有足够的理解来判断设置多包应用程序和使用它的最佳方法是什么。我一直在进行各种谷歌搜索,并发现了很多关于如何运行的参考资料,这些参考资料似乎属于上面列出的两种基本方法。但是我没有找到很多设置50,000行Python应用程序。

    更新

    凯文,谢谢你的回答。它有助于提高我对Python中包的理解,但我仍然有点困惑。

    我创建了这个目录结构:
        my_app应用
          \ sub_package1
          \ sub_package2

    在所有三个目录中,我创建了一个空的__init__.py文件。

    在my_app目录中,我创建了my_main.py:

    import sys
    
    import sub_package1.sub1
    import sub_package1.sub11
    
    import at_main_level
    import at_main_level_also
    
    def _my_print (someString):
      sys.stdout.write("{0}\n".format(someString))
    
    if __name__ == '__main__':
      _my_print ("Learning about Python packages and applications.")
      x = 3
      doubleX = at_main_level.double_number(x)
      _my_print ("Double of {0} is {1}".format(x, doubleX))
      doubleDoubleX = at_main_level_also.double_double_number(x)
      _my_print ("Double Double of {0} is {1}".format(x, doubleDoubleX))
      xMinus1 = sub_package1.sub1.subtract_1(x)
      _my_print ("{0}-1 is {1}".format(x, xMinus1))
      xMinus1Twice = sub_package1.sub11.subtract1_twice(x)
      _my_print ("{0}-2 is {1}".format(x, xMinus1Twice))
    

    同样在my_app中,我使用:

    创建了at_main_level.py
    def double_number(x):
      return 2 *x
    

    最后在my_app中我创建了at_main_level_also.py:

    import at_main_level
    
    def double_double_number(x):
      return at_main_level.double_number(at_main_level.double_number(x))
    

    然后在sub_package1中创建了sub1.py:

    def subtract_1 (x):
      return x - 1
    

    和sub11.py:

    import sub1
    
    def subtract1_twice(x):
      return sub1.subtract_1(sub1.subtract_1(x))
    

    现在,当我运行my_main.py时,我得到了对我有意义的结果:

    Learning about Python packages and applications.
    Double of 3 is 6
    Double Double of 3 is 12
    3-1 is 2
    3-2 is 1
    

    所以我可以编写代码:

    1)一个模块在顶层的另一个模块中使用代码 2)顶级模块可以使用子包中的代码,子包中的代码可以调用子包中另一个模块中定义的函数。

    但是在sub_package2中我创建了sub2.py:

    from ..sub_package1 import sub1
    
    def subtract_2 (x):
      return sub1(sub1(x))
    

    添加到my_main.py

    import sub_package2.sub2 
    

    并在我的主要功能结束时:

    xMinus2 = sub_package2.sub2.subtract_2(x)
    _my_print ("{0}-1 is {1}".format(x, xMinus2))
    

    运行my_main时我得到以下内容:

    [my_app]$ python my_main.py 
    Traceback (most recent call last):
      File "./my_main.py", line 8, in <module>
        import sub_package2.sub2
      File "/home/hcate/work/temp/my_app/sub_package2/sub2.py", line 2, in <module>
        from ..sub_package1 import sub1
    ValueError: Attempted relative import beyond toplevel package
    

    所以我还不知道如何创建一个应用程序,其中一个包中的代码可以使用另一个包中的代码。

    我应该做些什么不同?

    感谢。

1 个答案:

答案 0 :(得分:4)

创建多个包实际上与正确创建单个包 并不相同。

不要混淆“您的代码所在的目录”和“Python包的目录”。也就是说,你应该有一个所有软件包和模块的目录 - 让我们说它是/users/henry/myappcode/目录中,可能存在用于组织代码的Python包的目录;区别在于后面的目录总是出现在导入中(当你使用绝对导入时),而前者永远不会出现。只要你这样做,你就不需要弄乱sys.path

以下是如何使用正确的路径启动您的应用,以便导入工作:

  1. 入门的简单版本。你有这样的文件:

    my_app_main.py
    my_app/__init__.py
    my_app/module1.py
    my_app/module2.py
    my_app/subpackage/...
    my_app/...
    

    你可以从任何目录运行它,具有绝对路径或相对路径:

    python /users/henry/myappcode/my_app.py
    

    当您使用Python脚本的路径调用Python时,Python会自动将该脚本的位置(而不是当前目录)放在sys.path上,因此所有{{} 1}}文件将自动为.py,依此类推。

  2. 上面的缺点是它有一个“主脚本”,它与模块不同,与其他代码不在同一个包中。您已经知道import my_app.module,因此在这种情况下使其工作的方法是:

    python -m

    如上所述,您可以创建PYTHONPATH=/users/henry/myappcode python -m my_app.main 而不是my_app/main.py

  3. 选项2很难看。实现它的方法是让Python包可以安装(my_app_main.py)。使用setup.py选项,您可以自动创建一个与上述命令等效的shell脚本,但用户只需在安装后键入entry_points即可。

    如果你想进行开发而不必每次都运行my_app,但是有一个方便的命令,那么只需创建自己的shell脚本指向你的代码。

    python setup.py install
  4.   

    在my_app目录中,我创建了my_main.py:

    #!/bin/sh
    PYTHONPATH=/users/henry/myappcode exec python -m my_app.main "$@"
    

    如果执行'python my_app / my_main.py',则设置Python的路径以使my_app 不是包,它是包含顶级包的目录。这就是为什么您之后的亲戚导入失败了import sub_package1.sub1 :您已安排relative import beyond toplevel package 成为顶级套餐。

    以下是一般规则:您不得在命令行上命名包目录。您可以使用sub_package1(上面的选项2和3)命名,也可以调用不在包中的脚本(选项1)。

    您的相对导入 是正确的,但它失败了,因为Python没有将-m看作是一个包。确保您遵循选项1(主要脚本不是my_app内)或选项2(主要使用my_app启动)。