71

我们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 以定义functionfromalt_module而不是module我只需要更改一行:from alt_module import function. 如果我要使用 IM,我需要更改很多行代码。
我意识到 FPIM 在某种程度上消除了前两个问题,但第三个问题呢?

我对 IM+FPIM 可能优于 FMIF 的所有原因感兴趣,但特别是,我有兴趣详细阐述此处提到的以下几点:

IM 的优点:

  1. 易于在测试中模拟/注入。(我对模拟不是很熟悉,虽然我最近知道了这个术语的含义。你能在这里展示一下 IM 比 FMIF 更好的代码吗?)
  2. 通过重新定义一些条目来灵活更改模块的能力。(我一定是误解了一些东西,因为这似乎是 FMIF 优于 IM 的优势。请参阅上面我支持 FMIF 的第三个原因。)
  3. 数据序列化和恢复的可预测和可控行为。(我真的不明白 IM 或 FMIF 的选择如何影响这个问题。请详细说明。)
  4. 我知道 FMIF “污染了我的命名空间”,但除了听起来是负面的短语之外,我不理解这如何以任何具体方式伤害代码。
PS。在写这个问题时,我收到一个警告,这个问题看起来很主观,很可能会被关闭。请不要关闭它。我不是在寻找主观意见,而是在寻找 IM+FPIM 明显优于 FMIF 的具体编码情况。

非常感谢。

4

5 回答 5

47

您为 IM/FPIM 列出的否定通常可以通过适当使用as子句来改善。 from some.package import mymodulewithalongname as mymod可以有用地缩短你的代码并增强它的可读性,如果你重命名mymodulewithalongnamesomethingcompletelydifferent明天,该as子句可以作为单个语句来编辑。

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

反之亦然,F 能够协调切换耦合函数、类和变量(因此,通常,通过模块化,属于一起的实体)。例如,为了使测试可重复(FPIM 支持点 1),您可以在模块中进行模拟seed,如果您的代码遵循 FPIM,则一切就绪,保证协调;但是如果你有直接导入函数的代码,你必须寻找每个这样的模块并一遍又一遍地重复模拟。使测试完全可重复通常还需要日期和时间函数的“协调模拟”——如果你在某些模块中使用,你需要找到并模拟它们(以及所有那些做randomrandomfrom datetime import datetimefrom time import time等)以确保当系统的各个部分询问“现在几点了?”时收到的所有时间。完全一致(如果您使用 FPIM,您只需模拟两个相关模块)。

我喜欢 FPIM,因为使用多重限定名称而不是单一限定名称实际上并没有太多附加值(虽然裸名称和限定名称之间的差异很大——你可以通过限定名称获得更多控制权,无论是单独或倍增,这比使用裸名所能做到的多!)。

啊,好吧,不能把所有的工作日都花在回答你的每一个观点上——你的问题可能应该是六个问题;-)。我希望这至少解决了“为什么 F 比 R 更好”和一些模拟/测试问题——归结为保留和增强精心设计的模块化(通过 F)而不是破坏它(通过 R)。

于 2009-11-16T19:49:15.377 回答
18

这方面的经典著作通常来自 effbot 的 Fredrik Lundh。他的建议:总是使用 import - 除非你不应该这样做。

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

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

我也不明白第 3 项与任何事情有什么关系。但是,我认为您的第 4 项是基于一些误解。您提供的任何方法都不会“污染您的命名空间”。这样做的原因是,您from module import *根本不知道要导入什么,因此函数可能会出现在您的代码中,而不会向读者提供它们来自何处的线索。这太可怕了,应该不惜一切代价避免。

于 2009-11-16T19:32:11.213 回答
6

这里有很好的答案(我都赞成),这是我对这个问题的看法:

首先,解决您的每个项目符号:

(据称)FMIF 的优点:

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

也许,但模块名称通常足够短,所以这无关紧要。当然,还有datetime, os,resys。Python 内部有自由换行符{ [ (。对于嵌套模块,asIM 和 FPIM 中始终存在

  • 可读性:chisquare(...) 看起来比 scipy.stats.stats.chisquare(...) 更具可读性。

强烈反对。在阅读外国代码(或几个月后我自己的代码)时,很难知道每个函数的来源。限定名称使我免于从第 2345 行到模块声明标题来回走动。它还为您提供上下文chisquare?那是什么?哦,它来自scypy?好吧,然后是一些与数学相关的东西”。而且,再一次,您始终可以缩写scipy.stats.stats as scypyst. scypyst.chisquare(...)足够短,具有限定名称的所有好处。

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

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

您希望多久重新定义一个函数而不是整个模块?应该保留模块边界和功能协调,Alex 已经对此进行了深入的解释。对于大多数(全部?)现实世界场景,如果alt_module.x是 的可行替代品module.x,那么alt_module它本身可能是替代品的下降module,因此 IM 和 FPIM 就像 FMIF 一样是单行的,只要您使用as.

  • 我意识到 FPIM 在某种程度上消除了前两个问题......

实际上,as是减轻前 2 个问题(和第 3 个)的问题,而不是 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 期中所说的那样,恕我直言,它提高了可读性。

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

于 2013-11-07T03:26:18.597 回答
5

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

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

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

r_seed比类型更短,random.seed但在某种程度上保留了模块边界。随便看看你的代码的人可以看到r_seed()并且r_random()有机会了解它们是相关的。

当然,您总是可以简单地执行以下操作:

import random as r

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

于 2009-11-16T20:16:09.987 回答
1

我在这里最同意 MestreLion 的观点(因此是赞成票)。

我的观点:我经常查看我不熟悉的代码,并且仅仅看函数就知道函数来自哪个模块是非常令人沮丧的。

代码一次编写,多次阅读,因此可读性和可维护性胜过易于键入。

类似地,通常编写代码不是为了编码员的利益,而是为了另一个实体的利益。

你的代码应该对比你更了解python但不熟悉代码的人可读。

Full path imports can also better help IDE's point you at the correct source of the function or object you're looking at.

For all of these reasons and the reasons MestreLion noted, I conclude that it is best practice to import and use the full path.

于 2013-11-10T01:56:54.650 回答