Relative imports in Python 3

前端 未结 16 1053
误落风尘
误落风尘 2020-11-21 06:42

I want to import a function from another file in the same directory.

Sometimes it works for me with from .mymodule import myfunction but sometimes I get

相关标签:
16条回答
  • 2020-11-21 07:19

    Hopefully, this will be of value to someone out there - I went through half a dozen stackoverflow posts trying to figure out relative imports similar to whats posted above here. I set up everything as suggested but I was still hitting ModuleNotFoundError: No module named 'my_module_name'

    Since I was just developing locally and playing around, I hadn't created/run a setup.py file. I also hadn't apparently set my PYTHONPATH.

    I realized that when I ran my code as I had been when the tests were in the same directory as the module, I couldn't find my module:

    $ python3 test/my_module/module_test.py                                                                                                               2.4.0
    Traceback (most recent call last):
      File "test/my_module/module_test.py", line 6, in <module>
        from my_module.module import *
    ModuleNotFoundError: No module named 'my_module'
    

    However, when I explicitly specified the path things started to work:

    $ PYTHONPATH=. python3 test/my_module/module_test.py                                                                                                  2.4.0
    ...........
    ----------------------------------------------------------------------
    Ran 11 tests in 0.001s
    
    OK
    

    So, in the event that anyone has tried a few suggestions, believes their code is structured correctly and still finds themselves in a similar situation as myself try either of the following if you don't export the current directory to your PYTHONPATH:

    1. Run your code and explicitly include the path like so: $ PYTHONPATH=. python3 test/my_module/module_test.py
    2. To avoid calling PYTHONPATH=., create a setup.py file with contents like the following and run python setup.py development to add packages to the path:
    # setup.py
    from setuptools import setup, find_packages
    
    setup(
        name='sample',
        packages=find_packages()
    )
    
    0 讨论(0)
  • 2020-11-21 07:27

    I ran into this issue. A hack workaround is importing via an if/else block like follows:

    #!/usr/bin/env python3
    #myothermodule
    
    if __name__ == '__main__':
        from mymodule import as_int
    else:
        from .mymodule import as_int
    
    
    # Exported function
    def add(a, b):
        return as_int(a) + as_int(b)
    
    # Test function for module  
    def _test():
        assert add('1', '1') == 2
    
    if __name__ == '__main__':
        _test()
    
    0 讨论(0)
  • 2020-11-21 07:28

    unfortunately, this module needs to be inside the package, and it also needs to be runnable as a script, sometimes. Any idea how I could achieve that?

    It's quite common to have a layout like this...

    main.py
    mypackage/
        __init__.py
        mymodule.py
        myothermodule.py
    

    ...with a mymodule.py like this...

    #!/usr/bin/env python3
    
    # Exported function
    def as_int(a):
        return int(a)
    
    # Test function for module  
    def _test():
        assert as_int('1') == 1
    
    if __name__ == '__main__':
        _test()
    

    ...a myothermodule.py like this...

    #!/usr/bin/env python3
    
    from .mymodule import as_int
    
    # Exported function
    def add(a, b):
        return as_int(a) + as_int(b)
    
    # Test function for module  
    def _test():
        assert add('1', '1') == 2
    
    if __name__ == '__main__':
        _test()
    

    ...and a main.py like this...

    #!/usr/bin/env python3
    
    from mypackage.myothermodule import add
    
    def main():
        print(add('1', '1'))
    
    if __name__ == '__main__':
        main()
    

    ...which works fine when you run main.py or mypackage/mymodule.py, but fails with mypackage/myothermodule.py, due to the relative import...

    from .mymodule import as_int
    

    The way you're supposed to run it is...

    python3 -m mypackage.myothermodule
    

    ...but it's somewhat verbose, and doesn't mix well with a shebang line like #!/usr/bin/env python3.

    The simplest fix for this case, assuming the name mymodule is globally unique, would be to avoid using relative imports, and just use...

    from mymodule import as_int
    

    ...although, if it's not unique, or your package structure is more complex, you'll need to include the directory containing your package directory in PYTHONPATH, and do it like this...

    from mypackage.mymodule import as_int
    

    ...or if you want it to work "out of the box", you can frob the PYTHONPATH in code first with this...

    import sys
    import os
    
    PACKAGE_PARENT = '..'
    SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
    sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))
    
    from mypackage.mymodule import as_int
    

    It's kind of a pain, but there's a clue as to why in an email written by a certain Guido van Rossum...

    I'm -1 on this and on any other proposed twiddlings of the __main__ machinery. The only use case seems to be running scripts that happen to be living inside a module's directory, which I've always seen as an antipattern. To make me change my mind you'd have to convince me that it isn't.

    Whether running scripts inside a package is an antipattern or not is subjective, but personally I find it really useful in a package I have which contains some custom wxPython widgets, so I can run the script for any of the source files to display a wx.Frame containing only that widget for testing purposes.

    0 讨论(0)
  • 2020-11-21 07:28

    if both packages are in your import path (sys.path), and the module/class you want is in example/example.py, then to access the class without relative import try:

    from example.example import fkt
    
    0 讨论(0)
提交回复
热议问题