“导入模块”的编码风格是否优于“从模块导入功能”?

from module import function被称为 FMIF 编码风格。

import module被称为 IM 编码风格。

from package import module被称为 FPIM 编码风格。

为什么 IM + FPIM 被认为是一种比 FMIF 更好的编码风格

下面是一些让我更喜欢 FMIF 而不是 IM 的标准:

  1. 代码短: 它允许我使用较短的函数名,从而有助于坚持每行80列的约定。
  2. 可读性: chisquare(...)看起来比 scipy.stats.stats.chisquare(...)更具可读性。虽然这是一个主观的标准,但我想大多数人都会同意。
  3. 易于重定向: 如果我使用 FMIF,并且由于某种原因在以后的某个时间想要重定向 python,从 alt_module而不是 module定义 function,那么我只需要修改一行: from alt_module import function。如果我要使用 IM,我需要更改许多行代码。
我知道 FPIM 在某种程度上可以抵消前两个问题,但是第三个问题呢?

我对 IM + FPIM 可能比 FMIF 更好的所有原因感兴趣, 但特别是,我有兴趣详细阐述以下几点 这里提到:

即时通讯的优点:

  1. 在测试中容易嘲笑/注入。(我不是很熟悉嘲笑,虽然我最近才知道这个词是什么意思。你能在这里展示一些代码来说明 IM 比 FMIF 更好吗?)
  2. 模块通过重新定义一些条目灵活变更的能力。(我一定是误解了什么,因为这似乎是 FMIF 比 IM 的优势。请看我支持 FMIF 的第三个理由。)
  3. 数据序列化和恢复的可预测和可控行为。(我真的不明白 IM 或 FMIF 的选择如何影响这个问题。请详细说明。)
  4. 我理解 FMIF“污染了我的名称空间”,但除了是一个听起来消极的短语之外,我并不认为这会以任何具体的方式损害代码。
附言。在写这个问题的时候,我收到了一个警告: 这个问题看起来很主观,很可能是封闭的。请不要关门。我不寻求主观意见,而是具体的编码情况下 IM + FPIM 是明显优于 FMIF。

非常感谢。

5176 次浏览

这方面的经典著作,一如往常,来自弗雷德里克 · 伦德,一位效仿者。他的建议是: 始终使用 import——除非你不该这么做。

换句话说,理智一点。就我个人而言,我发现任何有几个模块深度的东西都倾向于通过 from x.y.z import a导入——主要的例子是 Django 模型。但是和其他任何事情一样,这是一个风格问题,您应该有一个一致的风格——特别是对于像 datetime这样的模块,其中模块和它包含的类被称为同一个东西。你需要写 datetime.datetime.now()还是只写 datetime.now()?(在我的代码中,总是前者。)

问题清单中的第1和第2项似乎是同一个问题。Python 的动态特性意味着,无论使用哪种方法,替换模块名称空间中的项都相当简单。如果模块中的一个函数引用了另一个函数,就会出现困难,这就是您想要模拟的函数。在这种情况下,导入模块而不是函数意味着您可以执行 module.function_to_replace = myreplacementfunc并且一切工作都是透明的——但是通过 FPIM 和通过 IM 一样容易。

我也不明白第三条和这件事有什么关系。但是我认为你的第四条是基于一个小小的误会。您提供的任何方法都不会“污染您的名称空间”。是的所做的就是 from module import *,在这里你根本不知道你要导入什么,所以函数可以出现在你的代码中,而不会给读者它们来自哪里的线索。这太可怕了,应该不惜一切代价避免。

您列出的 IM/FPIM 否定句通常可以通过适当使用 as子句得到改进。from some.package import mymodulewithalongname as mymod可以有效地缩短代码并增强其可读性,如果您明天将 mymodulewithalongname重命名为 somethingcompletelydifferent,则可以将 as子句用作单个语句进行编辑。

考虑你的亲 FMIF 点3(称它为 R 的重定向)与你的亲 FPIM 点2(称它为 F 的灵活性) : R 等于促进模块边界完整性的丧失,而 F 加强了它。模块中的多个函数、类和变量通常是为了一起工作: 它们不应该独立地切换到不同的含义。例如,考虑模块 random及其函数 seeduniform: 如果您要将其中一个模块的导入切换到另一个模块,那么您将中断对 seed的调用和对 uniform的调用结果之间的正常连接。当一个模块设计良好,具有内聚性和完整性时,R 对打破模块边界的促进作用实际上是负面的——它使得做一些比 没有做得更好的事情变得更容易。

反之亦然,F 支持 协调切换耦合函数、类和变量(因此,通常通过模块化将 属于组合在一起)。例如,为了使测试可重复(FPIM 支持点1) ,你在 random模块中同时模仿 seedrandom,如果你的代码遵循 FPIM,那么你就已经设置好了,协调性得到了保证; 但是如果你有直接导入函数的代码,你就必须找到每个这样的模块,然后一遍又一遍地重复模仿。要使测试完全可重复,通常还需要对日期和时间函数进行“协调模拟”——如果在某些模块中使用 from datetime import datetime,则需要查找并模拟所有的测试(以及所有执行 from time import time的测试,等等) ,以确保当系统的各个部分询问“现在是什么时间?”是完全一致的(如果您使用 FPIM,您只需要模仿两个相关的模块)。

