Python导入好的做法?

时间:2017-06-30 14:13:11

标签: python import

对于我目前的python项目,我目前在每个脚本的开头添加这行代码:

import os
import sys
current = os.path.abspath(os.path.dirname(__file__))
(__,folder) = os.path.split(current)
while folder != "my_root_folder_name":
    current = os.path.abspath(os.path.join(current, os.path.pardir))
    (__, folder) = os.path.split(current)
sys.path.insert(0, current)

此处 my_root_folder_name 是项目根文件夹的名称。在每个脚本中都有这个允许通过写入(导入脚本的任何地方)从任何其他脚本导入它们中的任何一个:

import subfolder1.subfolder2.thescript

其中子文件夹1 位于根文件夹 my_root_folder_name 中(最终路径为 /my_root_folder_name/subfolder1/subfolder2/thescript.py )。

这是一个好习惯吗?你觉得这个伎俩有什么不利吗?我有什么更好的选择?

编辑:让我的项目组织如下:

  • 在主文件夹中,我有子文件夹 src 数据模型日志配置 ...
  • src 我有一个主脚本'my_project.py'和子子文件夹数据模型功能

我使用此技巧的原因是为了确保我可以从另一个子文件夹导入子文件夹的脚本。如果你不注意,这可能会导致循环导入,但导入的python'..'语法也可以。它还使导入更清晰,因为每次导入都是通过将整个绝对路径写入脚本来完成的。

编辑2: 这段代码不会导入所有其他脚本!! 。它只是通过项目的主文件夹替换脚本的第一个(索引0)sys.path值,该值是文件所在的实际文件夹。

4 个答案:

答案 0 :(得分:4)

一般不会,出于某些原因,这不是一个好习惯。

  1. 您应该只导入每个文件绝对必需的包。导入不必要的包可能会导致性能问题,也可能导致代码混乱。
  2. 您正在为每个文件中的代码添加一个循环,没有真正的必要原因。这不是一个好的做法,取决于你有多少依赖关系会减慢你的速度。
  3. 你需要一个文件位于你的根文件夹中,并使用os路径来获取它,如果你能避免它,通常不是很好的性能。

答案 1 :(得分:1)

是否需要在Python代码本身内编写解决方案?

设置PYTHONPATH=/path/to/module/root完全符合您的要求。例如:

$ find /tmp/py/modules/ -type f
/tmp/py/modules/qwerty/a/b/target.py
/tmp/py/modules/asdf/a/b/source

$ pwd
/tmp/py/modules/asdf

$ python /tmp/py/modules/asdf/a/b/source
Traceback (most recent call last):
  File "/tmp/py/modules/asdf/a/b/source", line 3, in <module>
    import qwerty.a.b.target
ModuleNotFoundError: No module named 'qwerty'

$ export PYTHONPATH=/tmp/py/modules/ 

$ python /tmp/py/modules/asdf/a/b/source
Hello from target

<强> /tmp/py/modules/qwerty/a/b/target.py

#!/usr/bin/env python

print ("Hello from target")

<强>的/ tmp / PY /模块/ ASDF / A / B /来源

#!/usr/bin/env python

import qwerty.a.b.target

解决方案实际上与您正在进行的操作相同,只是在Python代码之外。

请注意,对于Python2,您需要在结构中的每个目录上创建__init__.py个文件(文件可能为空)。 Python3的文档也说它们是必需的,但上面的例子没有它们就可以正常工作。

更新的目录内容如下:

./qwerty/__init__.py
./qwerty/a/__init__.py
./qwerty/a/b/__init__.py
./qwerty/a/b/target.py
./asdf/__init__.py
./asdf/a/__init__.py
./asdf/a/b/__init__.py
./asdf/a/b/source

(对于这个简单的示例,__init__.py下并不严格要求文件asdf,但是因为您希望能够从所有其他脚本导入所有脚本,这就是您在项目中的操作方式)

作为一个补充说明,如果你保留上面的Python代码,我唯一的建议是检查是否到达根文件夹,以免你在移动脚本的情况下进入无限循环{{1} }不是其新位置路径的组件。对于UNIX,我会做这样的事情:

my_root_folder_name

答案 2 :(得分:1)

前段时间我遇到了同样的问题。在我看来,这是一个Python语言的问题,在像import语句这样的简单操作中变得复杂。我做了同样的选择来解决问题,通过sys.path命令更新PYTHONPATH环境变量。我以不同的方式对你做,我编写一个递归文件夹资源管理器并将其放入项目根文件夹中的__init__.py。代码如下:

import os, sys
from pathlib import Path


def recursive_explorer(path):
    sys.path.append(path)
    subfolders = [subfile for subfile in os.listdir(path) if os.path.isdir(path / subfile)]
    for subfolder in subfolders:
        recursive_explorer(path / subfolder)


recursive_explorer(Path(__file__).parent / 'your_src_folder')

我认为我的解决方案很好,因为它是可移植的,你需要写一次。

答案 3 :(得分:1)

您的方法的好处并不能证明成本合理。好处是你可以写

BlurTransformation

到处都是

的某种变体
import subfolder1.subfolder2.thescript

成本是每个阅读代码的人现在必须找出问题的答案,如

  • 如果更改根目录名称会怎样?
  • 根目录是否会丢失?
  • 循环有什么方法可以无法终止?
  • 如果许多模块将相同的文件夹添加到sys.path的头部,是否有任何问题?
  • 我是否考虑过此代码的所有潜在问题?也许我应该问Stack Overflow; - )。

一旦Joe Programmer确信代码没有错误并且没有任何不良影响,他可以不假思索地复制和粘贴它。但是为了避免Joe已经需要了解的略微丑陋的导入语法,是否值得添加5行不那么明显的样板?否。

选择其中一个易于理解的选项:

  • 使用相对导入语法。
  • 使用绝对导入语法 并始终从项目根目录中的脚本运行代码。 Python会自动将主脚本的目录添加到路径中。
  • 使用绝对导入语法 并始终从设置路径的包装器运行您的代码。 这样,任何目录搜索技巧都保存在一个文件中。