问题描述
我发现这种模式(或反模式),我很高兴。
I discovered this pattern (or anti-pattern) and I am very happy with it.
我觉得这很敏捷:
def example():
age = ...
name = ...
print "hello %(name)s you are %(age)s years old" % locals()
有时候我用它的表亲:
Sometimes I use its cousin:
def example2(obj):
print "The file at %(path)s has %(length)s bytes" % obj.__dict__
我不需要创建一个人工元组并计数参数保持元组中%s的匹配位置。
I don't need to create an artificial tuple and count parameters and keep the %s matching positions inside the tuple.
你喜欢吗?你/你会用吗是/否,请解释。
Do you like it? Do/Would you use it? Yes/No, please explain.
推荐答案
对于小型应用程序,据称是一次性脚本, code> vars 由@ kaizer.se提及的增强功能和@RedGlyph提到的 .format
版本。
It's OK for small applications and allegedly "one-off" scripts, especially with the vars
enhancement mentioned by @kaizer.se and the .format
version mentioned by @RedGlyph.
然而,对于维护寿命长的维护人员和许多维护人员的大型应用程序,这种做法可能会导致维护头痛,我认为这是@ S.Lott的答案来自哪里。让我解释一些涉及到的问题,因为对于没有发展和维护大型应用程序(或这种野兽的可重用组件)的人来说,这并不明显。
However, for large applications with a long maintenance life and many maintainers this practice can lead to maintenance headaches, and I think that's where @S.Lott's answer is coming from. Let me explain some of the issues involved, as they may not be obvious to anybody who doesn't have the scars from developing and maintaining large applications (or reusable components for such beasts).
在严重应用程序中,您的格式字符串不会被硬编码,或者如果有的话,它将以某种形式,如 _('Hello {name} 。')
,其中 _
来自或类似的i18n / L10n框架。关键在于这样一个应用程序(或可以在这些应用程序中使用的可重复使用的模块)必须支持国际化(AKA i18n)和定位(AKA L10n):您希望应用程序能够在某些情况下发出Hello Paul国家和文化,其他的保罗,还有其他的保罗等等。因此,根据当前的本地化设置,格式字符串或多或少在运行时自动替换为另一个;它不是硬编码,而是存在于某种数据库中。对于所有意图和目的,想象格式字符串始终是一个变量,而不是字符串文字。
In a "serious" application, you would not have your format string hard-coded -- or, if you had, it would be in some form such as _('Hello {name}.')
, where the _
comes from gettext or similar i18n / L10n frameworks. The point is that such an application (or reusable modules that can happen to be used in such applications) must support internationalization (AKA i18n) and locatization (AKA L10n): you want your application to be able to emit "Hello Paul" in certain countries and cultures, "Hola Paul" in some others, "Ciao Paul" in others yet, and so forth. So, the format string gets more or less automatically substituted with another at runtime, depending on the current localization settings; instead of being hardcoded, it lives in some sort of database. For all intents and purposes, imagine that format string always being a variable, not a string literal.
所以,你所拥有的基本上是
So, what you have is essentially
formatstring.format(**locals())
,您不能简单地检查格式化将要使用的本地名称。您必须打开并仔细阅读L10N数据库,确定不同设置中将要使用的格式字符串,验证所有这些。
and you can't trivially check exactly what local names the formatting is going to be using. You'd have to open and peruse the L10N database, identify the format strings that are going to be used here in different settings, verify all of them.
所以在实践中你不知道什么地方名字将被使用 - 这可怕地卷曲功能的维护。您不敢重命名或删除任何本地变量,因为它可能会吓倒用户的一些(对您)模糊语言,区域设置和偏好的组合的用户体验
So in practice you don't know what local names are going to get used -- which horribly crimps the maintenance of the function. You dare not rename or remove any local variable, as it might horribly break the user experience for users with some (to you) obscure combinaton of language, locale and preferences
如果你有精湛的整合/回归测试,破产将在测试版发布之前被捕获 - 但是QA会尖叫着你,发布会被推迟...而且,我们诚实地指出:单元测试是合理的,一旦考虑了组合爆炸性的设置[[对于L10N和更多的原因]],并且支持所有依赖的版本,测试是不合适的 。所以,你只是不要冒险破产,因为他们会被抓到质量检查(如果你这样做,你可能不会持续很长时间在开发大型应用程序或可重用组件的环境中; - )。
If you have superb integration / regression testing, the breakage will be caught before the beta release -- but QA will scream at you and the release will be delayed... and, let's be honest, while aiming for 100% coverage with unit tests is reasonable, it really isn't with integration tests, once you consider the combinatorial explosion of settings [[for L10N and for many more reasons]] and supported versions of all dependencies. So, you just don't blithely go around risking breakages because "they'll be caught in QA" (if you do, you may not last long in an environment that develops large apps or reusable components;-).
所以,实际上你永远不会删除名称局部变量,即使用户体验人员长期将这个问候转换为更合适的欢迎,恐惧霸主! (并且适当地为L10n的版本)。所有因为你去了本地人()
...
So, in practice, you'll never remove the "name" local variable even though the User Experience folks have long switched that greeting to a more appropriate "Welcome, Dread Overlord!" (and suitably L10n'ed versions thereof). All because you went for locals()
...
所以你正在累积的cruft,因为你的方式我们压制了维护和编辑你的代码的能力 - 也许这个名称局部变量只存在,因为它是从数据库等获取的,所以保持它(或其他本地的)不仅仅是cruft,它是降低你的表现。
So you're accumulating cruft because of the way you've crimped your ability to maintain and edit your code -- and maybe that "name" local variable only exists because it's been fetched from a DB or the like, so keeping it (or some other local) around is not just cruft, it's reducing your performance too. Is the surface convenience of locals()
worth that?-)
但是请等待,否则 locals()
的表面方便性有更糟!在许多有用的服务之中,一个 lint
类程序(例如,)可以为你做,是警告你关于未使用的局部变量(希望它可以为未使用的全局变量),但是,对于可重用的组件,这只是一个太难了;-)。这样,您将非常快速和便宜地捕获大多数偶尔的拼写错误,例如 if ...:nmae = ...
,而不是看到单元测试中断和做想要找出为什么破坏( )有强力,普遍的单元测试,将最终抓住这个,对吗? lint会告诉你一个未使用的局部变量 nmae
,你会立即解决它。
But wait, there's worse! Among the many useful services a lint
-like program (like, for example, pylint) can do for you, is to warn you about unused local variables (wish it could do it for unused globals as well, but, for reusable components, that's just a tad too hard;-). This way you'll catch most occasional misspellings such as if ...: nmae = ...
very rapidly and cheaply, rather than by seeing a unit-test break and doing sleuth work to find out why it broke (you do have obsessive, pervasive unit tests that would catch this eventually, right?-) -- lint will tell you about an unused local variable nmae
and you will immediately fix it.
但是如果你有你的代码一个 blah.format(** locals())
,或者等价于一个 blah%locals()
..你是SOL,pal! - )如何不好的绒毛会知道是否 nmae
实际上是一个未使用的变量,或者实际上它被任何外部函数或方法你通过 locals()
到?它不能 - 无论是要警告(导致一个哭泣狼的效果,最终导致你忽略或禁用这种警告),或者它永远不会警告(具有相同的最终效果:没有警告;-)
But if you have in your code a blah.format(**locals())
, or equivalently a blah % locals()
... you're SOL, pal!-) How is poor lint going to know whether nmae
is in fact an unused variable, or actually it does get used by whatever external function or method you're passing locals()
to? It can't -- either it's going to warn anyway (causing a "cry wolf" effect that eventually leads you to ignore or disable such warnings), or it's never going to warn (with the same final effect: no warnings;-).
将此与显式优于隐式替代...:
Compare this to the "explicit is better than implicit" alternative...:
blah.format(name=name)
有 - 没有维护,表演,以及对我而言的麻烦,再加上;幸福!您立即让所有有关人员清楚(lint included ;-)准确地说,使用本地变量,以及为什么目的。
There -- none of the maintenance, performance, and am-I-hampering-lint worries, applies any more; bliss! You make it immediately clear to everybody concerned (lint included;-) exactly what local variables are being used, and exactly for what purposes.
我可以继续,但我认为这个帖子已经很长了, - )。
I could go on, but I think this post is already pretty long;-).
所以,总结:γνῶθισεαυτόν!嗯,我的意思是认识你自己!而你自己我的意思是你的代码的目的和范围。如果它是一个1-off-or-outabouts的东西,永远不会是i18n'd和L10n'd,几乎不需要将来的维护,永远不会在更广泛的环境等重复使用,然后继续使用 localals()
因为它的小但整洁的方便;如果你不知道,或者即使你不完全确定,那么在谨慎的一边,让事情变得更加明确 - 承受一些小小的不便之处,就是要准确地说明你要做什么,并享受所有的优势。
So, summarizing: "γνῶθι σεαυτόν!" Hmm, I mean, "know thyself!". And by "thyself" I actually mean "the purpose and scope of your code". If it's a 1-off-or-thereabouts thingy, never going to be i18n'd and L10n'd, will hardly need future maintenance, will never be reused in a broader context, etc, etc, then go ahead and use locals()
for its small but neat convenience; if you know otherwise, or even if you're not entirely certain, err on the side of caution, and make things more explicit -- suffer the small inconvenience of spelling out exactly what you're going, and enjoy all the resulting advantages.
BTW,这只是Python正在努力支持小型,一次性,探索性,可能互动编程(通过允许和支持风险)的示例之一方便性远远超出 locals()
- 想到 import *
, eval
,
exec
,还有其他几种方式可以为了方便起见,减少命名空间和风险维护的影响)以及大型可重复使用的企业-y应用程序和组件。它可以在两者都做得很好,但只有当你知道自己,并避免使用便利部分,除非你绝对确定你实际上可以负担得起。重要的考虑是,我对命名空间做什么,以及编译器,lint和c,人类读者和维护者等的形成和使用的意识?。
BTW, this is just one of the examples where Python is striving to support both "small, one-off, exploratory, maybe interactive" programming (by allowing and supporting risky conveniences that extend well beyond locals()
-- think of import *
, eval
, exec
, and several other ways you can mush up namespaces and risk maintenance impacts for the sake of convenience), as well as "large, reusable, enterprise-y" apps and components. It can do a pretty good job at both, but only if you "know thyself" and avoid using the "convenience" parts except when you're absolutely certain you can in fact afford them. More often than not, the key consideration is, "what does this do to my namespaces, and awareness of their formation and use by the compiler, lint &c, human readers and maintainers, and so on?".
记住,命名空间是一个很好的主意 - 让我们做更多的这些! Python的禅意是如何得出结论...但Python作为同意成人的语言,让您定义了由于您的开发环境,目标和实践。负责任地使用这个功能! - )
Remember, "Namespaces are one honking great idea -- let's do more of those!" is how the Zen of Python concludes... but Python, as a "language for consenting adults", lets you define the boundaries of what that implies, as a consequence of your development environment, targets, and practices. Use this power responsibly!-)
这篇关于Python:正在使用“..%(var)s ..” %本地人()一个很好的做法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!