我喜欢 FPIM,因为使用 繁殖限定名比使用单个限定名真的没有多少附加值(而 barename 和限定名之间的区别是 巨大——使用限定名,无论是单名还是乘法名,都比使用 barename 可以获得更多的控制权!).

啊,好吧,不能把所有的工作时间都用来回答你的每一个观点——你的问题可能应该是半打问题; ——)。我希望这至少能解决“为什么 F 比 R 更好”以及一些嘲弄/测试问题——归结为 保持和增强良好设计的模块性(通过 F)而不是破坏它(通过 R)。

像 Alex Martelli 一样,我喜欢在导入函数时使用 as

我所做的一件事就是对从同一个模块导入的所有函数使用一些前缀:

from random import seed as r_seed
from random import random as r_random

r_seedrandom.seed短,但是在一定程度上保留了模块边界。有人随意地查看您的代码可以看到 r_seed()r_random(),并有机会了解到它们是相关的。

当然,你可以简单地这样做:

import random as r

然后使用 r.random()r.seed(),这可能是这种情况下理想的折衷方案。我只在从模块导入一两个函数时使用前缀技巧。当我想使用来自同一个模块的许多函数时,我将导入该模块,可能使用一个 as来缩短名称。

这里有很好的答案(我对所有答案都投了赞成票) ,以下是我对这个问题的看法:

首先,解决每一个问题:

(据称) FMIF 的优点:

  • 代码短: 较短的函数名有助于每行80列。

也许吧,但是模块名通常足够短,所以这不相关。当然,有 datetime,但也有 osresys等。而且 Python 在 { [ (中有免费的换行。对于嵌套模块,IM 和 FPIM 中总是有 as

  • 可读性: chissquare (...)似乎比 scypy.stats.stats.chissquare (...)更具可读性。

强烈反对。当阅读外国代码(或者几个月后我自己的代码)时,很难知道每个函数是从哪里来的。限定名可以避免在第2345行与模块声明头之间来回切换。它也给你 背景: “ ABC0? 那是什么? 哦,来自 scypy? 好吧,一些数学相关的东西”。同样,你也可以使用 scipy.stats.stats as scypyst的缩写。scypyst.chisquare(...)足够简短,并且具有限定名称的所有优点。

import os.path as osp是另一个很好的例子,考虑到在一个调用中链接3个或更多的函数是非常常见的: join (expanduser () ,basename (splitext ())等。

  • 易于重定向: 从 altmodule 而不是 module 单行重定义函数。

多久重新定义一个函数而不是整个模块?模块边界和功能协调应该得到保留,Alex 已经深入地解释了这一点。对大多数人来说在真实世界的场景中,如果 alt_module.xmodule.x的可行替代品,那么 alt_module本身可能是 module的替代品,所以 IM 和 FPIM 都是一行程序,就像 FMIF 一样,只要您使用 as

  • 我知道 FPIM 在某种程度上抵消了前两个问题..

实际上,as缓解了前两个问题(和第三个问题) ,而不是 FPIM。您也可以使用 IM: import some.long.package.path.x as x与 FPIM 获得相同的结果。


所以以上这些都不是 FMIF 真正的优点。我更喜欢 IM/FPIM 的原因是:

为了简单和一致性起见,当我导入一些东西时,不管是 IM 还是 FPIM,我总是导入一个 模组,而不是从模块中导入一个对象。请记住,FMIF 可以(ab -)用于导入函数、类、变量,甚至其他模块!想想 from somemodule import sys, somevar, os, SomeClass, datetime, someFunc的混乱。

此外,如果您想从一个模块中获得多个对象,那么 FMIF 将比 IM 或 FPIM 对您的名称空间造成更大的污染,无论您想使用多少个对象,它们都将使用单个名称。这样的对象将有一个 合格名称,这是一个赞成,而不是反对意见: 正如我在第2期中所说的,恕我直言,它的 好转了可读性。

这一切都归结为一致性、简单性、组织性。 “导入模块,而不是对象”是一个很好的、容易坚持的思维模式。

在这里,我最同意 MestreLion 的观点(因此是一个优势)。

我的观点是: 我经常查看我不熟悉的代码,仅仅通过查看函数就不知道函数的模块是什么,这非常令人沮丧。

代码只写一次,而且要读很多次,因此可读性和可维护性胜过易于输入。

类似地,典型的代码是为了编码者的利益而编写的 没有,但是为了另一个实体的利益。

对于比您更了解 python、但对代码不熟悉的人来说,您的代码应该是可读的。

全路径导入还可以更好地帮助 IDE 指向正确的函数或对象源。

由于所有这些原因以及 MestreLion 指出的原因,我得出结论,导入和使用完整路径是最佳实践。