【问题标题】:Python: How to import a function from a package that depends on other functions in that package?Python:如何从依赖于该包中其他函数的包中导入函数?
【发布时间】:2021-01-22 22:39:35
【问题描述】:

考虑以下场景:

test_setup/
├── __init__.py
├── helper.py
└── main.py

helper.py 是:

def helper_func():
    print('helping')

main.py 是:

from helper import helper_func

def main_func():
    helper_func()

if __name__ == '__main__':
    main_func()

__init__.py 是:

from .main import main_func

我希望能够做两件事:

1.从包中运行main。这行得通。

2.import main_func 在此包外使用。这行不通。从test_setup的父目录调用时, from test_setup import main_func 产量:

In [1]: from test_setup import main_func
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-1-c395483f58c8> in <module>
----> 1 from test_setup import main_func

~/PycharmProjects/test_setup/__init__.py in <module>
      1 #from .helper import helper_func
----> 2 from .main import main_func

~/PycharmProjects/test_setup/main.py in <module>
----> 1 from helper import helper_func
      2 
      3 def main_func():
      4     helper_func()
      5 

ModuleNotFoundError: No module named 'helper'

如果我将 main.py 中的第一行更改为相对导入 from .helper import helper_func 可以工作,但是当我尝试从包内运行时失败(上面的目标 1)产生:

Traceback (most recent call last):
  File "/Users/xxx/PycharmProjects/test_setup/main.py", line 1, in <module>
    from .helper import helper_func
ImportError: attempted relative import with no known parent package

这里发生了什么,我该如何解决问题以实现目标 1 和 2?尝试在__init__.py 中导入helper_func 也无济于事。 以下也失败了:

In [1]: from test_setup.main import main_func
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-1-764832db473f> in <module>
----> 1 from test_setup.main import main_func

~/PycharmProjects/test_setup/__init__.py in <module>
----> 1 from .main import main_func

~/PycharmProjects/test_setup/main.py in <module>
----> 1 from helper import helper_func
      2 
      3 def main_func():
      4     helper_func()
      5 

ModuleNotFoundError: No module named 'helper'

