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

unu*_*tbu 67 python python-import

我们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出于某种原因,在一段时间后要重定向蟒蛇定义functionalt_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.

非常感谢.

Ale*_*lli 45

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

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

反之亦然,F是能够通过模块化协调切换耦合函数,类和变量(通常是属于一起的实体).例如,为了使测试可重复(FPIM亲点1),你嘲笑都seedrandomrandom模块,如果你的代码如下FPIM,你所有的设置,协调保障; 但是如果你有直接导入函数的代码,你必须搜索每个这样的模块并反复重复模拟.使测试完全可重复通常还需要对日期和时间函数进行"协调from datetime import datetime模拟" - 如果您在某些模块中使用,则需要查找并模拟它们(以及所有正在执行的操作from time import time,等等)以确保所有当系统的各个部分问"现在几点了?"时收到的时间.完全一致(如果你使用FPIM,你只需要模拟两个相关的模块).

我喜欢FPIM,因为那里真的没有太多的附加值用乘法合格的名称,而不是一个单独合格的一个(而barenames和合格的名称之间的差异是巨大的 -你会得到这样一个合格的名字更多的控制,无论是单或者乘以比你可能用的名字!).

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

  • 我注意到你的答案使用FMIF:`来自question.FMIF导入point_2为F,point_3为R`. (15认同)

Dan*_*man 17

关于这一点的经典文本经常来自Fredrik Lundh,即effbot.他的建议是:总是使用导入 - 除非你不应该.

换句话说,要明智.就个人而言,我发现任何深层的模块都倾向于通过引入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 *,你根本不知道你要导入什么,所以函数可以出现在你的代码中,而不会给读者带来任何线索.那太可怕了,应该不惜一切代价避免.


Mes*_*ion 7

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

首先,解决每个子弹:

(据称)FMIF的优点:

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

也许,但模块名称通常足够短,所以这是不相关的.当然,还有datetime,而且os,re,sys,等.而Python有免费线内休息{ [ (.对于嵌套模块,总是as在IM和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()))等.

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

您想多久重新定义一个函数而不是整个模块?应保留模块边界和功能协调,Alex已经深入解释了这一点.对于大多数(所有?)现实场景,如果alt_module.x是可行的替代品module.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中所说的,恕我直言,它提高了可读性.

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


ste*_*eha 6

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

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

from random import seed as r_seed
from random import random as r_random
Run Code Online (Sandbox Code Playgroud)

r_seed类型比键入更短random.seed但有些保留模块边界.随便看看你的代码的人可以看到r_seed()并且r_random()有机会认识到它们是相关的.

当然,您可以随时做到:

import random as r
Run Code Online (Sandbox Code Playgroud)

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