对于小型应用程序和所谓的“一次性”脚本来说是可以的,尤其是vars
@kaizer.se 提到的增强功能和.format
@RedGlyph 提到的版本。
但是,对于维护寿命长且维护人员众多的大型应用程序,这种做法可能会导致维护问题,我认为这就是@S.Lott 的答案的来源。让我解释其中涉及的一些问题,因为对于没有开发和维护大型应用程序(或此类野兽的可重用组件)的任何人来说,它们可能并不明显。
在“严肃”的应用程序中,您不会对格式字符串进行硬编码——或者,如果有的话,它会采用某种形式,例如_('Hello {name}.')
,其中_
来自gettext或类似的 i18n / L10n 框架。关键是这样的应用程序(或可能碰巧在此类应用程序中使用的可重用模块)必须支持国际化(AKA i18n)和定位(AKA L10n):您希望您的应用程序能够在某些情况下发出“Hello Paul”国家和文化,其他一些中的“Hola Paul”,其他一些中的“Ciao Paul”,等等。因此,格式字符串在运行时或多或少会自动替换为另一个,具体取决于当前的本地化设置;它不是硬编码的,而是存在于某种数据库中。出于所有意图和目的,假设格式字符串始终是一个变量,而不是字符串文字。
所以,你所拥有的本质上是
formatstring.format(**locals())
而且您无法轻松检查格式化将使用的本地名称。您必须打开并仔细阅读 L10N 数据库,确定将在不同设置中使用的格式字符串,并验证所有这些。
因此,在实践中,您不知道将使用哪些本地名称——这严重影响了功能的维护。您不敢重命名或删除任何局部变量,因为它可能会严重破坏具有某些(对您而言)语言、区域设置和偏好的模糊组合的用户的用户体验
如果您有出色的集成/回归测试,那么问题将在 beta 版本发布之前被发现——但 QA 会对您大喊大叫,并且发布将被延迟……老实说,同时目标是 100% 覆盖单元测试是合理的,它真的不是集成测试,一旦你考虑设置的组合爆炸[[对于 L10N 和更多原因]] 和所有依赖项的支持版本。因此,您只是不要冒着损坏的风险四处走动,因为“他们会在 QA 中被抓住”(如果这样做,您可能不会在开发大型应用程序或可重用组件的环境中持续很长时间;-)。
因此,在实践中,您永远不会删除“name”局部变量,即使用户体验人员早就将该问候语切换为更合适的“欢迎,恐惧霸主!” (以及适当的 L10n'ed 版本)。都是因为你去locals()
...
因此,由于您限制了维护和编辑代码的能力,因此您正在积累垃圾-也许该“名称”局部变量仅存在,因为它是从数据库等中获取的,因此请保留它(或其他一些当地人)周围不仅是杂乱无章的,它也会降低你的表现。表面上的便利locals()
值得吗?-)
但是等等,还有更糟的!在许多有用的服务中,一个lint
类似的程序(例如pylint)可以为您做的,是警告您未使用的局部变量(希望它也可以为未使用的全局变量做这件事,但是,对于可重用的组件,那只是有点太难了;-)。通过这种方式,您将捕获大多数偶尔出现的拼写错误,例如if ...: nmae = ...
非常快速且廉价,而不是通过查看单元测试中断并进行侦探工作以找出它崩溃的原因(您确实有强迫性的、普遍的单元测试最终会发现这一点,对吗?-) -- lint 会告诉你一个未使用的局部变量nmae
,你会立即修复它。
但是,如果您的代码中有 ablah.format(**locals())
或等效的 a blah % locals()
...,那么您就是 SOL,伙计!-) 可怜的 lint 如何知道nmae
实际上是否是一个未使用的变量,或者实际上它确实被任何外部函数或你传递locals()
给的方法?它不能——要么它无论如何都会发出警告(导致最终导致你忽略或禁用此类警告的“哭狼”效果),或者它永远不会发出警告(具有相同的最终效果:没有警告;-) .
将此与“显式优于隐式”替代方案进行比较......:
blah.format(name=name)
那里 - 不再适用于维护、性能和 am-I-hampering-lint 问题;幸福!您立即让所有相关人员(包括 lint)清楚地知道正在使用哪些局部变量,以及确切用于什么目的。
我可以继续,但我认为这篇文章已经很长了;-)。
所以,总结一下:“ γνῶθι σεαυτόν!” 嗯,我的意思是,“认识你自己!”。而“你自己”实际上是指“你的代码的目的和范围”。如果它是一个 1-off-or-thereabouts 的东西,永远不会成为 i18n'd 和 L10n'd,几乎不需要未来的维护,永远不会在更广泛的环境中重复使用,等等,然后继续使用locals()
它小而整洁的便利;如果您不知道,或者即使您不完全确定,请谨慎行事,并让事情更明确 - 忍受一下准确说明您要做什么的小不便,并享受所有由此产生的优势。
顺便说一句,这只是 Python 努力支持“小型的、一次性的、探索性的、也许是交互式的”编程的例子之一(通过允许和支持远远超出的风险便利locals()
——想想import *
, eval
,exec
和其他几个为了方便起见,您可以混合命名空间和风险维护影响),以及“大型、可重用、企业级”应用程序和组件。它在这两个方面都可以做得很好,但前提是你“了解自己”并避免使用“方便”部分,除非你绝对确定你实际上可以负担得起。通常情况下,关键考虑因素是,“这对我的命名空间有什么影响,以及编译器、lint 和 c 对它们的形成和使用的认识,
请记住,“命名空间是一个很棒的想法——让我们做更多的事情!” 这就是 Python 之禅的结论……但是 Python,作为一种“成年人同意的语言”,允许您根据您的开发环境、目标和实践来定义其含义的边界。负责任地使用这种力量!-)