Python:使用 "..%(var)s.." % locals() 是一个好习惯吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1550479/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 22:33:50  来源:igfitidea点击:

Python: is using "..%(var)s.." % locals() a good practice?

pythonstringdesign-patternsanti-patterns

提问by flybywire

I discovered this pattern (or anti-pattern) and I am very happy with it.

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

I feel it is very agile:

我觉得它很敏捷:

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__

I don't need to create an artificial tuple and count parameters and keep the %s matching positions inside the tuple.

我不需要创建人工元组和计数参数并将 %s 匹配位置保留在元组内。

Do you like it? Do/Would you use it? Yes/No, please explain.

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

回答by Alex Martelli

It's OK for small applications and allegedly "one-off" scripts, especially with the varsenhancement mentioned by @kaizer.se and the .formatversion mentioned by @RedGlyph.

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

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

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

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 gettextor 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.

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

So, what you have is essentially

所以,你所拥有的基本上是

formatstring.format(**locals())

and you can't trivially check exactly whatlocal 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.

并且您无法简单地检查格式将使用哪些本地名称。您必须打开并仔细阅读 L10N 数据库,确定将在不同设置中使用的格式字符串,验证所有这些字符串。

So in practice you don't knowwhat 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

因此,在实践中,您不知道将使用哪些本地名称——这会严重影响函数的维护。你不敢重命名或删除任何局部变量,因为它可能会严重破坏用户的用户体验(对你来说)一些模糊的语言、语言环境和偏好组合

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 unittests is reasonable, it really isn't with integrationtests, 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;-).

如果您进行了出色的集成/回归测试,那么在 Beta 版发布之前就会发现漏洞——但是 QA 会向您大喊大叫,发布将被延迟……而且,老实说,同时旨在实现 100% 的单元测试覆盖率是合理的,一旦您考虑设置的组合爆炸[[对于 L10N 和更多原因]] 和所有依赖项的支持版本,它确实不是集成测试。所以,你只是不要冒险去冒险,因为“他们会被 QA 抓住”(如果你这样做,你可能不会在开发大型应用程序或可重用组件的环境中持续很长时间;-)。

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

因此,在实践中,您永远不会删除“名称”局部变量,即使用户体验人员长期以来将该问候语切换为更合适的“欢迎,恐惧霸王!” (以及适当的 L10n'ed 版本)。都是因为你去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?-)

因此,由于您限制了维护和编辑代码的能力,您正在积累 cruft——也许那个“名称”局部变量仅存在是因为它是从数据库等获取的,所以保留它(或其他一些本地) 不仅仅是垃圾,它也会降低你的表现。是表面上的便利locals()价值- ?)

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 whyit broke (you dohave obsessive, pervasive unit tests that wouldcatch this eventually, right?-) -- lint will tell you about an unused local variable nmaeand you will immediately fix it.

但是等等,还有更糟的!在一个lint类似程序(例如pylint)可以为您做的许多有用的服务中,是警告您未使用的局部变量(希望它也可以对未使用的全局变量这样做,但是,对于可重用的组件,那只是有点太难了;-)。通过这种方式,您将发现大多数偶然的拼写错误,例如if ...: nmae = ...非常快速和廉价,而不是通过看到单元测试中断并进行侦探工作来找出它中断的原因(您确实有强迫性的,普遍的单元测试,最终会发现这一点,对吗?-) -- lint 会告诉你一个未使用的局部变量nmae,你会立即修复它。

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

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

Compare this to the "explicit is better than implicit" alternative...:

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

blah.format(name=name)

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 whatlocal variables are being used, and exactly for what purposes.

那里——没有任何维护、性能和 am-I-hampering-lint 担忧,不再适用;幸福!您立即让所有相关人员(包括 lint ;-)清楚地了解正在使用哪些局部变量,以及具体用于什么目的。

I could go on, but I think this post is already pretty long;-).

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

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.

