Function closures and meaning of <locals> syntax in object name in Python
Solution 1:
See PEP 3155 -- Qualified name for classes and functions and the example with nested functions.
For nested classes, methods, and nested functions, the
__qualname__
attribute contains a dotted path leading to the object from the module top-level. A function's local namespace is represented in that dotted path by a component named<locals>
.
Since the __repr__
of a function uses the __qualname__
attribute, you see this extra component in the output when printing a nested function.
I was expecting func1 to simply be
outer.inner
That's not a fully qualified name. With this repr you might mistakenly assume you could import the name outer
and dynamically access the attribute inner
. Remember the qualname is a "dotted path leading to the object", but in this case attribute access is not possible because inner
is a local variable.
Also, is there something special about the enclosing <> syntax when used around a name?
There is nothing special about it, but it gives a pretty strong hint to the programmer that you can't access this namespace directly, because the name is not a valid identifier.
Solution 2:
You can think of outer.<locals>.inner
as saying that inner is a local variable created by the function. inner
is what is referred to a closure in computer science. Roughly speaking a closure is like a lambda in that it acts as a function, but it requires non-global data be bundled with it to operate. In memory it acts as a tuple between information and a reference to the function being called.
foo = outer("foo")
bar = outer("bar")
# In memory these more or less looks like the following:
("foo", outer.inner)
("bar", outer.inner)
# And since it was created from a local namespace and can not be accessed
# from a static context local variables bundled with the function, it
# represents that by adding <local> when printed.
# While something like this looks a whole lot more convenient, it gets way
# more annoying to work with when the local variables used are the length of
# your entire terminal screen.
<function outer."foo".inner at 0x1004d9d30>
There is nothing inherently special about the <> other than informing you that <local>
has some special meaning.
Edit:
I was not completely sure when writing my answer, but after seeing @wim's answer <local>
not only applies to closures created consuming variables within a local context. It can be applied more broadly to all functions (or anything else) created within a local namespace. So in summary foo.<local>.bar
just means that "bar was created within the local namespace of foo".