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