顶级 __init__.py 导入的最佳实践

tho*_*len 15 python python-import python-3.x

当包变得很大时,可能很难记住东西在哪里,并且我们想要的对象的点路径可能很麻烦。作者似乎解决这个问题的一种方法是将“最好的”对象的引用带到顶部,尽管这些代码实际上可以存在于下面的几个包级别中。

这让人可以说:

from pandas import wide_to_long
Run Code Online (Sandbox Code Playgroud)

代替

from pandas.core.reshape.melt import wide_to_long
Run Code Online (Sandbox Code Playgroud)

但是这样做的来龙去脉以及该方法的最佳实践是什么?不加载__init__.py带有许多导入的顶部(为了使它们在顶层可用)是否意味着包的单个对象的任何导入突然会占用比所需更多的内存 - 因为 中提到的所有内容都会自动__init__.py加载?

然而,包却可以做到这一点。例如,看看可以从顶层numpy或底层导入什么(可以在这个要点pandas中找到运行您自己的诊断的代码)。

$ python print_top_level_diagnosis.py numpy
--------- numpy ---------
599 objects can be imported from top level numpy:
  19 modules
  300 functions
  104 types

depth   count
0   162
1   406
2   2
3   29
4   1

$ python print_top_level_diagnosis.py pandas
--------- pandas ---------
115 objects can be imported from top level pandas:
  12 modules
  55 functions
  40 types

depth   count
0   12
3   37
4   65
5   1

Run Code Online (Sandbox Code Playgroud)

Gij*_*ben 0

所有的方法都有优点和缺点,__init__.py你几乎可以做你想做的事。话虽如此,一个重要的指导原则是__init__.py永远不应该有任何“副作用”。这意味着它除了声明命名空间之外不应该执行任何其他操作。副作用确实会让库的用户感到困惑,甚至成为安全风险。

__init__.py名为“mylibrary”的模块中的示例:

import requests

result = requests.get("URL TO SOME VIRUS")
...
Run Code Online (Sandbox Code Playgroud)

在此示例中,运行import mylibrary实际上会下载某些内容(或发送信息)。

如果您__init__.py没有任何副作用,则有一些注意事项。首先是加载时间。每次导入库时,Python 都会遍历并运行(!)__init__.py它能找到的所有文件。这意味着,如果您的文件包含缓慢的操作,例如加载大文件或大量外部依赖项,则您的库的加载实际上可能会变得非常慢。您可以通过将昂贵的调用包装在函数中并公开该函数来解决此问题,但这仍然是需要考虑的问题。

另一件需要考虑的事情是库的版本控制。如果将文件移动到其他文件夹,您的所有用户或您自己是否需要更新所有导入语句?如果您查看该pandas库,您会发现它们创建了包含所有公共函数/类/等的api子模块(例如)。pandas.core.api所有导入都是从api子模块完成的,这确保了更改后,只有 1 个位置需要更新导入语句,并且一切都将继续工作。