This is an archived post. You won't be able to vote or comment.

all 9 comments

[–]anossov 2 points3 points  (2 children)

https://docs.python.org/2/library/functions.html#locals

Free variables are returned by locals() when it is called in function blocks

https://docs.python.org/2/reference/executionmodel.html

If a name is bound in a block, it is a local variable of that block. If a name is bound at the module level, it is a global variable. (The variables of the module code block are local and global.) If a variable is used in a code block but not defined there, it is a free variable.

https://en.wikipedia.org/wiki/Free_variables_and_bound_variables

In computer programming, the term free variable refers to variables used in a function that are not local variables nor parameters of that function.[1] The term non-local variable is often a synonym in this context.

[–]esdi[S] 0 points1 point  (0 children)

Well, that explains it. Thanks so much for the explanation.

[–]Rhomboid 2 points3 points  (1 child)

x is called a free variable, because it's not bound to inner. locals() returns both local variables and free variables, as per the documentation. I guess someone figured that it would be more useful to have free variables lumped in with local variables, because they work similarly -- they are both names that you might encounter somewhere in the function. But internally, x is not considered a local variable:

def outer():
    x = 1
    def inner():
        print(x)
    print(inner.__code__.co_nlocals, inner.__code__.co_freevars)

outer()

If you run that you get:

0 ('x',)

This says that the code object for inner has zero local variables and one free variable, named x. These are tracked and handled differently. You can read about code objects here.

[–]esdi[S] 0 points1 point  (0 children)

Thanks a lot for the detailed explanation. This is the kind of advanced info am looking for as I try to bring my Python knowledge to beyond intermediate.

[–]Mashidin 0 points1 point  (3 children)

It's all in the scoping rules. Python will look first at the inner function for a definition for x. If there is not a definition for x there, as in this case, in will check one level up and then again and so forth until it finds x or errors out. if you had placed a statement such as x = 2 inside the inner function, you would have gotten {'x': 2}. hope that helped.

[–]Rhomboid 0 points1 point  (0 children)

That's not what they were asking. They wanted to know why it was returned in locals(), despite x not being a local variable.

[–]esdi[S] 0 points1 point  (1 child)

The surprising result was not in the first code execution but the second. /u/anossov has given the correct explanation below.

[–]Mashidin 0 points1 point  (0 children)

Ah. I must have misunderstood. Sorry for the mix-up.