`import module`比`from module import function`更好的编码风格?

时间:2009-11-16 19:18:49

标签: python python-import

from module import function称为FMIF编码样式。

import module称为IM编码风格。

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

为什么IM + FPIM被认为是比FMIF更好的编码风格? (有关此问题的灵感,请参阅this post。)

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

  1. 代码简短:它允许我使用较短的函数名称,因此有助于坚持80行每行约定。
  2. 可读性:chisquare(...)似乎比scipy.stats.stats.chisquare(...)更具可读性。虽然这是一个主观标准,但我认为大多数人会同意。
  3. 轻松重定向:如果我使用FMIF并且由于某种原因稍后想要重定向python以从function而不是alt_module定义module我需要更改一行:from alt_module import function。如果我使用IM,我需要更改许多行代码。
  4. 我意识到FPIM在某种程度上取消了前两个问题,但第三个问题呢?

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

    IM的优点:

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

      非常感谢。

5 个答案:

答案 0 :(得分:44)

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

考虑你的pro-FMIF第3点(称之为R用于重定向)与你的pro-FPIM第2点(称之为F的灵活性):R相当于促进模块边界完整性的丧失,而F则强化它。模块中的多个函数,类和变量通常旨在协同工作:它们不应独立地切换到不同的含义。例如,考虑模块random及其函数seeduniform:如果您要将其中一个的导入切换到另一个模块,那么您将打破正常连接在致电seed和调用uniform的结果之间。当一个模块设计得很好,具有凝聚力和完整性时,R可以帮助打破模块的界限,这实际上是一个负面因素 - 它可以让你更容易做出更好的事情而不是

反之亦然,F是通过模块化实现协调切换耦合函数,类和变量(通常是属于的实体)的功能。例如,为了使测试可重复(FPIM pro-point 1),您在seed模块中模拟了randomrandom,如果您的代码遵循FPIM,那么您已全部设置,协调保证;但是如果你有直接导入函数的代码,你必须搜索每个这样的模块并反复重复模拟。使测试完全可重复通常还需要“协调模拟”日期和时间函数 - 如果在某些模块中使用from datetime import datetime,则需要查找并模拟它们(以及所有执行from time import time的人,等等)确保系统的各个部分询问“现在几点了?”时收到的所有时间。完全一致(如果你使用FPIM,你只需要模拟两个相关的模块)。

我喜欢FPIM,因为使用乘法限定名称而不是单一合格名称实际上没有多少附加价值(而裸名和限定名称之间的区别是巨大 - 你得到所以更多的控制与一个合格的名字,无论是单独的还是多次的,比你可能用一个名字!)。

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

答案 1 :(得分:16)

关于这一点的经典文本,经常来自Fredrik Lundh,即effbot。他的建议是:always use 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 *,你根本不知道你要导入什么,因此函数可以出现在你的代码中,而不会给读者带来任何线索。这太可怕了,应该不惜一切代价避免。

答案 2 :(得分:6)

这里有很棒的答案(我全都赞成),以下是我对此事的看法:

首先,解决每个子弹:

(据称)FMIF的优点:

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

也许,但模块名称通常很短,所以这不相关。当然,还有datetime,还有osresys等。而Python在{ [ (内有免费换行符。对于嵌套模块,IM和FPIM中始终存在as

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

非常不同意。在阅读外国代码(或几个月后我自己的代码)时,很难知道每个函数的来源。合格的名称使我免于从2345行到模块声明标题。它还为你提供上下文chisquare?这是什么?哦,它来自scypy?好吧,一些数学相关的东西然后” 。而且,您可以再次缩写scipy.stats.stats as scypystscypyst.chisquare(...)足够短,并且具有合格名称的所有好处。

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

  • 易于重定向:从altmodule而不是模块重新定义函数。

您想要重新定义单个功能但不是整个模块的频率?应保留模块边界和功能协调,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中所说,恕我直言,它提高可读性。

这一切都归结为一致性,简洁性,组织性。 “导入模块,而不是对象”是一个很好的,易于思考的模型。

答案 3 :(得分: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()r.seed(),这可能是此案例的理想折衷方案。当我从模块导入一个或两个函数时,我只使用前缀技巧。当我想使用同一模块中的许多函数时,我将导入模块,可能使用as来缩短名称。

答案 4 :(得分:1)

我最同意MestreLion(这是一个upvote)。

我的观点:我经常查看我不熟悉的代码,并且不知道函数来自于查看函数的功能是非常令人沮丧的。

代码编写一次并多次读取,因此可读性和可维护性胜过打字的难易程度。

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

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

完整路径导入还可以更好地帮助IDE指出您正在查看的功能或对象的正确来源。

由于所有这些原因以及MestreLion注意到的原因,我得出结论,导入和使用完整路径是最佳做法。

相关问题