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

我发现这种模式(或反模式),我很高兴。

我觉得这很敏捷:

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__ 

我不需要创build一个人工元组和计数参数,并保持元组内的%s匹配位置。

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

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

但是,对于维护时间长和维护人员多的大型应用程序,这种做法可能会导致维护头痛,我认为这就是@ S.Lott的答案来自何处。 让我解释一些涉及的问题,因为对于没有开发和维护大型应用程序(或可重用组件)的人来说,这些问题可能并不明显。

在一个“严肃的”应用程序中,你的格式string不会被硬编码 – 或者,如果你有的话,它将以某种forms如_('Hello {name}.') ,其中_来自gettext或类似的i18n / L10n框架。 关键是这样一个应用程序(或者可以在这种应用程序中使用的可重用模块)必须支持国际化(AKA i18n)和本地化(AKA L10n):您希望您的应用程序能够在某些情况下发出“Hello Paul”国家和文化,“霍拉保罗”在其他一些“保罗”在其他人还没有,等等。 所以,格式化string在运行时或多或less会被另一个自动replace,具体取决于当前的本地化设置; 它不是硬编码的,而是生活在某种数据库中。 对于所有的意图和目的,想象格式string总是一个variables,而不是一个string文字。

所以,你有什么本质上的

 formatstring.format(**locals()) 

而且不能简单地检查格式将要使用的本地名称。 您必须打开并仔细阅读L10N数据库,在不同的设置中确定要在此处使用的格式string,并validation所有这些string。

所以在实践中,你不知道当地的名字将被使用 – 这可怕地压缩了function的维护。 你不敢重新命名或删除任何本地variables,因为它可能会严重破坏用户的语言,语言环境和偏好的一些(到你)晦涩的组合的用户体验

如果你有完美的整合/回归testing,破解将在beta版发布之前被捕获 – 但是QA会对你大喊大叫,并且发布会被延迟…而且,老实说,在100%覆盖单元testing的同时是合理的,一旦考虑到设置[L10N和更多的原因]的组合式爆炸,以及所有依赖的受支持版本,这实际上不是集成testing。 所以,你不要轻易冒险去冒险,因为“他们会陷入质量保证”(如果你这样做,在开发大型应用程序或可重用组件的环境中可能会持续很久;-)。

所以,实际上,即使用户体验人员已经把这个问候语转换为更合适的“欢迎,恐惧霸王!”,你也永远不会删除“名字”本地variables。 (以及适当的版本)。 所有,因为你去locals()

所以,由于你压缩维护和编辑你的代码的能力,你正积累一些东西 – 也许这个“name”局部variables只存在,因为它是从数据库或类似的东西中获取的,所以保存它(或一些其他的地方),不仅仅是肮脏的,它也会降低你的performance。 locals()的表面方便locals()值得吗? – )

但是等等,情况更糟! 在很多有用的服务中,类似lint的程序(比如pylint )可以为你做的是警告你未使用的局部variables(希望它也可以用于未使用的全局variables),但是对于可重用的组件,只是有点太难了;-)。 这样,你会发现大多数偶尔的拼写错误,如: if ...: nmae = ...非常迅速和便宜,而不是通过看unit testingrest,做侦探工作,找出为什么它打破了(你有迷恋,普遍的unit testing, 最终抓住这个,对吗?) – lint会告诉你一个未使用的局部variablesnmae ,你会立即修复它。

但是如果你在你的代码中有一个blah.format(**locals()) ,或者相当于一个blah % locals() …你是SOL,pal! – )可怜的皮棉怎么知道nmae是否在事实上是一个未使用的variables,或者实际上它被任何你传递给locals()外部函数或方法所使用? 它不能 – 要么无论如何都会发出警告(导致“狼来了”的效果,最终导致你忽略或禁用这样的警告),或者它永远不会警告(最后一个效果:没有警告;-) 。

比较这与“明确优于暗示”替代…:

 blah.format(name=name) 

在那里 – 没有任何维护,性能和我的阻碍 – 皮棉的担忧,不再适用。 幸福! 你立即清楚地向每个有关的人(lint包括;-)确切地说是什么局部variables正在被使用,正是为了什么目的。

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

因此,总结:“ γνῶθισεαυτόν !” 嗯,我的意思是“认识你自己”。 而“自己”其实是指“你的代码的目的和范围”。 如果它是一个“一分为二”的东西,那么永远不会被日后维护,也不会再需要维护,永远不会在更广的范围内被重用,等等,然后继续使用locals()小而整洁的方便; 如果你不知道,或者即使你不是完全确定的话,也要谨慎一点,把事情做得更清楚一点 – 承受一个小小的麻烦,就是要明确你所要做的事情,并享受所有的优点。

顺便说一下,这只是Python正在努力支持“小型,一次性,探索性,也许是交互式”编程的例子之一(通过允许和支持远远超出locals()有风险的便利locals() – 考虑import * evalexec和其他一些方法,为了方便起见,可以拼凑命名空间和风险维护影响),以及“大型的,可重用的,企业级的”应用程序和组件。 它可以做得很好,但只有当你“认识你自己”,并避免使用“便利”的部分,除非你确信你确实可以负担得起。 多数情况下,最关键的是“这对我的命名空间有什么影响,并且它们对编译器,lint&c,人类读者和维护者等的形成和使用有何认识?”。

请记住,“命名空间是一个伟大的想法 – 让我们做更多的! Python是如何得出结论的……但是Python作为一种“成人同意的语言”,可以让根据开发环境,目标和实践来定义所蕴含的边界。 负责任地使用这个权力!)

在一百万年里都不会发生。 目前还不清楚格式化的上下文是什么: locals可以包括几乎任何variables。 self.__dict__不是模糊的。 完全可怕的是让未来的开发人员在本地和本地不知情的情况下挠头。

这是一个故意的谜。 为什么要像以前那样维护您的组织?

关于“堂妹”,而不是obj.__dict__ ,它看起来更好用新的string格式:

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

我用这很多repr方法,例如

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

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

我从来不写代码,我不需要写 – less代码比更多的代码更好,这种使用locals()做法允许我写更less的代码,也很容易阅读和理解。

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

  • 比“%0s”更具可读性
  • 独立于论证顺序
  • 不强制使用string中的所有参数

我倾向于赞成str.format(),因为它应该是Python 3中的方法(按照PEP 3101 ),并且已经可以从2.6中获得。 与locals()虽然,你将不得不这样做:

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

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

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

效果当然是一样的。

现在有一个正式的方法来做到这一点,从Python 3.6.0: 格式化string文字 。

它是这样工作的:

 f'normal string text {local_variable_name}' 

例如,而不是这些:

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

只是这样做:

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

这是官方的例子:

 >>> 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' 

参考:

  • Python 3.6新增function
  • PEP 498
  • 词汇分析描述