小编典典

Python:使用“ ..%(var)s ..”%locals()是一种好习惯吗?

python

我发现了这种模式(或反模式),对此我感到非常满意。

我觉得它非常敏捷:

def example():
    age = ...
    name = ...
    print "hello %(name)s you are %(age)s years old" % locals()

有时我用它的表弟:

def example2(obj):
    print "The file at %(path)s has %(length)s bytes" % obj.__dict__

我不需要创建人为的元组并计算参数并将%s匹配位置保留在元组中。

你喜欢它吗?您会/会使用它吗?是/否,请解释。


阅读 205

收藏
2020-12-20

共1个答案

小编典典

对于小型应用程序和所谓的“一次性”脚本,这是可以的,尤其是vars@ .formatkaizer.se提到的增强功能和@RedGlyph提到的版本。

但是,对于维护寿命长且维护人员众多的大型应用程序,这种做法会导致维护麻烦,而我认为这就是@
S.Lott的答案。让我解释一下其中涉及的一些问题,因为对于没有开发和维护大型应用程序(或此类野兽的可重用组件)的人来说,问题可能并不明显。

在“严肃的”应用程序中,您不会对格式字符串进行硬编码-或者,如果有的话,它将采用某种格式,例如_('Hello {name}.'),其中gettext_来自gettext或类似的i18n
/ L10n框架。关键是这样的应用程序(或可能在此类应用程序中使用的可重用模块)必须支持国际化(AKA i18n)和本地化(AKA
L10n):您希望您的应用程序能够在某些情况下发出“ Hello Paul”国家和文化,还有“ Hola Paul”,还有“ Ciao
Paul”,依此类推。因此,取决于当前的本地化设置,格式字符串在运行时会或多或少地自动被另一个替换。而不是硬编码,它存在于某种数据库中。出于所有目的和目的,请想象格式字符串始终是变量,而不是字符串文字。

因此,您所拥有的基本上是

formatstring.format(**locals())

你不能平凡检查究竟 是什么
地方的名字的格式将被使用。您必须打开并仔细阅读L10N数据库,在不同的设置中标识将在此处使用的格式字符串,并验证所有这些格式字符串。

因此,在实践中,您不 知道 将使用哪些本地名称-
这严重限制了该功能的维护。您不敢重命名或删除任何局部变量,因为这可能会严重破坏某些(对您)语言,区域设置和首选项组合的用户的用户体验

如果您进行了出色的集成/回归测试,那么在Beta版本发布之前就会发现破损-
但是质量检查将对您大喊大叫,并且版本发布会延迟…而且,老实说,我们的目标是100%覆盖 单元
测试是合理的,一旦考虑了设置的组合爆炸([出于L10N和更多原因])以及所有依赖项的受支持版本, 集成
测试实际上就不对了。因此,您只是不会轻易地冒着遭受破坏的风险,因为“它们会陷入质量检查中”(如果这样做,您可能不会在开发大型应用程序或可重用组件的环境中持续很长时间;-)。

因此,实际上,即使“用户体验”用户早已将问候语切换为更合适的“欢迎,恐惧霸王”,您也永远不会删除“名称”局部变量。(以及适当的L10n版本)。都是因为你去locals()

因此,由于您削弱了维护和编辑代码的能力的方式,您正在积累工作量-
也许“名称”局部变量仅存在是因为它是从数据库等中获取的,因此请保留它(或其他一些本地人不仅在工作,而且也在降低您的表现。是表面上的便利locals()价值
- ?)

但是,等等,更糟的是!在许多有用的服务中,lint类似程序(例如pylint)可以为您提供警告,警告您未使用的局部变量(希望它也可以对未使用的全局变量执行此操作,但是对于可重用的组件,这仅仅是有点太难了;-)。这样一来,您将if ...: nmae = ...迅速而便宜地捕获大多数偶然的拼写错误,而不是看到单元测试中断并进行侦查工作以找出其 为何 会破译(您 确实
有强迫症,普遍存在的单元测试最终 抓住这种情况,对吗?-)-lint会告诉您一个未使用的局部变量nmae,您将立即修复它。

但是,如果您的代码中包含ablah.format(**locals())或等效的blah % locals()…您是SOL,朋友!-)可怜的棉绒怎么知道nmae实际上是否是未使用的变量,或者实际上它是否被任何外部函数或您要传递locals()给的方法?它不能-
无论如何都会发出警告(导致“哭狼”效应,最终导致您忽略或禁用此类警告),或者永远不会发出警告(最终效果相同:无警告;-) 。

将此与“显式胜于隐式”替代方案进行比较…:

blah.format(name=name)

那里-维护,性能和烦人的皮毛担心都不再适用;极乐!您会立即让所有相关人员(包括棉绒;-)清楚地 知道 正在使用 什么
局部变量,以及到底是出于什么目的。

我可以继续,但是我认为这篇文章已经很长了;-)。

因此,总结一下:“ γνῶθισεαυτόν !”
嗯,我的意思是,“知道自己!”。我所说的“自己”实际上是“代码的目的和范围”。如果它是“按价出售”或“按价出售”之类的东西,那么永远都不会成为i18n’和L10n’d,几乎不需要将来的维护,也永远不会在更广泛的上下文中重复使用,等等,等等,然后继续使用locals()它小而整洁的便利;如果您知道其他情况,或者即使您不确定,也请谨慎行事,使事情更加明确-
麻烦一点,因为您无法清楚地说明要走的路,并享受由此带来的所有优势。

顺便说一句,这仅仅是在哪里Python是努力支持“小,一次性的,探索性的,也许互动”节目(通过允许和支持是远远超出危险便利的一个例子locals()-想到import *evalexec,和其他几个为了方便起见,可以使用多种方法来命名空间和风险维护影响,以及“大型,可重用的企业级”应用程序和组件。它在两者上都可以做得很好,但前提是您“了解自己”并且避免使用“便利”部分,除非您绝对确定可以负担得起。通常,关键的考虑因素是:“这对我的命名空间有什么作用,以及编译器lint&c对它们的形成和使用的认识,

请记住,“命名空间是一个很棒的主意-让我们做更多的事!” 这就是Python的Zen总结的方式……但是Python作为“成年人同意的语言”,
可以根据开发环境,目标和实践来定义隐含的边界。负责任地使用此电源!-)

2020-12-20