Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Scoping It Out

00:00 In the previous lesson, I completed the section on objects and classes by discussing inheritance. In this lesson, I’ll be talking about variable name scoping within Python.

00:10 When you declare a variable in a programming language, there are rules about where that variable can be seen. This is called the scope, and it applies to both variables and names in your code.

00:21 Some items are only accessible within a statement. For example, a variable in a list comprehension isn’t available outside of that comprehension. An item might be available within a block, like a function, or a variable might be available for the whole module.

00:36 Python’s scoping rules determine just how all this works. Python has four levels of scope. This is called the LEGB rule. The L stands for the local context.

00:48 The E is the enclosing context. For example, a list comprehension inside of a function still has access to the variables defined in the function. That’s the enclosing scope.

00:59 G is for global scope, which typically means the module file. And finally, B is the built-in scope, which is kind of what this course is all about, the functions that are available everywhere without needing to import a module.

01:13 There are two built-in functions that allow you to interact with the scope. The first is locals(), and the second is globals().

01:20 And as you might guess, they relate to two of the four levels of LEGB. Let’s go scope it out. Yeah, I’m not getting any better, am I? Calling the built-in locals() function returns a dictionary containing what is defined in the local scope.

01:36 As I’m in a fresh REPL with nothing defined, all that comes back from locals() is some dunder values that are part of Python’s internals. Let me define something.

01:48 Now when I look at locals() again, you’ll see that cat has been appended. Let’s create a function to look at different levels of scope.

01:59 Before I do anything, I’m going to print out the local scope, then do the actual addition,

02:06 and then print out the scope again, and return the result.

02:13 Looking at the local scope, you can see that the definition of the function has been added. Let me invoke it. The first line in the response is the scope before the result variable gets defined.

02:26 It includes the arguments to the function. It also doesn’t include any of the dunder stuff from before. You’ve changed your level of scope, so locals() only returns the local level.

02:36 The second line of output is our second print, which is after the definition of result. In it, you can see that result has been added to the local scope.

02:46 And then the third line is actually the return result from the function in the REPL, which is 3 plus 4. Let’s try something a little more complicated.

02:58 When I demonstrated the built-in open() function, I created a file with some fruit in it. This function will print out a prompt string and then the contents of that file.

03:07 But first, I’m going to show the local scope.

03:12 I’m using a context manager to open the fruit text file,

03:19 adding a header variable,

03:22 printing out the local scope once more.

03:30 Then I print all that stuff out, and locals() one more time. Let’s try this out.

03:42 The first line of output is the local scope for the function, which at that point only contains the function’s prompt argument. The second line is a little longer.

03:52 It’s the local scope inside of the with context manager’s block. This scope includes the prompt argument, the file handle created by the open() context manager, and the header defined within the block.

04:03 The block’s scope includes the enclosing scope of the function. Next, I print out the header, prompt, and contents of the file.

04:12 The extra spaces at the beginning of the line there are because I sent all the arguments into the same print statement. I wasn’t aiming for elegance. The final line is the last call to locals().

04:23 Note that the variables defined within the context manager block were added to the local scope. Python doesn’t actually create a new scope for these blocks. They aren’t bound like the scope of a comprehension or a lambda.

04:36 locals() is the lowest level of scope, while globals() is the highest level you can control. There’s a level above it, the built-ins, but you can’t affect those in your code. Running globals() in the REPL shows all those things I’ve defined already because the REPL module level scope is the global scope.

04:53 Let’s do this in a function. Subtraction to go with my adding. And when I call the function, you see all that same global scope from before, except the new sub() function has been added to it as well.

05:14 Note that the arguments to sub() aren’t there. That’s because they’re within the local scope, not the global one. Calling globals() gives you a reference to the dictionary where global values are stored.

05:24 And like any dictionary, you can add and modify values within it. Felix has now been replaced by Garfield. You don’t have to use globals() a lot.

05:41 In fact, if you need access to a global value, you should prefer using the global keyword. One use of the function, though, is to import variable definitions from a file.

05:50 For example, Django can load configuration from a file and add each value in it into the global scope for reference later.

05:59 To learn more about the LEGB scope rules, check out this video course or tutorial.

06:05 Next up, introspecting objects.

Become a Member to join the conversation.