Python:正在使用“...%(var)s.."% locals() 一个好的做法? [英] Python: is using "..%(var)s.." % locals() a good practice?

查看:11
本文介绍了Python:正在使用“...%(var)s.."% locals() 一个好的做法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现了这种模式(或反模式),我对它非常满意.

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()

有时我使用它的表亲:

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.

推荐答案

对于小型应用程序和所谓的一次性"脚本来说没问题,尤其是@kaizer.se 提到的 vars 增强和@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}.'),其中 _ 来自 gettext 或类似的i18n/L10n 框架.关键是这样的应用程序(或可以在此类应用程序中使用的可重用模块)必须支持国际化(AKA i18n)和定位(AKA L10n):您希望您的应用程序能够在某些情况下发出Hello Paul"国家和文化,Hola Paul"在其他一些人中,Ciao 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

如果您进行了出色的集成/回归测试,那么在 Beta 版发布之前就会发现漏洞——但是 QA 会向您大喊大叫,发布将被推迟......而且,说实话,同时瞄准 100% 的覆盖率使用单元测试是合理的,但集成测试确实不是,一旦您考虑到设置的组合爆炸[[对于L10N和更多原因]]并支持所有依赖项的版本.因此,您不能因为他们会被 QA 抓到"而逍遥法外地冒着损坏的风险(如果这样做,您可能无法在开发大型应用程序或可重用组件的环境中持续很长时间;-).

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;-).

因此,在实践中,您永远不会删除name"局部变量,即使用户体验人员长期以来将该问候语切换为更合适的欢迎,恐惧霸王!"(以及适当的 L10n'ed 版本).都是因为你选择了 locals()...

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 -- 也许那个name"局部变量仅存在因为它是从数据库或类似的地方获取的,所以保持它(或其他一些本地的)不仅仅是垃圾,它也会降低你的表现.locals() 表面上的便利是否值得?-)

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?-)

但是等等,还有更糟的!在众多有用的服务中,有一个类似 lint 的程序(例如,pylint) 可以为您做的,是警告您未使用的局部变量(希望它也可以对未使用的全局变量这样做,但是,对于可重用的组件,这有点太难了;-).通过这种方式,您可以非常快速且廉价地发现大多数偶然的拼写错误,例如 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,伙计!-) 可怜的 lint 如何知道 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)

那里——没有任何维护、性能和 am-I-hampering-lint 担忧,不再适用;幸福!您立即向所有相关人员(包括 lint;-)明确使用什么局部变量,以及具体用于什么目的.

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-abouts 的东西,永远不会被 i18n'd 和 L10n'd,几乎不需要未来的维护,永远不会在更广泛的上下文中重用,等等,然后继续使用 locals() 小而整洁的便利;如果您知道其他情况,或者即使您不完全确定,也请谨慎行事,让事情变得更加明确 - 忍受准确说明您要做什么的小不便,并享受所有由此产生的好处.

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.

顺便说一句,这只是 Python 努力支持小型的、一次性的、探索性的、可能是交互式的"编程的例子之一(通过允许和支持远远超出 locals() -- 想想 import *evalexec 和其他几种方法,您可以将命名空间和风险维护影响混为一谈为了方便),以及大型、可重用、企业级"的应用程序和组件.它可以在这两方面都做得很好,但前提是您了解自己"并避免使用便利"部分,除非您绝对确定您实际上可以负担得起.通常情况下,关键的考虑是,这对我的命名空间有什么影响,以及编译器、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.."% locals() 一个好的做法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