This is normal for small applications and supposedly “one-time” scripts, especially with the vars improvement mentioned in @ kaizer.se and the .format version mentioned by @RedGlyph.
However, for large, long-life applications and many proponents, this practice can lead to maintenance headaches, and I think when @ S.Lott's answer comes. Let me explain some of the issues raised, as they may not be obvious to those who do not have scars from developing and supporting large applications (or reusable components for such animals).
In a “serious” application, you won’t have hard-coded formatted string code - or, if you had, it would be in some form, for example _('Hello {name}.') , Where _ comes from gettext or similar i18n frameworks / L10n. The fact is that such an application (or reusable modules that can be used in such applications) must support internationalization (AKA i18n) and localization (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, and so on. Thus, the format string is more or less automatically replaced by another at runtime, depending on the current localization settings; instead of being hardcoded, it lives in some kind of database. In all senses and purposes, imagine that a format string is always a variable, not a string literal.
So, you have essentially
formatstring.format(**locals())
and you cannot trivially check which local names will use formatting. You will need to open and examine the L10N database, determine the format strings that will be used here in different settings, check them all.
Thus, in practice, you do not know what local names will be used, which terribly interferes with maintaining the function. You do not dare to rename or delete any local variable, as this can greatly disrupt the user interface for users with some (you) incomprehensible combination of language, language and preferences.
If you have excellent integration / regression testing, a breakdown will be detected before the beta, but QA will yell at you, and the release will be delayed ... and to be honest, aiming at 100% coverage with unit tests is reasonable, it really is not with integration tests, as soon as you consider the combinatorial burst of settings [[for L10N and for many other reasons]] and the supported versions of all the dependencies. Thus, you simply do not carelessly circumvent the risk of breakdown, because "they will be caught in QA" (if you do this, you may not live in an environment that develops large applications or reusable components for a long time ;-).
Thus, in practice, you will never delete the local variable "name", even if People Experience users have long switched this greeting to the more appropriate "Welcome, Dread Overlord!" (and accordingly L10n'ed version). That's because you went for locals() ...
So, you accumulate due to the fact that you have the ability to save and edit your code, and perhaps this local variable "name" exists only because it was extracted from a database or the like, therefore saving it (or some others local) around is not just cool, it also reduces your productivity. The convenience of surface locals() worth what ?)
But wait, worse! Among many useful services, the lint -like program (for example,
Compare this to the "explicit is better than implicit" alternative ...:
blah.format(name=name)
There is nothing of concern for maintenance, performance, and am-I-hampering-lint; bliss! You immediately understood to everyone who concerned (lint included ;-) exactly what local variables are used, and exactly for what purpose.
I could go on, but I think this post is already quite long; -).
So, summing up: " γνῶθι σεαυτόν !" Hmm, I mean, "know yourself!". And "myself" I actually mean "the purpose and scope of your code." If this is a 1-off-or-thereabout thingy, there will never be i18n'd and L10n'd, service in the future is unlikely to be needed, will never be reused in a wider context, etc. Etc., Then go and use locals() for its small but neat convenience; if you know differently, or even if you are not completely sure, make a mistake on the side of caution and make everything more obvious - experience slight inconvenience in writing exactly what you are going to, and enjoy all the benefits arising from this.
By the way, this is just one example where Python seeks to support both “small, one-time, research, and interactive” programming (allowing and supporting dangerous amenities that go far beyond locals() ). import * , eval , exec and several other ways to simplify the namespace and risks for ease of maintenance), as well as "large, reusable, enterprise" applications and components. This can do a pretty good job for both, but only if you "know yourself" and avoid using the "convenient" parts, unless you are absolutely sure that you can actually buy them. Most often, this is a key consideration: "What does this do with my namespaces and the awareness of their formation and use by the compiler, readers and readers, people, etc.?".
Remember: "Namespaces are one great idea - let more of them!" how Zen Python completes ... but Python, as a "language for agreeing adults," allows you to define the boundaries of what this implies, as a consequence of your development environment, goals and practice. Use this power responsibly! -)