【问题标题】:Python - command line argument extracted as a string ImportErrorPython - 命令行参数提取为字符串 ImportError
【发布时间】:2019-12-24 10:29:42
【问题描述】:

我正在尝试执行以下操作:

在 CMD 中:

python __main__.py 127.0.0.1

ma​​in.py中的:

address = sys.argv[1]

然后在我的 config.py 文件中,我像这样导入地址:

from __main__.py import address

...

EXAMPLE_URL = f"http://{address}/login"

我在一个简单的测试场景中使用 URL,它是从配置导入的,我收到以下错误:

ImportError: cannot import name 'address' from '__main__' (__main__.py)

这是我的目录结构:

QA System/
├── config/
│   ├── config.py
│   ├── __init__.py
├── .... some other unneccessary stuff
└── tests/
    ├── test_scenarios
       ├── test_scenario_01.py
       ├── test_scenario_02.py
       ├── __init__.py
    |── test_suite.py
    |── __init__.py
|
|-- __main__.py   < --- I launch tests from here
|-- __init__.py

似乎错误在导入期间的配置文件中,但我不明白错误在哪里。提前致谢!

.py文件:

import argparse
import sys

from tests.test_suite import runner

if __name__ == "__main__":
    address = str(sys.argv[1])
    runner()   # This runs the tests, and the tests also use config.py for
                 various settings, I am worried something happens with the
                 imports there.

【问题讨论】:

  • 是在 main.py 中定义的地址吗??
  • 是的,对不起,我编辑了我的帖子 - 我一开始使用了 file.py,最后忘记重新编辑了。
  • 变量地址是否在顶级范围内?
  • 你在哪里初始化address?它是在函数内部还是全局的?
  • 我已经发布了我的 main.py。我在代码的 if name__=="__main" 部分初始化它。但是,我担心它可能会发生,因为 runner() 位为测试导入配置,然后从 main 导入配置,所以它变得像循环导入。

标签: python import command-line-arguments importerror argv


【解决方案1】:

我认为你在这里有一个循环依赖。您的 __main__ 没有直接导入 config 但我猜跑步者是。基本上,如果你在点击address = str(sys.argv[1]) 行之前导入config,你就会遇到这个问题。

我认为更好的方法是使用这样的结构:

config.py

URL_TEMPLATE = ""http://{}/login"

然后在__main__.py

from config import URL_TEMPLATE

url = URL_TEMPLATE.format(address)

这仅保留配置中的静态内容,而动态运行时生成的最终 url 在您的主目录中

【讨论】:

    【解决方案2】:

    你有一个circular import

    当您在 Python 中导入模块时,例如,import __main__(如您的示例中所示),会为模块的命名空间创建一个 module 对象,该命名空间最初为空。然后,随着模块主体中的代码被执行——分配的变量、定义的函数和类等,命名空间被按顺序填充。例如。采取以下脚本:

    $ cat a.py
    print(locals())
    an_int = 1
    
    print("after an_int = 1")
    print(locals())
    
    def a_func(): pass
    print("after def a_func(): pass")
    print(locals())
    

    然后运行它:

    $ python a.py
    {'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__file__': 'a.py', '__doc__': None, '__package__': None}
    after an_int = 1
    {'an_int': 1, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'a.py', '__package__': None, '__name__': '__main__', '__doc__': None}
    after def a_func(): pass
    {'an_int': 1, 'a_func': <function a_func at 0x6ffffeed758>, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'a.py', '__package__': None, '__name__': '__main__', '__doc__': None}
    

    您可以看到命名空间被逐行填充。

    现在说我们修改它:

    $ cat a.py
    print(locals())
    an_int = 1
    
    print("after an_int = 1")
    print(locals())
    
    import b
    print("after import b")
    
    def a_func(): pass
    print("after def a_func(): pass")
    print(locals())
    

    并添加b.py:

    $ cat b.py
    import sys
    print('a is in progress of being imported:', sys.modules['a'])
    print("is a_func defined in a? `'a_func' in sys.modules['a'].__dict__`:",
          'a_func' in sys.modules['a'].__dict__)
    from a import a_func
    

    然后像这样运行它:

    python -c 'import a'
    

    你会得到一些以 Traceback 结尾的输出:

    ...
    after an_int = 1
    {'an_int': 1, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'a.py', '__package__': None, '__name__': '__main__', '__doc__': None}
    a is in progress of being imported: <module 'a' from '/path/to/a.py'>
    is a_func defined in a? `'a_func' in sys.modules['a'].__dict__`: False
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "a.py", line 7, in <module>
        import b
      File "b.py", line 1, in <module>
        from a import a_func
    ImportError: cannot import name 'a_func'
    

    如果您将import b 移动到a_func 的定义之后:

    $ cat a.py
    print(locals())
    an_int = 1
    
    print("after an_int = 1")
    print(locals())
    
    def a_func(): pass
    print("after def a_func(): pass")
    print(locals())
    
    import b
    print("after import b")
    

    再次运行python -c 'import a',你会看到它工作正常,输出以“after import b”结尾。

    奖励问题:为什么我运行python -c 'import a' 而不仅仅是python a.py?如果您尝试后者,之前的版本实际上会工作,并且会出现两次导入a.py。这是因为当您运行python somemodule.py 时,它最初不是作为somemodule 导入的,而是作为__main__ 导入的。所以从导入系统的角度来看,a 模块在运行from a import a_func 时还没有被导入。一个非常令人困惑的警告。


    所以如果你有类似__main__.py的东西:

    import config
    address = 1
    

    config.py:

    from __main__ import address
    

    当您运行python __main__.py 时,当它运行import config 时,address 尚未分配,因此config 中尝试从__main__ 导入address 的代码导致@ 987654353@.

    在您的情况下,它有点复杂,因为您没有直接在 __main__ 中导入 config,但间接情况仍然如此。

    在这种情况下,您不应该通过 import 语句在模块之间传递变量。事实上,__main__ 应该只是您代码的命令行前端,而您的其余代码应该能够独立于它工作(例如,一个好的设计将允许您运行 from tests.test_runner import runner 并调用runner() 原则上来自交互式 Python 提示符,即使您从未真正以这种方式使用它。

    所以改为让runner(...) 为它需要的任何选项提供参数。然后__main__.py 只会从命令行参数中获取这些参数。例如:

    def runner(address=None):
        # Or maybe just runner(address) if you don't want to make the
        # address argument optional
    

    然后

    if __name__ == '__main__':
        address = sys.argv[1]  # Use argparse instead if you can
        runner(address=address)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-23
      • 2015-12-26
      • 1970-01-01
      • 1970-01-01
      • 2016-10-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多