所以,总结一下:“ γν?θισεαυτ?ν!” 嗯,我的意思是,“认识你自己!”。“你自己”实际上是指“你的代码的目的和范围”。如果它是 1-off-or-abouts 的东西,永远不会被 i18n'd 和 L10n'd,几乎不需要未来的维护,永远不会在更广泛的环境中重复使用,等等,然后继续使用locals()它小而整洁的便利;如果您知道其他情况,或者即使您不完全确定,也请谨慎行事,让事情变得更加明确 - 忍受准确说明您要做什么的小不便,并享受所有由此产生的好处。

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是努力支持“小,一次性的,探索性的,也许互动”节目(通过允许和支持是远远超出危险便利的一个例子locals()-想到import *evalexec,和其他几个为了方便起见,您可以混合命名空间和风险维护影响的方式),以及“大型、可重用、企业级”应用程序和组件。它可以在这两方面都做得很好,但前提是您“了解自己”并避免使用“便利”部分,除非您绝对确定您实际上可以负担得起。通常情况下,关键的考虑是,“这对我的命名空间有什么影响,以及编译器、lint 和 c 对它们的形成和使用的认识,

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 youdefine the boundaries of what that implies, as a consequence of your development environment, targets, and practices. Use this power responsibly!-)

请记住,“命名空间是一个很棒的主意——让我们做更多这样的事情!” 这就是 Python 之禅的结论……但是 Python 作为一种“适合成年人的语言”,让可以根据您的开发环境、目标和实践来定义其含义的界限。负责任地使用这种权力!-)

回答by Andrew Hare

I think it is a great pattern because you are leveraging built-in functionality to reduce the code you need to write. I personally find it quite Pythonic.

我认为这是一个很好的模式,因为您正在利用内置功能来减少需要编写的代码。我个人觉得它非常 Pythonic。

I never write code that I don't need to write - less code is better than more code and this practice of using locals()for example allows me to write less code and is also very easy to read and understand.

我从不编写我不需要编写的代码 - 少代码比多代码好locals(),例如这种使用实践使我可以编写更少的代码并且也很容易阅读和理解。

回答by pfctdayelise

Regarding the "cousin", instead of obj.__dict__, it looks a lot better with new string formatting:

关于“表亲”,而不是obj.__dict__,使用新的字符串格式看起来好多了:

def example2(obj):
    print "The file at {o.path} has {o.length} bytes".format(o=obj)

I use this a lot for reprmethods, e.g.

经常将它用于repr方法,例如

def __repr__(self):
    return "{s.time}/{s.place}/{s.warning}".format(s=self)

回答by S.Lott

Never in a million years. It's unclear what the context for formatting is: localscould include almost any variable. self.__dict__is not as vague. Perfectly awful to leave future developers scratching their heads over what's local and what's not local.

在一百万年里都不会发生。目前尚不清楚格式化的上下文是什么:locals几乎可以包括任何变量。self.__dict__不是那么模糊。让未来的开发人员为什么是本地的和什么不是本地的而挠头,这真是太糟糕了。

It's an intentional mystery. Why saddle your organization with future maintenance headaches like that?

这是一个有意为之的谜。为什么要让您的组织在未来遇到这样的维护难题?

回答by RedGlyph

The "%(name)s" % <dictionary>or even better, the "{name}".format(<parameters>)have the merit of

"%(name)s" % <dictionary>甚至更好,则"{name}".format(<parameters>)具有的优点

  • being more readable than "%0s"
  • being independent from the argument order
  • not compelling to use all the arguments in the string
  • 比“%0s”更具可读性
  • 独立于论证顺序
  • 不强制使用字符串中的所有参数

I would tend to favour the str.format(), since it should be the way to do that in Python 3 (as per PEP 3101), and is already available from 2.6. With locals()though, you would have to do this:

我倾向于使用 str.format(),因为它应该是在 Python 3 中做到这一点的方式(根据PEP 3101),并且在 2.6 中已经可用。随着locals()虽然,你将不得不这样做:

print("hello {name} you are {age} years old".format(**locals()))

回答by u0b34a0f6ae

Using the built-in vars([object])(documentation) might make the second look better to you:

使用内置vars([object])文档)可能会让你觉得第二个更好看:

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

The effect is of course just the same.

效果当然是一样的。

回答by Hymantose

There is now an official way to do this, as of Python 3.6.0: formatted string literals.

从 Python 3.6.0 开始,现在有一种官方方法可以做到这一点:格式化字符串文字

It works like this:

它是这样工作的:

f'normal string text {local_variable_name}'

E.g. instead of these:

例如,而不是这些:

"hello %(name)s you are %(age)s years old" % locals()
"hello {name} you are {age} years old".format(**locals())
"hello {} you are {} years old".format(name, age)

just do this:

只需这样做:

f"hello {name} you are {age} years old"

Here's the official example:

这是官方的例子:

>>> name = "Fred"
>>> f"He said his name is {name}."
'He said his name is Fred.'
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}"  # nested fields
'result:      12.35'

Reference:

参考: