【问题标题】:How to write an importing function?如何编写导入函数?
【发布时间】:2019-04-26 11:10:49
【问题描述】:

我正在处理我的一个小组项目,代码的每一部分都非常混乱。我想提高导入的可读性。在我看来,20 行凌乱的导入会损害代码的主要结构。

所以这里是导入的一部分:

import datetime
import os
import json
import re
import psycopg2 as dbapi2
from datetime import datetime
from datetime import date
from flask import Flask, jsonify
from flask import redirect

我想用类似这样的方式导入所有这些:

import importLibs
importLibs()

为此,我尝试了How to make global imports from a function? 的解决方案并想出了这个:

def importLibs():
    globals()['datetime'  ]       = __import__('datetime'         )
    globals()['os'        ]       = __import__('os'               )
    globals()['json'      ]       = __import__('json'             )
    globals()['re'        ]       = __import__('re'               )
    globals()['dbapi2'    ]       = __import__('psycopg2'         )
    globals()['date'      ]       = __import__('datetime'         )
    globals()['Flask'     ]       = __import__('flask.Flask'      )
    globals()['jsonify'   ]       = __import__('flask.jsonify'    )
    globals()['redirect'  ]       = __import__('flask.redirect'   )

但它失败了:

ModuleNotFoundError: No module named 'flask.Flask'

【问题讨论】:

  • “在我看来,20 行凌乱的导入会损害代码的主要结构。”真的没有。
  • 回应上述评论;您对导入列表(许多 IDE 会为您崩溃)的解决方案是添加一个间接层。即使这可行,当您需要引用依赖项时,您也必须首先转到一个完全不同的文件。当我们说这最终会让您的生活更加艰难时,请相信我们。
  • 如果你想提高可读性,使用由对 Python 工作原理有深刻理解的人编写的样式指南,以及社区实际使用的指南不是更好吗?请记住,可读性也来自于遵循已知和已建立的模式。我建议您查看PEP 8,而不是在实施可能与您打算做的完全相反的事情时遇到麻烦。
  • 其实你没事。认为这将是更好的编码约定对我来说是愚蠢的。 IDE 无论如何都会折叠导入段。感谢您的关注。

标签: python import


【解决方案1】:

不要解决不存在的问题。

每个 Python 程序员都已经确切地知道对导入有什么期望。那个 hacky importLibs() 函数只是增加了一层额外的间接性和不必要的复杂性。

您应该遵循 PEP8 中给出的导入标准指南:https://www.python.org/dev/peps/pep-0008/#imports

对于有问题的特定导入,您可能需要按字母顺序排列外部模块并将其与标准库模块分开。除此之外,别管他们了

以下是看起来完全可读(和标准)的轻微清理:

import datetime
import json
import os
import re
from datetime import date

import psycopg2 as dbapi2
from flask import Flask, jsonify, redirect

【讨论】:

  • 请注意(就像 OP 的原始代码一样)您将 datetime moduledatetime.datetime class 重叠。
【解决方案2】:

首先,惯用的 Python(“pythonic”)风格是使用 20 行显式导入语句。如果您不尝试规避正常的导入机制,静态分析工具往往会运行得更好。 PyCharm 等一些 IDE 可以为您分类。

如果不实际运行代码,这样的动态导入更难理解。

也就是说,一些库确实使用像这样的运行时导入。有时您需要导入的内容取决于运行时条件,因此值得学习动态导入的工作原理。

其次,一般不推荐使用 __import__ 内置函数,因为在嵌套模块时它不会为您处理 .。请改用importlib.import_module。但是对于Flask来说,它不是一个模块,而是一个类,所以使用import_module('flask').Flask

第三,模块不可调用。你不能像你建议的那样简单地导入一些东西并调用它。

import importLibs
importLibs()

应该是这样的

from my_imports import importLibs

可以通过改变sys.modules 将任意对象放入导入缓存中,因此您可以通过这种方式直接调用导入。众所周知,模块会在导入时用其他东西替换自己。请负责任地使用,因为如果不实际运行代码,这又很难推理。

第四,globals() 函数引用定义模块的全局变量。不是它被调用的模块。所以你不能只是from my_imports import importLibs 然后期望它把东西导入到当前模块中。他们将进入my_imports 模块。至少有两种方法可以解决这个问题。

一种是在函数中添加某种globals_dict 参数并在其上设置全局变量而不是globals()。然后你会称它为importLibs(globals())。这样,它将使用调用模块中的 globals dict,而不是定义 (my_imports) 模块。

另一种方法是使用inspect 模块向上堆栈帧并找到调用者的模块以获取其全局变量。这样您就不必显式传递它,只需使用importLibs()。但是这种自动魔法更脆弱,如果你以某种方式在不同的模块中间接调用它,它可能会崩溃。


另一种选择是将所有内容导入第三个模块my_imports,在那里提供__all__,然后使用

from my_imports import *

如果你仔细管理你的全局命名空间,你甚至不需要__all__。不鼓励星号导入,因为显式导入更容易静态推理,但有些库确实以这种方式工作。


最后,您可以创建一个具有短名称的模块,将所有内容导入其中,然后通过 . 从任何地方访问内容,例如

import my

my.Flask

同样,有些库是这样工作的,比如 tk。

【讨论】:

  • 我的想法是为 python 导入提供某种“requirements.txt”。它增加了一层干扰,但在我看来它使代码整洁。如果您有 10 个进口商品,我认为最好像这样组织它们。我会像每个人建议的那样研究 pep8 指南。谢谢你的努力:)
【解决方案3】:

警告:您所做的几乎可以肯定是个坏主意。


错误的原因是flask.Flask是一个类,而不是一个模块。

如果你真的必须这样做,你可以试试:

__import__('flask').Flask
__import__('flask').jsonify
__import__('flask').redirect

但是,这严重会损害您代码的可读性。

通过将导入放在模块顶部,您可以立即看到代码正在使用什么。通过将其移动到单独的文件中,您隐藏了代码的依赖项,并使其更难理解发生了什么。

将导入放在文件顶部是我能想到的每种语言的标准做法。想想当有人看到这个时他们会怎么想。他们为什么这样做?这看起来真的很复杂。有什么我想念的吗?为什么他们不像其他人一样只使用 import 语句?

当他们弄清楚你在做什么时,他们已经浪费了 5 分钟的时间。 这是一种干扰

最后,请记住,您编写的代码越复杂,错误潜入的空间就越大!

【讨论】:

  • 嗯,你有一个很好的观点。而且我可以在 IDE 中折叠导入,实际上没有任何问题。更少的代码行并不一定意味着可读的代码。谢谢。
猜你喜欢
  • 1970-01-01
  • 2016-06-15
  • 1970-01-01
  • 1970-01-01
  • 2014-04-12
  • 2015-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多