【问题讨论】:

    标签: python import package


    【解决方案1】:

    直接导入函数应该是:from test_setup.main import main_func。但是,您可以从模块中使用它。

    import test_setup
    
    test_setup.main_func()
    

    在编辑时,您错过了 from .helper import helper_func 上的 . 以指示相对导入。

    进一步查看目标 1 问题和this great post you should read,Guido 本人建议不要在模块内运行脚本,而不是作为模块运行。

    import docs 说:

    请注意,相对导入基于当前模块的名称。由于主模块的名称始终为“ma​​in”,因此用作 Python 应用程序主模块的模块必须始终使用绝对导入。

    这意味着脚本(你可以独立于包运行的东西)不应该在可能的情况下从内部运行。如果需要保留当前结构,可以使用 python -m test_setup.main 将 main.py 文件作为模块运行,这将与相对导入一起使用。

    另一种选择是将父文件夹添加到 test_setup 文件夹到 PYTHONPATH,这将允许从任何地方进行绝对导入,例如 test_setup.main.main_func,尽管有 good threads 建议您在构建包时应该使用 proper development tools

    【讨论】:

    • 此方法适用于从包外运行 main_func test_setup 但在从包内运行 main 时会失败。
    【解决方案2】:

    不知道您使用的是哪个版本,但这可能会对您有所帮助...

    __init__.py:

    # 1. run main from within the package
    
    # Python2
    '''
    >>> from main import main_func
    main: from helper import helper_func
    >>> main_func()
    helping
    '''
    
    # Python3
    '''
    >>> from main import main_func
    main: from helper import helper_func
    >>> main_func()
    helping
    '''
    
    # XXX: Notice that both of the above are the same
    # However, note the differences below
    
    # 2. import main_func for use outside this package
    
    # Python2
    '''
    >>> from test_setup import main_func
    test_setup.main: from helper import helper_func
    test_setup: from main import main_func
    >>> main_func()
    helping
    '''
    
    # Python3
    '''
    >>> from test_setup import main_func
    # Failed
    ImportError:0 No module named 'main' in module test_setup
    ImportError:0 No module named 'helper' in module test_setup.main
    # Succeeded
    test_setup.main: from .helper import helper_func
    test_setup: from .main import main_func
    >>> main_func()
    helping
    '''
    
    try:
        from main import main_func
        print("%s: from main import main_func"%(__name__))
    except ImportError as exc:
        print("ImportError:0 %s in module %s"%(exc,__name__))
        try:
            from .main import main_func
            print("%s: from .main import main_func"%(__name__))
        except ImportError as exc:
            print("ImportError:1 %s in module %s"%(exc,__name__))
            from test_setup.main import main_func
            print("%s: from test_setup.main import main_func"%(__name__))
    

    main.py:

    try:
        from helper import helper_func
        print("%s: from helper import helper_func"%(__name__))
    except ImportError as exc:
        print("ImportError:0 %s in module %s"%(exc,__name__))
        try:
            from .helper import helper_func
            print("%s: from .helper import helper_func"%(__name__))
        except ImportError as exc:
            print("ImportError:1 %s in module %s"%(exc,__name__))
            from test_setup.helper import helper_func
            print("%s from test_setup.helper import helper_func"%(__name__))
    
    
    def main_func():
        helper_func()
    
    if __name__ == '__main__':
        main_func()
    

    为澄清而编辑:

    __init__.py:

    """Init for main module"""
    
    print("__init__.__doc__ = %s"%(__doc__))
    
    # -----------------------------------------------------------------------------
    from os import path
    import sys
    
    print("sys.executable = %s"%(sys.executable))
    print("sys.argv[0] = %s"%(sys.argv[0]))
    
    main_prg_path = path.abspath(path.dirname(__file__))
    print("main_prg_path = %s"%(main_prg_path))
    
    if getattr(sys, 'frozen', False):
        # When being bundled by pyinstaller, paths are different
        print("Running as pyinstaller bundle!", sys.argv[0])
        main_prg_path = path.abspath(path.dirname(sys.argv[0]))
    
    sys.path.append(main_prg_path)
    print("main_prg_path = %s"%(main_prg_path))
    # Append other directories needed for main program
    #sys.path.append(os.path.join(main_prg_path, 'utils'))
    
    # -----------------------------------------------------------------------------
    from main import main_func
    

    main.py:

    """Main module"""
    
    print("main.__doc__ = %s"%(__doc__))
    
    # -----------------------------------------------------------------------------
    # Running in python
    # inside:  from main import main_func
    # outside: from test_setup import main_func
    
    # Running in terminal
    # inside:  python -m main
    # outside: python -m test_setup/main
    # -----------------------------------------------------------------------------
    from os import path
    import sys
    
    print("sys.executable = %s"%(sys.executable))
    print("sys.argv[0] = %s"%(sys.argv[0]))
    
    main_prg_path = path.abspath(path.dirname(__file__))
    print("main_prg_path = %s"%(main_prg_path))
    
    if getattr(sys, 'frozen', False):
        # When being bundled by pyinstaller, paths are different
        print("Running as pyinstaller bundle!", sys.argv[0])
        main_prg_path = path.abspath(path.dirname(sys.argv[0]))
    
    sys.path.append(main_prg_path)
    print("main_prg_path = %s"%(main_prg_path))
    # Append other directories needed for main program
    #sys.path.append(os.path.join(main_prg_path, 'utils'))
    
    # -----------------------------------------------------------------------------
    from helper import helper_func
    
    
    def main_func():
        helper_func()
    
    # -----------------------------------------------------------------------------
    if __name__ == '__main__':
        print("running as main: %s"%(__name__))
        main_func()
    else:
        print("running as file: %s"%(__file__))
        main_func()
    

    helper.py:

    """Helper module"""
    
    print("helper.__doc__ = %s"%(__doc__))
    
    __author__ = "Your Name"
    __author_email__ = "Your Email"
    __version__ = "0.0.1"
    __date__ = "24 Jan 2021"
    
    def helper_func():
        print("Author = %s\nEmail = %s\nVersion = %s\nDate = %s"%(
            __author__, __author_email__, __version__, __date__))
    

    @rhz 编辑:添加更改以显示正在发生的事情和sys.path.append 的简单用法示例

    【讨论】:

    • 相当混乱。我正在使用python 3.9。在外部使用 #: from test_setup import main_func
    • 然后改变这个:外部:python -m test_setup/mainoutside: python -m test_setup.main(对于 python3)用于终端或使用 from test_setup import main_func 用于 python 解释器。有打印语句来显示每种方式发生的情况,但是是的,它看起来确实令人困惑。当我回到电脑前,我会更新以解释发生了什么。
    猜你喜欢
    • 2013-01-26
    • 2013-11-30
    • 2018-06-05
    • 2023-04-03
    • 1970-01-01
    • 2021-04-27
    • 2020-08-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多