Python Land

Python List Comprehension: Tutorial With Examples

There’s a concept called set-builder notation in mathematics, also called set comprehension. Inspired by this principle, Python offers list comprehensions. In fact, Python list comprehensions are one of the defining features of the language. It allows us to create concise, readable code that outperforms the uglier alternatives like for loops or using map() .

We’ll first look at the most well-known type: list comprehensions. Once we’ve got a good grasp of how they work, you’ll also learn about set comprehensions and dictionary comprehensions.

Table of Contents

  • 1 What are list comprehensions?
  • 2 Examples of list comprehensions
  • 3 More advanced examples
  • 4 Other comprehensions

What are list comprehensions?

A Python list comprehension is a language construct. It’s used to create a Python list based on an existing list. Sounds a little vague, but after a few examples, that ‘ah-ha!’ moment will follow, trust me.

The basic syntax of a list comprehension is:

The ‘if’-part is optional, as you’ll discover soon. However, we do need a list to start from. Or, more specifically, anything that can be iterated . We’ll use Python’s range() function, which is a special type of iterator called a generator: it generates a new number on each iteration.

Examples of list comprehensions

Enough theory, let’s look at the most basic example, and I encourage you to fire up a Python REPL to try this yourself:

Some observations:

  • The expression part is just x
  • Instead of a list, we use the range() function. We can use [1, 2, 3, 4] too, but using range() is more efficient and requires less typing for longer ranges.

The result is a list of elements, obtained from range() . Not very useful, but we did create our first Python list comprehension. We could just as well use:

So let’s throw in that if-statement to make it more useful:

The if -part acts like a filter. If the condition after the if resolves to True, the item is included. If it resolves to False, it’s omitted. This way, we can get only the even numbers using a list comprehension.

So far, our expression (the x ) has been really simple. Just to make this absolutely clear though, expression can be anything that is valid Python code and is considered an expression. Example:

This expression adds four to x, which is still quite simple. But we could also have done something more complicated, like calling a function with x as the argument:

More advanced examples

You mastered the basics; congrats! Let’s continue with some more advanced examples.

Nested list comprehension

If expression can be any valid Python expression, it can also be another list comprehension. This can be useful when you want to create a matrix:

Or, if you want to flatten the previous matrix:

The same, but with some more whitespace to make this clearer:

The first part loops over the matrix m . The second part loops over the elements in each vector.

Alternatives to list comprehensions

The Python language could do without comprehensions; it would just not look as beautiful. Using functional programming functions like map() and reduce() can do everything a list comprehension can.

Another alternative is using for-loops . If you’re coming from a C-style language, like Java, you’ll be tempted to use for loops. Although it’s not the end of the world, you should know that list comprehensions are more performant and considered a better coding style.

Other comprehensions

If there are list comprehensions, why not create dictionary comprehension as well? Or what about set comprehensions? As you might expect, both exist.

Set comprehensions

The syntax for a Python set comprehension is not much different. We just use curly braces instead of square brackets:

For example:

Dictionary comprehensions

A dictionary requires a key and a value. Otherwise, it’s the same trick again:

The only difference is that we define both the key and value in the expression part.

Get certified with our courses

Learn Python properly through small, easy-to-digest lessons, progress tracking, quizzes to test your knowledge, and practice sessions. Each course will earn you a downloadable course certificate.

The Python Course for Beginners

Related articles

  • JSON in Python: How To Read, Write, and Parse
  • Python List: How To Create, Sort, Append, Remove, And More
  • Python Set: The Why And How With Example Code
  • Python YAML: How to Load, Read, and Write YAML

Leave a Comment Cancel reply

You must be logged in to post a comment.

Learn Python practically and Get Certified .

Popular Tutorials

Popular examples, reference materials, learn python interactively, python introduction.

  • Get Started With Python
  • Your First Python Program
  • Python Comments

Python Fundamentals

  • Python Variables and Literals
  • Python Type Conversion
  • Python Basic Input and Output
  • Python Operators

Python Flow Control

  • Python if...else Statement
  • Python for Loop
  • Python while Loop
  • Python break and continue
  • Python pass Statement

Python Data types

  • Python Numbers, Type Conversion and Mathematics

Python List

  • Python Tuple
  • Python Sets
  • Python Dictionary
  • Python Functions
  • Python Function Arguments
  • Python Variable Scope
  • Python Global Keyword
  • Python Recursion
  • Python Modules
  • Python Package
  • Python Main function

Python Files

  • Python Directory and Files Management
  • Python CSV: Read and Write CSV files
  • Reading CSV files in Python
  • Writing CSV files in Python
  • Python Exception Handling
  • Python Exceptions
  • Python Custom Exceptions

Python Object & Class

  • Python Objects and Classes
  • Python Inheritance
  • Python Multiple Inheritance
  • Polymorphism in Python
  • Python Operator Overloading

Python Advanced Topics

  • List comprehension

Python Lambda/Anonymous Function

  • Python Iterators
  • Python Generators
  • Python Namespace and Scope
  • Python Closures
  • Python Decorators
  • Python @property decorator
  • Python RegEx

Python Date and Time

  • Python datetime
  • Python strftime()
  • Python strptime()
  • How to get current date and time in Python?
  • Python Get Current Time
  • Python timestamp to datetime and vice-versa
  • Python time Module
  • Python sleep()

Additional Topic

  • Precedence and Associativity of Operators in Python
  • Python Keywords and Identifiers
  • Python Asserts
  • Python Json
  • Python *args and **kwargs

Python Tutorials

Python Dictionary Comprehension

Python Dictionary fromkeys()

  • Python range() Function
  • Python List insert()

Python List Comprehension

List comprehension offers a concise way to create a new list based on the values of an existing list.

Suppose we have a list of numbers and we desire to create a new list containing the double value of each element in the list.

Python List Comprehension

  • Syntax of List Comprehension

for every item in list , execute the expression if the condition is True .

Note: The if statement in list comprehension is optional.

  • for Loop vs. List Comprehension

List comprehension makes the code cleaner and more concise than for loop.

Let's write a program to print the square of each list element using both for loop and list comprehension.

List Comprehension

It's much easier to understand list comprehension once you know Python for loop() .

  • Conditionals in List Comprehension

List comprehensions can utilize conditional statements like if…else to filter existing lists.

Let's see an example of an if statement with list comprehension.

Here, list comprehension checks if the number from range(1, 10) is even or odd. If even, it appends the number in the list.

Note : The range() function generates a sequence of numbers. To learn more, visit Python range() .

Let's use if...else with list comprehension to find even and odd numbers.

Here, if an item in the numbers list is divisible by 2 , it appends Even to the list even_odd_list . Else, it appends Odd .

Let's use nested if with list comprehension to find even numbers that are divisible by 5 .

Here, list comprehension checks two conditions:

  • if y is divisible by 2 or not.
  • if yes, is y divisible by 5 or not.

If y satisfies both conditions, the number appends to num_list .

  • Example: List Comprehension with String

We can also use list comprehension with iterables other than lists.

Here, we used list comprehension to find vowels in the string 'Python' .

More on Python List Comprehension

We can also use nested loops in list comprehension. Let's write code to compute a multiplication table.

Here is how the nested list comprehension works:

Nested Loop in List Comprehension

Let's see the equivalent code using nested for loop.

Equivalent Nested for Loop

Here, the nested for loop generates the same output as the nested list comprehension. We can see that the code with list comprehension is much cleaner and concise.

Along with list comprehensions, we also use lambda functions to work with lists.

While list comprehension is commonly used for filtering a list based on some conditions, lambda functions are commonly used with functions like map() and filter() .

They are used for complex operations or when an anonymous function is required.

Let's look at an example.

We can achieve the same result using list comprehension by:

If we compare the two codes, list comprehension is straightforward and simpler to read and understand.

So unless we need to perform complex operations, we can stick to list comprehension.

Visit Python Lambda/ Function to learn more about the use of lambda functions in Python.

Table of Contents

  • Introduction

Video: Python List, Set & Dictionary Comprehension

Sorry about that.

Related Tutorials

Python Tutorial

Python Library

Datagy logo

  • Learn Python
  • Python Lists
  • Python Dictionaries
  • Python Strings
  • Python Functions
  • Learn Pandas & NumPy
  • Pandas Tutorials
  • Numpy Tutorials
  • Learn Data Visualization
  • Python Seaborn
  • Python Matplotlib

List Comprehensions in Python (Complete Guide with Examples)

  • May 6, 2020 December 16, 2022

List Comprehensions in Python (Complete Guide with Examples) Cover image

In this post, we’ll cover everything you need to know about List Comprehensions in Python, using comprehensive examples! List comprehensions provide elegant, concise ways to create, modify, and filter lists in Python.

By the end of this tutorial, you’ll have learned:

  • What list comprehensions are and how they’re different from for loops
  • How to replace your for loops with list comprehensions
  • How to add conditions to your list comprehensions
  • How to nest list comprehensions in Python

Table of Contents

Video Tutorial on Python List Comprehensions

What is a List Comprehension in Python?

A list comprehension is an elegant, concise way to define and create a list in Python. The code is written in a much easier-to-read format. Python List Comprehensions consist of square brackets containing an expression, which is executed for each element in an iterable. Each element can be conditionally included and or transformed by the comprehension.

What is a List in Python?

A Python list a built-in data structure and a compound data type, meaning you can use it to group values together. Lists are heterogeneous, meaning they can have a combination of different data types, such as Booleans, strings, integers, etc.

What makes Python lists different from other Python compound data types (e.g., dictionaries, tuples), in that it’s an ordered collection (meaning items have an index) and they are mutable (meaning they can be changed).

Why Use List Comprehensions in Python?

List comprehensions provide concise ways to create lists. One main use is to make lists where each element is the result of an operation applied to each item of an iterable item, or to create a smaller set of items of a given list.

List Comprehension Benefits

Python list comprehensions are a more Pythonic way to create, modify, and filter lists.

This is incredibly helpful because it represents a single tool to complete a number of tasks, rather than relying on for loops, map() functions, or filter() functions. This is great as you can apply one method, rather trying to fit a use case to a scenario.

List comprehensions are also, in many cases, easier to read than for-loops. They don’t require you to instantiate an empty list or to append items to a new list.

How Do You Write a List Comprehension?

List comprehensions in Python follow the structure of: [Expression for Item in Iterable].

  • An expression can be any expression – a single item, a function applied to an item, etc.
  • An item is an item in an iterable,
  • An iterable is an object that you can iterate over (such as a list, tuple, etc.)

Let’s see what this looks like with an example:

In the above example, we iterate over a list that contains the numbers 1-5 and square each number. If we wrote this out as Python code, it would look like this:

List Comprehension vs. For Loop

List comprehensions have concise terminology for replacing for-loops that are used to iterate over items. Python for loops are often used to create lists from another iterable.. However, they are slower to write and to execute.

Let’s take a look at an example of a for loop. Below, we use a for-loop to create a list of 0 to 9. To do this, we’ll:

  • Instantiate (create) an empty list,
  • Loop of items in a range of elements, and
  • Append each item to the end of the list.

To do this using a list comprehension, we can simply write the code below:

Both of these pieces of code return:

It’s important to note that every list comprehension can be written as a for loop, but not every for-loop can be a list comprehension. Always strive for readability!

Why are List Comprehensions Faster than For-Loops?

List comprehensions are faster than for loops because they are optimized to be interpreted a predictable pattern, do not need to look up the list each time, and do not need to execute the append statement during each run.

List Comprehensions vs. Lambda and Map() Functions

Lambda functions are anonymous functions in Python, meaning that they are used only when they are created. These functions are generally used with the map() function, especially in a way to alter lists.

Let’s explore how a map and lambda functions can be used to alter a list. In the example, we’ll change a list of weight in kilograms to pounds:

To write this as a list comprehension, we could write the following code:

Both of these returns the following:

Python List Comprehension If Else (Conditionals)

Conditionals can enhance Python list comprehensions significantly. They serve two main purposes:

  • To filter a list, and
  • To modify items in a list.

Depending on what you want to use a Python list comprehension if else statement for, the conditional goes into a different place.

Modifying a List with a List Comprehension If Else Statement

If you want to modify items in a list based on a condition, the conditional goes in the front of the list comprehension:

new_list = [expression (if-else statement) for item in iterable]

Let’s try this with an example. We’ll take a list with numbers from 1-5 and label items as either even or odd:

For each item in the list, Python evaluates the modulus of a number and 2 – if the value returned is 0, the string ‘even’ is added to the new list. If it not 0, ‘odd’ is added to the new list.

This returns the following:

Filtering a List with List Comprehension If Statement

If you want to filter a list, the conditional goes to the end of the list comprehension:

new_list = [expression for item in iterable (if statement)]

Let’s try this with another example. If we had a list of numbers from 1-5 and wanted to filter out any even numbers, we could write:

For each item in the list, Python evaluates the modulus of the item and 2, and if the value returned is 1, then the original item is added to the new list.

Multiple If Conditions in List Comprehensions

Multiple if conditions can be applied within Python List Comprehensions by chaining them together.

Let’s take a look at an example. If we wanted to generate a list of numbers that are divisible by 2 and by 5, we could do this with a for-loop:

To do this with a list comprehension, we can cut down the amount of code significantly:

Nested List Comprehensions

List Comprehensions can also be nested, which is especially useful when working with lists of lists. This can include generating lists of lists, reducing them to normal lists, or transposing them.

For example, if we had a list of list that looked like the list below:

nested_list = [[1,2,3],[4,5,6],[7,8,9]]

If we wanted to reduce this to a flattened list, we could do this with a for-loop:

To do this with a list comprehension, we can write:

Both of these return:

Example: Finding Common Items in Two Lists Using List Comprehensions

We can use nested list comprehensions to identify common items between two lists.

Let’s first see how this can be done using for-loops:

To do this using a list comprehension, we can simply write:

Both of these return the same list, but the list comprehension is much easier to understand!

When Not to Use List Comprehensions

List Comprehensions are great because they are Pythonic – until they’re not. Python strives for readability (see The Zen of Python ). While in most cases, list comprehensions tend to be more readable than other methods such as for-loops, they can also become quite complex. In these cases, it may be better to use another method to help future readability.

It’s important to not write incredibly long list comprehensions to replace simple for-loops.

Conclusion: Python List Comprehensions

In this post, we learned how to use Python list comprehensions to create, modify, and filters lists. Specifically, we learned:

  • The benefits of list comprehensions,
  • How to use list comprehensions to replace for-loops and map() functions,
  • How to add conditionals to list comprehensions,
  • How to use nested list comprehensions, and importantly,
  • When not to use list comprehensions.

Thanks for reading! If you found this tutorial helpful, check out some of our other Python tutorials, including an in-depth look at dictionary comprehensions.

Additional Resources

To learn more about related topics, check out the tutorials below:

  • Python Dictionary Comprehensions (With Examples)
  • Python Lists: A Complete Overview
  • Python For Loop Tutorial – All You Need to Know!
  • Python Dictionaries: A Complete Overview

Nik Piepenbreier

Nik is the author of datagy.io and has over a decade of experience working with data analytics, data science, and Python. He specializes in teaching developers how to use Python for data science using hands-on tutorials. View Author posts

16 thoughts on “List Comprehensions in Python (Complete Guide with Examples)”

Pingback:  Python: Reverse a String (6 Easy Ways) • datagy

Pingback:  Python Lowercase String with .lower(), .casefold(), and .islower() • datagy

Pingback:  Python: Convert Degrees to Radians (and Radians to Degrees) • datagy

Pingback:  Pandas: Iterate over a Pandas Dataframe Rows • datagy

Pingback:  Python: Remove Duplicates From a List (7 Ways) • datagy

Pingback:  Python: Flatten Lists of Lists (4 Ways) • datagy

Pingback:  Python Sum of Squares: 3 Different Ways • datagy

Pingback:  Relative and Absolute Frequencies in Python and Pandas• datagy

Pingback:  Python List Length or Size: 5 Ways to Get Length of List • datagy

Pingback:  Python: Split a List into n Chunks (4 Ways) • datagy

Pingback:  Python List Difference: Find the Difference between 2 Python Lists • datagy

Pingback:  Python: Copy a File (4 Different Ways) • datagy

Pingback:  Python: Count Number of Occurrences in List (6 Ways) • datagy

Pingback:  Python Merge Dictionaries - Combine Dictionaries (7 Ways) • datagy

Pingback:  Python Multiline Comments (2 Different Options) • datagy

Pingback:  Python Lists: A Complete Overview • datagy

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Save my name, email, and website in this browser for the next time I comment.

Python Simplified

PythonSimplifiedcomLogo

Comprehensive Guide to Python List Comprehensions

  • June 28, 2021
  • Author - Chetan Ambi
  • Category - Python

Python List Comprehensions

Table of Contents

Introduction

Python list comprehensions provide a concise way of creating lists. For example, if you want to write code that squares all the numbers from 1 to 10, you would write as below (the traditional way). First, you need to create an empty list, then loop through all the elements, square each element and then append to the list. 

You can achieve the same result using Python list comprehensions as shown below. As you can see, list comprehensions are readable, concise, and more Pythonic.

Understanding list comprehensions don’t stop here. But there is much to learn about list comprehensions. In this comprehensive guide, we’ll dig deeper and understand more about the list comprehensions. Once you go through this guide, you would have mastered list comprehensions in Python. I know you are excited!! Let’s get started.

Lists are one of the sequence types in Python. Refer to our in-depth article  here   for introduction to sequence types in Python.

List Comprehensions

As mentioned earlier, list comprehensions are concise ways of creating lists. The general syntax of list comprehension looks as below. It consists of three parts: iteration, transformation, and filtering .

list-comprehension-syntax

  • Iteration: This is the first part that gets executed in a list comprehension that iterates through all the elements in the iterable. 
  • Transformation : As the name says transformation will be applied to each element in the iterable. In the above example, we are squaring each element from the iterable.
  • Filter (optional) : This is optional and it filters out some of the elements based on the conditions. The above example loops through all the elements from 1 to 10, filters out odd numbers and considers only positive numbers before applying the transformation.

Let’s see couple more examples of list comprehensions. The below example converts all the strings in the list to upper case.

Here is another example that displays all the numbers less than 50 that are even and divisible by 7.

What happens under the hood

Now that you have understood the syntax and how to write list comprehensions, let’s try to understand what goes on under the hood.

Whenever Python encounters a list comprehension, it creates a temporary function that gets executed during the runtime. The result is stored at some memory location and then gets assigned the variable on the LHS. Refer to the diagram below.

list-comprehension-as-a-function

You can confirm this by running the list comprehension through the dis module. In the below example, the built-in compile() function generates the bytecode of the list comprehension. Then when you run it through the dis module, it generates byte code instructions as below.

Refer to the output of dis below. The bytecode instruction has MAKE_FUNCTION , CALL_FUNCTION . This confirms that Python converts list comprehensions to temporary functions internally.

Since list comprehensions are internally treated as functions, they exhibit some of the properties of functions. List comprehensions also have local and global scopes . In the below example, num is a local variable and factor is a global variable. But you can still access the global variable factor inside the list comprehension.

Nested list comprehensions

It’s possible to write nested list comprehensions meaning you can include a list comprehension within another. One of the uses of nested list comprehensions is that they can be used to create n-dimensional matrices. 

In the example below, we are able to create a 5×5 matrix using nested list comprehensions.

Nested loops in list comprehensions

It’s also possible to include multiple loops in list comprehensions. You can use as many loops as you want. But, as a best practice, you should limit the depth to two so as not to lose the readability. The below example uses two for loops. 

One of the uses of nested loops is to vectorize the matrix (flattening the matrix). In the below example, my_matrix is a 2×2 matrix. Using nested loops, we are able to create a vectorized version.

Multi-line list comprehensions

Most of the examples you have seen are one-line list comprehensions. But note that you can also write multi-line list comprehensions and are preferred when you can’t fit all the instructions in one line. They just increase the readability and won’t offer any added benefit. See an example below.

The same multi-line list comprehension is written in the single line as below. As you can see this is not readable.

List comprehensions with conditionals (if-else)

You can also use if-else conditions with list comprehensions. An example of using the if condition was covered in the syntax section. However, let me provide two examples here one with if and another with if-else .

Using if conditional:

The below example uses the if statement to filter out the languages that start with the letter “P”. Notice that if statement comes after the loop. In general, if condition is used to filtering.

Using if-else conditional:

When the if-else is used with list comprehension, the syntax is a little different compared to comprehension with only if statement. The below example calculates the square even numbers and cube of odd numbers between 1 to 10 using an if-else statement.

Do you want to master Python for else and while else? Please read our detailed article here .

List comprehensions with walrus operator

You can create list comprehensions using the walrus operator := and is also called as “assignment expression”. The walrus operator was first introduced in Python 3.8. As mentioned in PEP 572, it was introduced to speedup the coding by writing short code.

The below example creates a list of even numbers between 0 to 10 using the walrus operator.

List comprehensions Vs map and filter

The map and filter function can also be used as alternatives to list comprehensions as both the functions help us in creating the list object. 

map vs. list comprehension

The below example shows that the list comprehension function can be an alternative to the map function. 

The below code also confirms that list comprehensions are faster than the map function.

filter vs. list comprehension

From the below example, it is evident that list comprehensions can be an alternative to the filter function. 

Even in this case, list comprehensions are faster than filter functions. Refer to the below example.

Set Comprehensions

You can also create set comprehensions but the only difference is they are enclosed within curly brackets { } . The below example creates set comprehension from a list. It converts all the strings in the given list to upper case and creates a set (of course, by removing the duplicates)

Dictionary Comprehensions

Dictionary comprehensions are also enclosed with { } brackets but its contents follow key: value format as you expect. The below example creates a dictionary comprehension from a list. It creates a dictionary with string as key and length of the string as value. 

Generator Expressions

The generator expressions look very similar to list comprehension with the difference is that generator expressions are enclosed within parenthesis ( ) . You could say generator comprehension a lazy version of list comprehension . 

In the below example, we created a list of squares of numbers between 1 to 10 that are even-numbered using generator expression. Note that the output of generator expression is a generator object. To get the result, you need to pass the generator object to the list constructor (or to a tuple or set the constructor based on your need). 

Generator Expressions Vs. List Comprehensions

a) Storage efficiency: A list comprehension stores the entire list in the memory whereas a generator yields one item at a time on-demand. Hence, generator expressions are more memory efficient than lists. As seen from the below example, the same code generator expression takes far less memory.

b) Speed efficiency: List comprehensions are faster than generator expressions. The same code takes 7 seconds for list comprehensions but 9 seconds with generator expressions.

c) Use list comprehensions if you need to iterate over the list multiple times. If not, consider using generator expressions. Because you won’t be able to iterate generator expression again once you already iterated over. See the example below. When you iterate over a second time, it returns an empty list. 

So, which one should you consider? list comprehensions or generator expressions. There is always a trade-off between memory and speed efficiency so you need to choose based on the requirements. 

Do we have tuple comprehensions?

No, there are no tuple comprehensions! As we have gone through earlier, list comprehensions loop through each item in the iterable and append to the list under the hood. You are mutating the list by appending elements to it. List, set and dictionary comprehensions exist because they are mutable. But tuple is an immutable object. Hence, we don’t have tuple comprehensions. 

Refer to this detailed discussion on the same subject on Stack Overflow.

  • List comprehensions provide a concise way of creating lists.
  • Apart from list comprehensions , Python also has set comprehensions , dictionary comprehensions , and generator expressions .
  • Under the hood, list comprehensions converted to equivalent functions.
  • List comprehensions come in different formats  — nested list comprehensions, with nested loop, with multi-lines, with walrus operator, with if and if-else conditionals, etc.
  • List comprehensions with if conditional is used for filtering while if-else is used when you want to perform different transformations based on the conditions.
  • List comprehensions can be a replacement for map, filter, reduce, and for loop . In most cases list comprehensions is faster than all these methods.
  • If you need to use more than two for loops in the comprehension then it’s better to use the traditional way of creating a list. Because using more than two for loops is not easy to readable.
  • Consider using generator expressions instead of list comprehension if you are dealing with large data and not iterating over the list multiple times.
  • Comprehensions are supposed to be concise , clear , and Pythonic . Don’t write complicated lengthy comprehensions that will defeat the very purpose of using them. 

[1]. https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions

Chetan Ambi

Chetan Ambi

python list comprehension referenced before assignment

Fix "local variable referenced before assignment" in Python

python list comprehension referenced before assignment

Introduction

If you're a Python developer, you've probably come across a variety of errors, like the "local variable referenced before assignment" error. This error can be a bit puzzling, especially for beginners and when it involves local/global variables.

Today, we'll explain this error, understand why it occurs, and see how you can fix it.

The "local variable referenced before assignment" Error

The "local variable referenced before assignment" error in Python is a common error that occurs when a local variable is referenced before it has been assigned a value. This error is a type of UnboundLocalError , which is raised when a local variable is referenced before it has been assigned in the local scope.

Here's a simple example:

Running this code will throw the "local variable 'x' referenced before assignment" error. This is because the variable x is referenced in the print(x) statement before it is assigned a value in the local scope of the foo function.

Even more confusing is when it involves global variables. For example, the following code also produces the error:

But wait, why does this also produce the error? Isn't x assigned before it's used in the say_hello function? The problem here is that x is a global variable when assigned "Hello ". However, in the say_hello function, it's a different local variable, which has not yet been assigned.

We'll see later in this Byte how you can fix these cases as well.

Fixing the Error: Initialization

One way to fix this error is to initialize the variable before using it. This ensures that the variable exists in the local scope before it is referenced.

Let's correct the error from our first example:

In this revised code, we initialize x with a value of 1 before printing it. Now, when you run the function, it will print 1 without any errors.

Fixing the Error: Global Keyword

Another way to fix this error, depending on your specific scenario, is by using the global keyword. This is especially useful when you want to use a global variable inside a function.

No spam ever. Unsubscribe anytime. Read our Privacy Policy.

Here's how:

In this snippet, we declare x as a global variable inside the function foo . This tells Python to look for x in the global scope, not the local one . Now, when you run the function, it will increment the global x by 1 and print 1 .

Similar Error: NameError

An error that's similar to the "local variable referenced before assignment" error is the NameError . This is raised when you try to use a variable or a function name that has not been defined yet.

Running this code will result in a NameError :

In this case, we're trying to print the value of y , but y has not been defined anywhere in the code. Hence, Python raises a NameError . This is similar in that we are trying to use an uninitialized/undefined variable, but the main difference is that we didn't try to initialize y anywhere else in our code.

Variable Scope in Python

Understanding the concept of variable scope can help avoid many common errors in Python, including the main error of interest in this Byte. But what exactly is variable scope?

In Python, variables have two types of scope - global and local. A variable declared inside a function is known as a local variable, while a variable declared outside a function is a global variable.

Consider this example:

In this code, x is a global variable, and y is a local variable. x can be accessed anywhere in the code, but y can only be accessed within my_function . Confusion surrounding this is one of the most common causes for the "variable referenced before assignment" error.

In this Byte, we've taken a look at the "local variable referenced before assignment" error and another similar error, NameError . We also delved into the concept of variable scope in Python, which is an important concept to understand to avoid these errors. If you're seeing one of these errors, check the scope of your variables and make sure they're being assigned before they're being used.

python list comprehension referenced before assignment

Building Your First Convolutional Neural Network With Keras

Most resources start with pristine datasets, start at importing and finish at validation. There's much more to know. Why was a class predicted? Where was...

David Landup

© 2013- 2024 Stack Abuse. All rights reserved.

python list comprehension referenced before assignment

Explore your training options in 10 minutes Get Started

  • Graduate Stories
  • Partner Spotlights
  • Bootcamp Prep
  • Bootcamp Admissions
  • University Bootcamps
  • Coding Tools
  • Software Engineering
  • Web Development
  • Data Science
  • Tech Guides
  • Tech Resources
  • Career Advice
  • Online Learning
  • Internships
  • Apprenticeships
  • Tech Salaries
  • Associate Degree
  • Bachelor's Degree
  • Master's Degree
  • University Admissions
  • Best Schools
  • Certifications
  • Bootcamp Financing
  • Higher Ed Financing
  • Scholarships
  • Financial Aid
  • Best Coding Bootcamps
  • Best Online Bootcamps
  • Best Web Design Bootcamps
  • Best Data Science Bootcamps
  • Best Technology Sales Bootcamps
  • Best Data Analytics Bootcamps
  • Best Cybersecurity Bootcamps
  • Best Digital Marketing Bootcamps
  • Los Angeles
  • San Francisco
  • Browse All Locations
  • Digital Marketing
  • Machine Learning
  • See All Subjects
  • Bootcamps 101
  • Full-Stack Development
  • Career Changes
  • View all Career Discussions
  • Mobile App Development
  • Cybersecurity
  • Product Management
  • UX/UI Design
  • What is a Coding Bootcamp?
  • Are Coding Bootcamps Worth It?
  • How to Choose a Coding Bootcamp
  • Best Online Coding Bootcamps and Courses
  • Best Free Bootcamps and Coding Training
  • Coding Bootcamp vs. Community College
  • Coding Bootcamp vs. Self-Learning
  • Bootcamps vs. Certifications: Compared
  • What Is a Coding Bootcamp Job Guarantee?
  • How to Pay for Coding Bootcamp
  • Ultimate Guide to Coding Bootcamp Loans
  • Best Coding Bootcamp Scholarships and Grants
  • Education Stipends for Coding Bootcamps
  • Get Your Coding Bootcamp Sponsored by Your Employer
  • GI Bill and Coding Bootcamps
  • Tech Intevriews
  • Our Enterprise Solution
  • Connect With Us
  • Publication
  • Reskill America
  • Partner With Us

Career Karma

  • Resource Center
  • Bachelor’s Degree
  • Master’s Degree

Python local variable referenced before assignment Solution

When you start introducing functions into your code, you’re bound to encounter an UnboundLocalError at some point. This error is raised when you try to use a variable before it has been assigned in the local context .

In this guide, we talk about what this error means and why it is raised. We walk through an example of this error in action to help you understand how you can solve it.

Find your bootcamp match

What is unboundlocalerror: local variable referenced before assignment.

Trying to assign a value to a variable that does not have local scope can result in this error:

Python has a simple rule to determine the scope of a variable. If a variable is assigned in a function , that variable is local. This is because it is assumed that when you define a variable inside a function you only need to access it inside that function.

There are two variable scopes in Python: local and global. Global variables are accessible throughout an entire program; local variables are only accessible within the function in which they are originally defined.

Let’s take a look at how to solve this error.

An Example Scenario

We’re going to write a program that calculates the grade a student has earned in class.

We start by declaring two variables:

These variables store the numerical and letter grades a student has earned, respectively. By default, the value of “letter” is “F”. Next, we write a function that calculates a student’s letter grade based on their numerical grade using an “if” statement :

Finally, we call our function:

This line of code prints out the value returned by the calculate_grade() function to the console. We pass through one parameter into our function: numerical. This is the numerical value of the grade a student has earned.

Let’s run our code and see what happens:

An error has been raised.

The Solution

Our code returns an error because we reference “letter” before we assign it.

We have set the value of “numerical” to 42. Our if statement does not set a value for any grade over 50. This means that when we call our calculate_grade() function, our return statement does not know the value to which we are referring.

We do define “letter” at the start of our program. However, we define it in the global context. Python treats “return letter” as trying to return a local variable called “letter”, not a global variable.

We solve this problem in two ways. First, we can add an else statement to our code. This ensures we declare “letter” before we try to return it:

Let’s try to run our code again:

Our code successfully prints out the student’s grade.

If you are using an “if” statement where you declare a variable, you should make sure there is an “else” statement in place. This will make sure that even if none of your if statements evaluate to True, you can still set a value for the variable with which you are going to work.

Alternatively, we could use the “global” keyword to make our global keyword available in the local context in our calculate_grade() function. However, this approach is likely to lead to more confusing code and other issues. In general, variables should not be declared using “global” unless absolutely necessary . Your first, and main, port of call should always be to make sure that a variable is correctly defined.

In the example above, for instance, we did not check that the variable “letter” was defined in all use cases.

That’s it! We have fixed the local variable error in our code.

The UnboundLocalError: local variable referenced before assignment error is raised when you try to assign a value to a local variable before it has been declared. You can solve this error by ensuring that a local variable is declared before you assign it a value.

Now you’re ready to solve UnboundLocalError Python errors like a professional developer !

About us: Career Karma is a platform designed to help job seekers find, research, and connect with job training programs to advance their careers. Learn about the CK publication .

What's Next?

icon_10

Get matched with top bootcamps

Ask a question to our community, take our careers quiz.

James Gallagher

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Apply to top tech training programs in one click

Local variable referenced before assignment in Python

avatar

Last updated: Apr 8, 2024 Reading time · 4 min

banner

# Local variable referenced before assignment in Python

The Python "UnboundLocalError: Local variable referenced before assignment" occurs when we reference a local variable before assigning a value to it in a function.

To solve the error, mark the variable as global in the function definition, e.g. global my_var .

unboundlocalerror local variable name referenced before assignment

Here is an example of how the error occurs.

We assign a value to the name variable in the function.

# Mark the variable as global to solve the error

To solve the error, mark the variable as global in your function definition.

mark variable as global

If a variable is assigned a value in a function's body, it is a local variable unless explicitly declared as global .

# Local variables shadow global ones with the same name

You could reference the global name variable from inside the function but if you assign a value to the variable in the function's body, the local variable shadows the global one.

accessing global variables in functions

Accessing the name variable in the function is perfectly fine.

On the other hand, variables declared in a function cannot be accessed from the global scope.

variables declared in function cannot be accessed in global scope

The name variable is declared in the function, so trying to access it from outside causes an error.

Make sure you don't try to access the variable before using the global keyword, otherwise, you'd get the SyntaxError: name 'X' is used prior to global declaration error.

# Returning a value from the function instead

An alternative solution to using the global keyword is to return a value from the function and use the value to reassign the global variable.

return value from the function

We simply return the value that we eventually use to assign to the name global variable.

# Passing the global variable as an argument to the function

You should also consider passing the global variable as an argument to the function.

pass global variable as argument to function

We passed the name global variable as an argument to the function.

If we assign a value to a variable in a function, the variable is assumed to be local unless explicitly declared as global .

# Assigning a value to a local variable from an outer scope

If you have a nested function and are trying to assign a value to the local variables from the outer function, use the nonlocal keyword.

assign value to local variable from outer scope

The nonlocal keyword allows us to work with the local variables of enclosing functions.

Had we not used the nonlocal statement, the call to the print() function would have returned an empty string.

not using nonlocal prints empty string

Printing the message variable on the last line of the function shows an empty string because the inner() function has its own scope.

Changing the value of the variable in the inner scope is not possible unless we use the nonlocal keyword.

Instead, the message variable in the inner function simply shadows the variable with the same name from the outer scope.

# Discussion

As shown in this section of the documentation, when you assign a value to a variable inside a function, the variable:

  • Becomes local to the scope.
  • Shadows any variables from the outer scope that have the same name.

The last line in the example function assigns a value to the name variable, marking it as a local variable and shadowing the name variable from the outer scope.

At the time the print(name) line runs, the name variable is not yet initialized, which causes the error.

The most intuitive way to solve the error is to use the global keyword.

The global keyword is used to indicate to Python that we are actually modifying the value of the name variable from the outer scope.

  • If a variable is only referenced inside a function, it is implicitly global.
  • If a variable is assigned a value inside a function's body, it is assumed to be local, unless explicitly marked as global .

If you want to read more about why this error occurs, check out [this section] ( this section ) of the docs.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

  • SyntaxError: name 'X' is used prior to global declaration

book cover

Borislav Hadzhiev

Web Developer

buy me a coffee

Copyright © 2024 Borislav Hadzhiev

How to Fix Local Variable Referenced Before Assignment Error in Python

How to Fix Local Variable Referenced Before Assignment Error in Python

Table of Contents

Fixing local variable referenced before assignment error.

In Python , when you try to reference a variable that hasn't yet been given a value (assigned), it will throw an error.

That error will look like this:

In this post, we'll see examples of what causes this and how to fix it.

Let's begin by looking at an example of this error:

If you run this code, you'll get

The issue is that in this line:

We are defining a local variable called value and then trying to use it before it has been assigned a value, instead of using the variable that we defined in the first line.

If we want to refer the variable that was defined in the first line, we can make use of the global keyword.

The global keyword is used to refer to a variable that is defined outside of a function.

Let's look at how using global can fix our issue here:

Global variables have global scope, so you can referenced them anywhere in your code, thus avoiding the error.

If you run this code, you'll get this output:

In this post, we learned at how to avoid the local variable referenced before assignment error in Python.

The error stems from trying to refer to a variable without an assigned value, so either make use of a global variable using the global keyword, or assign the variable a value before using it.

Thanks for reading!

python list comprehension referenced before assignment

  • Privacy Policy
  • Terms of Service

python list comprehension referenced before assignment

Cookie Policy

We use cookies to operate this website, improve usability, personalize your experience, and improve our marketing. Privacy Policy .

By clicking "Accept" or further use of this website, you agree to allow cookies.

  • Data Science
  • Data Analytics
  • Machine Learning

alfie-grace-headshot-square2.jpg

Python List Comprehension: single, multiple, nested, & more

The general syntax for list comprehension in Python is:

Quick Example

We've got a list of numbers called num_list , as follows:

Using list comprehension , we'd like to append any values greater than ten to a new list. We can do this as follows:

This solution essentially follows the same process as using a for loop to do the job, but using list comprehension can often be a neater and more efficient technique. The example below shows how we could create our new list using a for loop.

Using list comprehension instead of a for loop, we've managed to pack four lines of code into one clean statement.

In this article, we'll first look at the different ways to use list comprehensions to generate new lists. Then we'll see what the benefits of using list comprehensions are. Finally, we'll see how we can tackle multiple list comprehensions .

How list comprehension works

A list comprehension works by translating values from one list into another by placing a for statement inside a pair of brackets, formally called a generator expression .

A generator is an iterable object, which yields a range of values. Let's consider the following example, where for num in num_list is our generator and num is the yield.

In this case, Python has iterated through each item in num_list , temporarily storing the values inside of the num variable. We haven't added any conditions to the list comprehension, so all values are stored in the new list.

Conditional statements in list comprehensions

Let's try adding in an if statement so that the comprehension only adds numbers greater than four:

The image below represents the process followed in the above list comprehension:

python list comprehension referenced before assignment

We could even add in another condition to omit numbers smaller than eight. Here, we can use and inside of a list comprehension:

But we could also write this without and as:

When using conditionals, Python checks whether our if statement returns True or False for each yield. When the if statement returns True , the yield is appended to the new list.

Adding functionality to list comprehensions

List comprehensions aren't just limited to filtering out unwanted list values, but we can also use them to apply functionality to the appended values. For example, let's say we'd like to create a list that contains squared values from the original list:

We can also combine any added functionality with comparison operators. We've got a lot of use out of num_list , so let's switch it up and start using a different list for our examples:

In the above example, our list comprehension has squared any values in alternative_list that fall between thirty and fifty. To help demonstrate what's happening above, see the diagram below:

python list comprehension referenced before assignment

Using comparison operators

List comprehension also works with or , in and not .

Like in the example above using and , we can also use or :

Using in , we can check other lists as well:

Likewise, not in is also possible:

Lastly, we can use if statements before generator expressions within a list comprehension. By doing this, we can tell Python how to treat different values:

The example above stores values in our new list if they are greater than forty; this is covered by num if num > 40 . Python stores zero in their place for values that aren't greater than forty, as instructed by else 0 . See the image below for a visual representation of what's happening:

python list comprehension referenced before assignment

Multiple List Comprehension

Naturally, you may want to use a list comprehension with two lists at the same time. The following examples demonstrate different use cases for multiple list comprehension.

Flattening lists

The following synax is the most common version of multiple list comprehension, which we'll use to flatten a list of lists:

The order of the loops in this style of list comprehension is somewhat counter-intuitive and difficult to remember, so be prepared to look it up again in the future! Regardless, the syntax for flattening lists is helpful for other problems that would require checking two lists for values.

Nested lists

We can use multiple list comprehension when nested lists are involved. Let's say we've got a list of lists populated with string-type values. If we'd like to convert these values from string-type to integer-type, we could do this using multiple list comprehensions as follows:

Readability

The problem with using multiple list comprehensions is that they can be hard to read, making life more difficult for other developers and yourself in the future. To demonstrate this, here's how the first solution looks when combining a list comprehension with a for loop:

Our hybrid solution isn't as sleek to look at, but it's also easier to pick apart and figure out what's happening behind the scenes. There's no limit on how deep multiple list comprehensions can go. If list_of_lists had more lists nested within its nested lists, we could do our integer conversion as follows:

As the example shows, our multiple list comprehensions have now become very difficult to read. It's generally agreed that multiple list comprehensions shouldn't go any deeper than two levels ; otherwise, it could heavily sacrifice readability. To prove the point, here's how we could use for loops instead to solve the problem above:

Speed Test: List Comprehension vs. for loop

When working with lists in Python, you'll likely often find yourself in situations where you'll need to translate values from one list to another based on specific criteria.

Generally, if you're working with small datasets, then using for loops instead of list comprehensions isn't the end of the world. However, as the sizes of your datasets start to increase, you'll notice that working through lists one item at a time can take a long time.

Let's generate a list of ten thousand random numbers, ranging in value from one to a million, and store this as num_list . We can then use a for loop and a list comprehension to generate a new list containing the num_list values greater than half a million. Finally, using %timeit , we can compare the speed of the two approaches:

The list comprehension solution runs twice as fast, so not only does it use less code, but it's also much quicker. With that in mind, it's also worth noting that for loops can be much more readable in certain situations, such as when using multiple list comprehensions.

Ultimately, if you're in a position where multiple list comprehensions are required, it's up to you if you'd prefer to prioritize performance over readability.

List comprehensions are an excellent tool for generating new lists based on your requirements. They're much faster than using a for loop and have the added benefit of making your code look neat and professional.

For situations where you're working with nested lists, multiple list comprehensions are also available to you. The concept of using comprehensions may seem a little complex at first, but once you've wrapped your head around them, you'll never look back!

Get updates in your inbox

Join over 7,500 data science learners.

Recent articles:

The 6 best python courses for 2024 – ranked by software engineer, best course deals for black friday and cyber monday 2024, sigmoid function, dot product, meet the authors.

alfie-grace-headshot-square2.jpg

Alfie graduated with a Master's degree in Mechanical Engineering from University College London. He's currently working as Data Scientist at Square Enix. Find him on  LinkedIn .

Brendan Martin

Back to blog index

Python Tutorial

File handling, python modules, python numpy, python pandas, python matplotlib, python scipy, machine learning, python mysql, python mongodb, python reference, module reference, python how to, python examples, python - list comprehension, list comprehension.

List comprehension offers a shorter syntax when you want to create a new list based on the values of an existing list.

Based on a list of fruits, you want a new list, containing only the fruits with the letter "a" in the name.

Without list comprehension you will have to write a for statement with a conditional test inside:

With list comprehension you can do all that with only one line of code:

Advertisement

The return value is a new list, leaving the old list unchanged.

The condition is like a filter that only accepts the items that valuate to True .

Only accept items that are not "apple":

The condition if x != "apple"  will return True for all elements other than "apple", making the new list contain all fruits except "apple".

The condition is optional and can be omitted:

With no if statement:

The iterable can be any iterable object, like a list, tuple, set etc.

You can use the range() function to create an iterable:

Same example, but with a condition:

Accept only numbers lower than 5:

The expression is the current item in the iteration, but it is also the outcome, which you can manipulate before it ends up like a list item in the new list:

Set the values in the new list to upper case:

You can set the outcome to whatever you like:

Set all values in the new list to 'hello':

The expression can also contain conditions, not like a filter, but as a way to manipulate the outcome:

Return "orange" instead of "banana":

The expression in the example above says:

"Return the item if it is not banana, if it is banana return orange".

Get Certified

COLOR PICKER

colorpicker

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail: [email protected]

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail: [email protected]

Top Tutorials

Top references, top examples, get certified.

Python Enhancement Proposals

  • Python »
  • PEP Index »

PEP 572 – Assignment Expressions

The importance of real code, exceptional cases, scope of the target, relative precedence of :=, change to evaluation order, differences between assignment expressions and assignment statements, specification changes during implementation, _pydecimal.py, datetime.py, sysconfig.py, simplifying list comprehensions, capturing condition values, changing the scope rules for comprehensions, alternative spellings, special-casing conditional statements, special-casing comprehensions, lowering operator precedence, allowing commas to the right, always requiring parentheses, why not just turn existing assignment into an expression, with assignment expressions, why bother with assignment statements, why not use a sublocal scope and prevent namespace pollution, style guide recommendations, acknowledgements, a numeric example, appendix b: rough code translations for comprehensions, appendix c: no changes to scope semantics.

This is a proposal for creating a way to assign to variables within an expression using the notation NAME := expr .

As part of this change, there is also an update to dictionary comprehension evaluation order to ensure key expressions are executed before value expressions (allowing the key to be bound to a name and then re-used as part of calculating the corresponding value).

During discussion of this PEP, the operator became informally known as “the walrus operator”. The construct’s formal name is “Assignment Expressions” (as per the PEP title), but they may also be referred to as “Named Expressions” (e.g. the CPython reference implementation uses that name internally).

Naming the result of an expression is an important part of programming, allowing a descriptive name to be used in place of a longer expression, and permitting reuse. Currently, this feature is available only in statement form, making it unavailable in list comprehensions and other expression contexts.

Additionally, naming sub-parts of a large expression can assist an interactive debugger, providing useful display hooks and partial results. Without a way to capture sub-expressions inline, this would require refactoring of the original code; with assignment expressions, this merely requires the insertion of a few name := markers. Removing the need to refactor reduces the likelihood that the code be inadvertently changed as part of debugging (a common cause of Heisenbugs), and is easier to dictate to another programmer.

During the development of this PEP many people (supporters and critics both) have had a tendency to focus on toy examples on the one hand, and on overly complex examples on the other.

The danger of toy examples is twofold: they are often too abstract to make anyone go “ooh, that’s compelling”, and they are easily refuted with “I would never write it that way anyway”.

The danger of overly complex examples is that they provide a convenient strawman for critics of the proposal to shoot down (“that’s obfuscated”).

Yet there is some use for both extremely simple and extremely complex examples: they are helpful to clarify the intended semantics. Therefore, there will be some of each below.

However, in order to be compelling , examples should be rooted in real code, i.e. code that was written without any thought of this PEP, as part of a useful application, however large or small. Tim Peters has been extremely helpful by going over his own personal code repository and picking examples of code he had written that (in his view) would have been clearer if rewritten with (sparing) use of assignment expressions. His conclusion: the current proposal would have allowed a modest but clear improvement in quite a few bits of code.

Another use of real code is to observe indirectly how much value programmers place on compactness. Guido van Rossum searched through a Dropbox code base and discovered some evidence that programmers value writing fewer lines over shorter lines.

Case in point: Guido found several examples where a programmer repeated a subexpression, slowing down the program, in order to save one line of code, e.g. instead of writing:

they would write:

Another example illustrates that programmers sometimes do more work to save an extra level of indentation:

This code tries to match pattern2 even if pattern1 has a match (in which case the match on pattern2 is never used). The more efficient rewrite would have been:

Syntax and semantics

In most contexts where arbitrary Python expressions can be used, a named expression can appear. This is of the form NAME := expr where expr is any valid Python expression other than an unparenthesized tuple, and NAME is an identifier.

The value of such a named expression is the same as the incorporated expression, with the additional side-effect that the target is assigned that value:

There are a few places where assignment expressions are not allowed, in order to avoid ambiguities or user confusion:

This rule is included to simplify the choice for the user between an assignment statement and an assignment expression – there is no syntactic position where both are valid.

Again, this rule is included to avoid two visually similar ways of saying the same thing.

This rule is included to disallow excessively confusing code, and because parsing keyword arguments is complex enough already.

This rule is included to discourage side effects in a position whose exact semantics are already confusing to many users (cf. the common style recommendation against mutable default values), and also to echo the similar prohibition in calls (the previous bullet).

The reasoning here is similar to the two previous cases; this ungrouped assortment of symbols and operators composed of : and = is hard to read correctly.

This allows lambda to always bind less tightly than := ; having a name binding at the top level inside a lambda function is unlikely to be of value, as there is no way to make use of it. In cases where the name will be used more than once, the expression is likely to need parenthesizing anyway, so this prohibition will rarely affect code.

This shows that what looks like an assignment operator in an f-string is not always an assignment operator. The f-string parser uses : to indicate formatting options. To preserve backwards compatibility, assignment operator usage inside of f-strings must be parenthesized. As noted above, this usage of the assignment operator is not recommended.

An assignment expression does not introduce a new scope. In most cases the scope in which the target will be bound is self-explanatory: it is the current scope. If this scope contains a nonlocal or global declaration for the target, the assignment expression honors that. A lambda (being an explicit, if anonymous, function definition) counts as a scope for this purpose.

There is one special case: an assignment expression occurring in a list, set or dict comprehension or in a generator expression (below collectively referred to as “comprehensions”) binds the target in the containing scope, honoring a nonlocal or global declaration for the target in that scope, if one exists. For the purpose of this rule the containing scope of a nested comprehension is the scope that contains the outermost comprehension. A lambda counts as a containing scope.

The motivation for this special case is twofold. First, it allows us to conveniently capture a “witness” for an any() expression, or a counterexample for all() , for example:

Second, it allows a compact way of updating mutable state from a comprehension, for example:

However, an assignment expression target name cannot be the same as a for -target name appearing in any comprehension containing the assignment expression. The latter names are local to the comprehension in which they appear, so it would be contradictory for a contained use of the same name to refer to the scope containing the outermost comprehension instead.

For example, [i := i+1 for i in range(5)] is invalid: the for i part establishes that i is local to the comprehension, but the i := part insists that i is not local to the comprehension. The same reason makes these examples invalid too:

While it’s technically possible to assign consistent semantics to these cases, it’s difficult to determine whether those semantics actually make sense in the absence of real use cases. Accordingly, the reference implementation [1] will ensure that such cases raise SyntaxError , rather than executing with implementation defined behaviour.

This restriction applies even if the assignment expression is never executed:

For the comprehension body (the part before the first “for” keyword) and the filter expression (the part after “if” and before any nested “for”), this restriction applies solely to target names that are also used as iteration variables in the comprehension. Lambda expressions appearing in these positions introduce a new explicit function scope, and hence may use assignment expressions with no additional restrictions.

Due to design constraints in the reference implementation (the symbol table analyser cannot easily detect when names are re-used between the leftmost comprehension iterable expression and the rest of the comprehension), named expressions are disallowed entirely as part of comprehension iterable expressions (the part after each “in”, and before any subsequent “if” or “for” keyword):

A further exception applies when an assignment expression occurs in a comprehension whose containing scope is a class scope. If the rules above were to result in the target being assigned in that class’s scope, the assignment expression is expressly invalid. This case also raises SyntaxError :

(The reason for the latter exception is the implicit function scope created for comprehensions – there is currently no runtime mechanism for a function to refer to a variable in the containing class scope, and we do not want to add such a mechanism. If this issue ever gets resolved this special case may be removed from the specification of assignment expressions. Note that the problem already exists for using a variable defined in the class scope from a comprehension.)

See Appendix B for some examples of how the rules for targets in comprehensions translate to equivalent code.

The := operator groups more tightly than a comma in all syntactic positions where it is legal, but less tightly than all other operators, including or , and , not , and conditional expressions ( A if C else B ). As follows from section “Exceptional cases” above, it is never allowed at the same level as = . In case a different grouping is desired, parentheses should be used.

The := operator may be used directly in a positional function call argument; however it is invalid directly in a keyword argument.

Some examples to clarify what’s technically valid or invalid:

Most of the “valid” examples above are not recommended, since human readers of Python source code who are quickly glancing at some code may miss the distinction. But simple cases are not objectionable:

This PEP recommends always putting spaces around := , similar to PEP 8 ’s recommendation for = when used for assignment, whereas the latter disallows spaces around = used for keyword arguments.)

In order to have precisely defined semantics, the proposal requires evaluation order to be well-defined. This is technically not a new requirement, as function calls may already have side effects. Python already has a rule that subexpressions are generally evaluated from left to right. However, assignment expressions make these side effects more visible, and we propose a single change to the current evaluation order:

  • In a dict comprehension {X: Y for ...} , Y is currently evaluated before X . We propose to change this so that X is evaluated before Y . (In a dict display like {X: Y} this is already the case, and also in dict((X, Y) for ...) which should clearly be equivalent to the dict comprehension.)

Most importantly, since := is an expression, it can be used in contexts where statements are illegal, including lambda functions and comprehensions.

Conversely, assignment expressions don’t support the advanced features found in assignment statements:

  • Multiple targets are not directly supported: x = y = z = 0 # Equivalent: (z := (y := (x := 0)))
  • Single assignment targets other than a single NAME are not supported: # No equivalent a [ i ] = x self . rest = []
  • Priority around commas is different: x = 1 , 2 # Sets x to (1, 2) ( x := 1 , 2 ) # Sets x to 1
  • Iterable packing and unpacking (both regular or extended forms) are not supported: # Equivalent needs extra parentheses loc = x , y # Use (loc := (x, y)) info = name , phone , * rest # Use (info := (name, phone, *rest)) # No equivalent px , py , pz = position name , phone , email , * other_info = contact
  • Inline type annotations are not supported: # Closest equivalent is "p: Optional[int]" as a separate declaration p : Optional [ int ] = None
  • Augmented assignment is not supported: total += tax # Equivalent: (total := total + tax)

The following changes have been made based on implementation experience and additional review after the PEP was first accepted and before Python 3.8 was released:

  • for consistency with other similar exceptions, and to avoid locking in an exception name that is not necessarily going to improve clarity for end users, the originally proposed TargetScopeError subclass of SyntaxError was dropped in favour of just raising SyntaxError directly. [3]
  • due to a limitation in CPython’s symbol table analysis process, the reference implementation raises SyntaxError for all uses of named expressions inside comprehension iterable expressions, rather than only raising them when the named expression target conflicts with one of the iteration variables in the comprehension. This could be revisited given sufficiently compelling examples, but the extra complexity needed to implement the more selective restriction doesn’t seem worthwhile for purely hypothetical use cases.

Examples from the Python standard library

env_base is only used on these lines, putting its assignment on the if moves it as the “header” of the block.

  • Current: env_base = os . environ . get ( "PYTHONUSERBASE" , None ) if env_base : return env_base
  • Improved: if env_base := os . environ . get ( "PYTHONUSERBASE" , None ): return env_base

Avoid nested if and remove one indentation level.

  • Current: if self . _is_special : ans = self . _check_nans ( context = context ) if ans : return ans
  • Improved: if self . _is_special and ( ans := self . _check_nans ( context = context )): return ans

Code looks more regular and avoid multiple nested if. (See Appendix A for the origin of this example.)

  • Current: reductor = dispatch_table . get ( cls ) if reductor : rv = reductor ( x ) else : reductor = getattr ( x , "__reduce_ex__" , None ) if reductor : rv = reductor ( 4 ) else : reductor = getattr ( x , "__reduce__" , None ) if reductor : rv = reductor () else : raise Error ( "un(deep)copyable object of type %s " % cls )
  • Improved: if reductor := dispatch_table . get ( cls ): rv = reductor ( x ) elif reductor := getattr ( x , "__reduce_ex__" , None ): rv = reductor ( 4 ) elif reductor := getattr ( x , "__reduce__" , None ): rv = reductor () else : raise Error ( "un(deep)copyable object of type %s " % cls )

tz is only used for s += tz , moving its assignment inside the if helps to show its scope.

  • Current: s = _format_time ( self . _hour , self . _minute , self . _second , self . _microsecond , timespec ) tz = self . _tzstr () if tz : s += tz return s
  • Improved: s = _format_time ( self . _hour , self . _minute , self . _second , self . _microsecond , timespec ) if tz := self . _tzstr (): s += tz return s

Calling fp.readline() in the while condition and calling .match() on the if lines make the code more compact without making it harder to understand.

  • Current: while True : line = fp . readline () if not line : break m = define_rx . match ( line ) if m : n , v = m . group ( 1 , 2 ) try : v = int ( v ) except ValueError : pass vars [ n ] = v else : m = undef_rx . match ( line ) if m : vars [ m . group ( 1 )] = 0
  • Improved: while line := fp . readline (): if m := define_rx . match ( line ): n , v = m . group ( 1 , 2 ) try : v = int ( v ) except ValueError : pass vars [ n ] = v elif m := undef_rx . match ( line ): vars [ m . group ( 1 )] = 0

A list comprehension can map and filter efficiently by capturing the condition:

Similarly, a subexpression can be reused within the main expression, by giving it a name on first use:

Note that in both cases the variable y is bound in the containing scope (i.e. at the same level as results or stuff ).

Assignment expressions can be used to good effect in the header of an if or while statement:

Particularly with the while loop, this can remove the need to have an infinite loop, an assignment, and a condition. It also creates a smooth parallel between a loop which simply uses a function call as its condition, and one which uses that as its condition but also uses the actual value.

An example from the low-level UNIX world:

Rejected alternative proposals

Proposals broadly similar to this one have come up frequently on python-ideas. Below are a number of alternative syntaxes, some of them specific to comprehensions, which have been rejected in favour of the one given above.

A previous version of this PEP proposed subtle changes to the scope rules for comprehensions, to make them more usable in class scope and to unify the scope of the “outermost iterable” and the rest of the comprehension. However, this part of the proposal would have caused backwards incompatibilities, and has been withdrawn so the PEP can focus on assignment expressions.

Broadly the same semantics as the current proposal, but spelled differently.

Since EXPR as NAME already has meaning in import , except and with statements (with different semantics), this would create unnecessary confusion or require special-casing (e.g. to forbid assignment within the headers of these statements).

(Note that with EXPR as VAR does not simply assign the value of EXPR to VAR – it calls EXPR.__enter__() and assigns the result of that to VAR .)

Additional reasons to prefer := over this spelling include:

  • In if f(x) as y the assignment target doesn’t jump out at you – it just reads like if f x blah blah and it is too similar visually to if f(x) and y .
  • import foo as bar
  • except Exc as var
  • with ctxmgr() as var

To the contrary, the assignment expression does not belong to the if or while that starts the line, and we intentionally allow assignment expressions in other contexts as well.

  • NAME = EXPR
  • if NAME := EXPR

reinforces the visual recognition of assignment expressions.

This syntax is inspired by languages such as R and Haskell, and some programmable calculators. (Note that a left-facing arrow y <- f(x) is not possible in Python, as it would be interpreted as less-than and unary minus.) This syntax has a slight advantage over ‘as’ in that it does not conflict with with , except and import , but otherwise is equivalent. But it is entirely unrelated to Python’s other use of -> (function return type annotations), and compared to := (which dates back to Algol-58) it has a much weaker tradition.

This has the advantage that leaked usage can be readily detected, removing some forms of syntactic ambiguity. However, this would be the only place in Python where a variable’s scope is encoded into its name, making refactoring harder.

Execution order is inverted (the indented body is performed first, followed by the “header”). This requires a new keyword, unless an existing keyword is repurposed (most likely with: ). See PEP 3150 for prior discussion on this subject (with the proposed keyword being given: ).

This syntax has fewer conflicts than as does (conflicting only with the raise Exc from Exc notation), but is otherwise comparable to it. Instead of paralleling with expr as target: (which can be useful but can also be confusing), this has no parallels, but is evocative.

One of the most popular use-cases is if and while statements. Instead of a more general solution, this proposal enhances the syntax of these two statements to add a means of capturing the compared value:

This works beautifully if and ONLY if the desired condition is based on the truthiness of the captured value. It is thus effective for specific use-cases (regex matches, socket reads that return '' when done), and completely useless in more complicated cases (e.g. where the condition is f(x) < 0 and you want to capture the value of f(x) ). It also has no benefit to list comprehensions.

Advantages: No syntactic ambiguities. Disadvantages: Answers only a fraction of possible use-cases, even in if / while statements.

Another common use-case is comprehensions (list/set/dict, and genexps). As above, proposals have been made for comprehension-specific solutions.

This brings the subexpression to a location in between the ‘for’ loop and the expression. It introduces an additional language keyword, which creates conflicts. Of the three, where reads the most cleanly, but also has the greatest potential for conflict (e.g. SQLAlchemy and numpy have where methods, as does tkinter.dnd.Icon in the standard library).

As above, but reusing the with keyword. Doesn’t read too badly, and needs no additional language keyword. Is restricted to comprehensions, though, and cannot as easily be transformed into “longhand” for-loop syntax. Has the C problem that an equals sign in an expression can now create a name binding, rather than performing a comparison. Would raise the question of why “with NAME = EXPR:” cannot be used as a statement on its own.

As per option 2, but using as rather than an equals sign. Aligns syntactically with other uses of as for name binding, but a simple transformation to for-loop longhand would create drastically different semantics; the meaning of with inside a comprehension would be completely different from the meaning as a stand-alone statement, while retaining identical syntax.

Regardless of the spelling chosen, this introduces a stark difference between comprehensions and the equivalent unrolled long-hand form of the loop. It is no longer possible to unwrap the loop into statement form without reworking any name bindings. The only keyword that can be repurposed to this task is with , thus giving it sneakily different semantics in a comprehension than in a statement; alternatively, a new keyword is needed, with all the costs therein.

There are two logical precedences for the := operator. Either it should bind as loosely as possible, as does statement-assignment; or it should bind more tightly than comparison operators. Placing its precedence between the comparison and arithmetic operators (to be precise: just lower than bitwise OR) allows most uses inside while and if conditions to be spelled without parentheses, as it is most likely that you wish to capture the value of something, then perform a comparison on it:

Once find() returns -1, the loop terminates. If := binds as loosely as = does, this would capture the result of the comparison (generally either True or False ), which is less useful.

While this behaviour would be convenient in many situations, it is also harder to explain than “the := operator behaves just like the assignment statement”, and as such, the precedence for := has been made as close as possible to that of = (with the exception that it binds tighter than comma).

Some critics have claimed that the assignment expressions should allow unparenthesized tuples on the right, so that these two would be equivalent:

(With the current version of the proposal, the latter would be equivalent to ((point := x), y) .)

However, adopting this stance would logically lead to the conclusion that when used in a function call, assignment expressions also bind less tight than comma, so we’d have the following confusing equivalence:

The less confusing option is to make := bind more tightly than comma.

It’s been proposed to just always require parentheses around an assignment expression. This would resolve many ambiguities, and indeed parentheses will frequently be needed to extract the desired subexpression. But in the following cases the extra parentheses feel redundant:

Frequently Raised Objections

C and its derivatives define the = operator as an expression, rather than a statement as is Python’s way. This allows assignments in more contexts, including contexts where comparisons are more common. The syntactic similarity between if (x == y) and if (x = y) belies their drastically different semantics. Thus this proposal uses := to clarify the distinction.

The two forms have different flexibilities. The := operator can be used inside a larger expression; the = statement can be augmented to += and its friends, can be chained, and can assign to attributes and subscripts.

Previous revisions of this proposal involved sublocal scope (restricted to a single statement), preventing name leakage and namespace pollution. While a definite advantage in a number of situations, this increases complexity in many others, and the costs are not justified by the benefits. In the interests of language simplicity, the name bindings created here are exactly equivalent to any other name bindings, including that usage at class or module scope will create externally-visible names. This is no different from for loops or other constructs, and can be solved the same way: del the name once it is no longer needed, or prefix it with an underscore.

(The author wishes to thank Guido van Rossum and Christoph Groth for their suggestions to move the proposal in this direction. [2] )

As expression assignments can sometimes be used equivalently to statement assignments, the question of which should be preferred will arise. For the benefit of style guides such as PEP 8 , two recommendations are suggested.

  • If either assignment statements or assignment expressions can be used, prefer statements; they are a clear declaration of intent.
  • If using assignment expressions would lead to ambiguity about execution order, restructure it to use statements instead.

The authors wish to thank Alyssa Coghlan and Steven D’Aprano for their considerable contributions to this proposal, and members of the core-mentorship mailing list for assistance with implementation.

Appendix A: Tim Peters’s findings

Here’s a brief essay Tim Peters wrote on the topic.

I dislike “busy” lines of code, and also dislike putting conceptually unrelated logic on a single line. So, for example, instead of:

instead. So I suspected I’d find few places I’d want to use assignment expressions. I didn’t even consider them for lines already stretching halfway across the screen. In other cases, “unrelated” ruled:

is a vast improvement over the briefer:

The original two statements are doing entirely different conceptual things, and slamming them together is conceptually insane.

In other cases, combining related logic made it harder to understand, such as rewriting:

as the briefer:

The while test there is too subtle, crucially relying on strict left-to-right evaluation in a non-short-circuiting or method-chaining context. My brain isn’t wired that way.

But cases like that were rare. Name binding is very frequent, and “sparse is better than dense” does not mean “almost empty is better than sparse”. For example, I have many functions that return None or 0 to communicate “I have nothing useful to return in this case, but since that’s expected often I’m not going to annoy you with an exception”. This is essentially the same as regular expression search functions returning None when there is no match. So there was lots of code of the form:

I find that clearer, and certainly a bit less typing and pattern-matching reading, as:

It’s also nice to trade away a small amount of horizontal whitespace to get another _line_ of surrounding code on screen. I didn’t give much weight to this at first, but it was so very frequent it added up, and I soon enough became annoyed that I couldn’t actually run the briefer code. That surprised me!

There are other cases where assignment expressions really shine. Rather than pick another from my code, Kirill Balunov gave a lovely example from the standard library’s copy() function in copy.py :

The ever-increasing indentation is semantically misleading: the logic is conceptually flat, “the first test that succeeds wins”:

Using easy assignment expressions allows the visual structure of the code to emphasize the conceptual flatness of the logic; ever-increasing indentation obscured it.

A smaller example from my code delighted me, both allowing to put inherently related logic in a single line, and allowing to remove an annoying “artificial” indentation level:

That if is about as long as I want my lines to get, but remains easy to follow.

So, in all, in most lines binding a name, I wouldn’t use assignment expressions, but because that construct is so very frequent, that leaves many places I would. In most of the latter, I found a small win that adds up due to how often it occurs, and in the rest I found a moderate to major win. I’d certainly use it more often than ternary if , but significantly less often than augmented assignment.

I have another example that quite impressed me at the time.

Where all variables are positive integers, and a is at least as large as the n’th root of x, this algorithm returns the floor of the n’th root of x (and roughly doubling the number of accurate bits per iteration):

It’s not obvious why that works, but is no more obvious in the “loop and a half” form. It’s hard to prove correctness without building on the right insight (the “arithmetic mean - geometric mean inequality”), and knowing some non-trivial things about how nested floor functions behave. That is, the challenges are in the math, not really in the coding.

If you do know all that, then the assignment-expression form is easily read as “while the current guess is too large, get a smaller guess”, where the “too large?” test and the new guess share an expensive sub-expression.

To my eyes, the original form is harder to understand:

This appendix attempts to clarify (though not specify) the rules when a target occurs in a comprehension or in a generator expression. For a number of illustrative examples we show the original code, containing a comprehension, and the translation, where the comprehension has been replaced by an equivalent generator function plus some scaffolding.

Since [x for ...] is equivalent to list(x for ...) these examples all use list comprehensions without loss of generality. And since these examples are meant to clarify edge cases of the rules, they aren’t trying to look like real code.

Note: comprehensions are already implemented via synthesizing nested generator functions like those in this appendix. The new part is adding appropriate declarations to establish the intended scope of assignment expression targets (the same scope they resolve to as if the assignment were performed in the block containing the outermost comprehension). For type inference purposes, these illustrative expansions do not imply that assignment expression targets are always Optional (but they do indicate the target binding scope).

Let’s start with a reminder of what code is generated for a generator expression without assignment expression.

  • Original code (EXPR usually references VAR): def f (): a = [ EXPR for VAR in ITERABLE ]
  • Translation (let’s not worry about name conflicts): def f (): def genexpr ( iterator ): for VAR in iterator : yield EXPR a = list ( genexpr ( iter ( ITERABLE )))

Let’s add a simple assignment expression.

  • Original code: def f (): a = [ TARGET := EXPR for VAR in ITERABLE ]
  • Translation: def f (): if False : TARGET = None # Dead code to ensure TARGET is a local variable def genexpr ( iterator ): nonlocal TARGET for VAR in iterator : TARGET = EXPR yield TARGET a = list ( genexpr ( iter ( ITERABLE )))

Let’s add a global TARGET declaration in f() .

  • Original code: def f (): global TARGET a = [ TARGET := EXPR for VAR in ITERABLE ]
  • Translation: def f (): global TARGET def genexpr ( iterator ): global TARGET for VAR in iterator : TARGET = EXPR yield TARGET a = list ( genexpr ( iter ( ITERABLE )))

Or instead let’s add a nonlocal TARGET declaration in f() .

  • Original code: def g (): TARGET = ... def f (): nonlocal TARGET a = [ TARGET := EXPR for VAR in ITERABLE ]
  • Translation: def g (): TARGET = ... def f (): nonlocal TARGET def genexpr ( iterator ): nonlocal TARGET for VAR in iterator : TARGET = EXPR yield TARGET a = list ( genexpr ( iter ( ITERABLE )))

Finally, let’s nest two comprehensions.

  • Original code: def f (): a = [[ TARGET := i for i in range ( 3 )] for j in range ( 2 )] # I.e., a = [[0, 1, 2], [0, 1, 2]] print ( TARGET ) # prints 2
  • Translation: def f (): if False : TARGET = None def outer_genexpr ( outer_iterator ): nonlocal TARGET def inner_generator ( inner_iterator ): nonlocal TARGET for i in inner_iterator : TARGET = i yield i for j in outer_iterator : yield list ( inner_generator ( range ( 3 ))) a = list ( outer_genexpr ( range ( 2 ))) print ( TARGET )

Because it has been a point of confusion, note that nothing about Python’s scoping semantics is changed. Function-local scopes continue to be resolved at compile time, and to have indefinite temporal extent at run time (“full closures”). Example:

This document has been placed in the public domain.

Source: https://github.com/python/peps/blob/main/peps/pep-0572.rst

Last modified: 2023-10-11 12:05:51 GMT

  • Python »
  • 3.14.0a0 Documentation »
  • The Python Tutorial »
  • Theme Auto Light Dark |

9. Classes ¶

Classes provide a means of bundling data and functionality together. Creating a new class creates a new type of object, allowing new instances of that type to be made. Each class instance can have attributes attached to it for maintaining its state. Class instances can also have methods (defined by its class) for modifying its state.

Compared with other programming languages, Python’s class mechanism adds classes with a minimum of new syntax and semantics. It is a mixture of the class mechanisms found in C++ and Modula-3. Python classes provide all the standard features of Object Oriented Programming: the class inheritance mechanism allows multiple base classes, a derived class can override any methods of its base class or classes, and a method can call the method of a base class with the same name. Objects can contain arbitrary amounts and kinds of data. As is true for modules, classes partake of the dynamic nature of Python: they are created at runtime, and can be modified further after creation.

In C++ terminology, normally class members (including the data members) are public (except see below Private Variables ), and all member functions are virtual . As in Modula-3, there are no shorthands for referencing the object’s members from its methods: the method function is declared with an explicit first argument representing the object, which is provided implicitly by the call. As in Smalltalk, classes themselves are objects. This provides semantics for importing and renaming. Unlike C++ and Modula-3, built-in types can be used as base classes for extension by the user. Also, like in C++, most built-in operators with special syntax (arithmetic operators, subscripting etc.) can be redefined for class instances.

(Lacking universally accepted terminology to talk about classes, I will make occasional use of Smalltalk and C++ terms. I would use Modula-3 terms, since its object-oriented semantics are closer to those of Python than C++, but I expect that few readers have heard of it.)

9.1. A Word About Names and Objects ¶

Objects have individuality, and multiple names (in multiple scopes) can be bound to the same object. This is known as aliasing in other languages. This is usually not appreciated on a first glance at Python, and can be safely ignored when dealing with immutable basic types (numbers, strings, tuples). However, aliasing has a possibly surprising effect on the semantics of Python code involving mutable objects such as lists, dictionaries, and most other types. This is usually used to the benefit of the program, since aliases behave like pointers in some respects. For example, passing an object is cheap since only a pointer is passed by the implementation; and if a function modifies an object passed as an argument, the caller will see the change — this eliminates the need for two different argument passing mechanisms as in Pascal.

9.2. Python Scopes and Namespaces ¶

Before introducing classes, I first have to tell you something about Python’s scope rules. Class definitions play some neat tricks with namespaces, and you need to know how scopes and namespaces work to fully understand what’s going on. Incidentally, knowledge about this subject is useful for any advanced Python programmer.

Let’s begin with some definitions.

A namespace is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries, but that’s normally not noticeable in any way (except for performance), and it may change in the future. Examples of namespaces are: the set of built-in names (containing functions such as abs() , and built-in exception names); the global names in a module; and the local names in a function invocation. In a sense the set of attributes of an object also form a namespace. The important thing to know about namespaces is that there is absolutely no relation between names in different namespaces; for instance, two different modules may both define a function maximize without confusion — users of the modules must prefix it with the module name.

By the way, I use the word attribute for any name following a dot — for example, in the expression z.real , real is an attribute of the object z . Strictly speaking, references to names in modules are attribute references: in the expression modname.funcname , modname is a module object and funcname is an attribute of it. In this case there happens to be a straightforward mapping between the module’s attributes and the global names defined in the module: they share the same namespace! [ 1 ]

Attributes may be read-only or writable. In the latter case, assignment to attributes is possible. Module attributes are writable: you can write modname.the_answer = 42 . Writable attributes may also be deleted with the del statement. For example, del modname.the_answer will remove the attribute the_answer from the object named by modname .

Namespaces are created at different moments and have different lifetimes. The namespace containing the built-in names is created when the Python interpreter starts up, and is never deleted. The global namespace for a module is created when the module definition is read in; normally, module namespaces also last until the interpreter quits. The statements executed by the top-level invocation of the interpreter, either read from a script file or interactively, are considered part of a module called __main__ , so they have their own global namespace. (The built-in names actually also live in a module; this is called builtins .)

The local namespace for a function is created when the function is called, and deleted when the function returns or raises an exception that is not handled within the function. (Actually, forgetting would be a better way to describe what actually happens.) Of course, recursive invocations each have their own local namespace.

A scope is a textual region of a Python program where a namespace is directly accessible. “Directly accessible” here means that an unqualified reference to a name attempts to find the name in the namespace.

Although scopes are determined statically, they are used dynamically. At any time during execution, there are 3 or 4 nested scopes whose namespaces are directly accessible:

the innermost scope, which is searched first, contains the local names

the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contain non-local, but also non-global names

the next-to-last scope contains the current module’s global names

the outermost scope (searched last) is the namespace containing built-in names

If a name is declared global, then all references and assignments go directly to the next-to-last scope containing the module’s global names. To rebind variables found outside of the innermost scope, the nonlocal statement can be used; if not declared nonlocal, those variables are read-only (an attempt to write to such a variable will simply create a new local variable in the innermost scope, leaving the identically named outer variable unchanged).

Usually, the local scope references the local names of the (textually) current function. Outside functions, the local scope references the same namespace as the global scope: the module’s namespace. Class definitions place yet another namespace in the local scope.

It is important to realize that scopes are determined textually: the global scope of a function defined in a module is that module’s namespace, no matter from where or by what alias the function is called. On the other hand, the actual search for names is done dynamically, at run time — however, the language definition is evolving towards static name resolution, at “compile” time, so don’t rely on dynamic name resolution! (In fact, local variables are already determined statically.)

A special quirk of Python is that – if no global or nonlocal statement is in effect – assignments to names always go into the innermost scope. Assignments do not copy data — they just bind names to objects. The same is true for deletions: the statement del x removes the binding of x from the namespace referenced by the local scope. In fact, all operations that introduce new names use the local scope: in particular, import statements and function definitions bind the module or function name in the local scope.

The global statement can be used to indicate that particular variables live in the global scope and should be rebound there; the nonlocal statement indicates that particular variables live in an enclosing scope and should be rebound there.

9.2.1. Scopes and Namespaces Example ¶

This is an example demonstrating how to reference the different scopes and namespaces, and how global and nonlocal affect variable binding:

The output of the example code is:

Note how the local assignment (which is default) didn’t change scope_test 's binding of spam . The nonlocal assignment changed scope_test 's binding of spam , and the global assignment changed the module-level binding.

You can also see that there was no previous binding for spam before the global assignment.

9.3. A First Look at Classes ¶

Classes introduce a little bit of new syntax, three new object types, and some new semantics.

9.3.1. Class Definition Syntax ¶

The simplest form of class definition looks like this:

Class definitions, like function definitions ( def statements) must be executed before they have any effect. (You could conceivably place a class definition in a branch of an if statement, or inside a function.)

In practice, the statements inside a class definition will usually be function definitions, but other statements are allowed, and sometimes useful — we’ll come back to this later. The function definitions inside a class normally have a peculiar form of argument list, dictated by the calling conventions for methods — again, this is explained later.

When a class definition is entered, a new namespace is created, and used as the local scope — thus, all assignments to local variables go into this new namespace. In particular, function definitions bind the name of the new function here.

When a class definition is left normally (via the end), a class object is created. This is basically a wrapper around the contents of the namespace created by the class definition; we’ll learn more about class objects in the next section. The original local scope (the one in effect just before the class definition was entered) is reinstated, and the class object is bound here to the class name given in the class definition header ( ClassName in the example).

9.3.2. Class Objects ¶

Class objects support two kinds of operations: attribute references and instantiation.

Attribute references use the standard syntax used for all attribute references in Python: obj.name . Valid attribute names are all the names that were in the class’s namespace when the class object was created. So, if the class definition looked like this:

then MyClass.i and MyClass.f are valid attribute references, returning an integer and a function object, respectively. Class attributes can also be assigned to, so you can change the value of MyClass.i by assignment. __doc__ is also a valid attribute, returning the docstring belonging to the class: "A simple example class" .

Class instantiation uses function notation. Just pretend that the class object is a parameterless function that returns a new instance of the class. For example (assuming the above class):

creates a new instance of the class and assigns this object to the local variable x .

The instantiation operation (“calling” a class object) creates an empty object. Many classes like to create objects with instances customized to a specific initial state. Therefore a class may define a special method named __init__() , like this:

When a class defines an __init__() method, class instantiation automatically invokes __init__() for the newly created class instance. So in this example, a new, initialized instance can be obtained by:

Of course, the __init__() method may have arguments for greater flexibility. In that case, arguments given to the class instantiation operator are passed on to __init__() . For example,

9.3.3. Instance Objects ¶

Now what can we do with instance objects? The only operations understood by instance objects are attribute references. There are two kinds of valid attribute names: data attributes and methods.

data attributes correspond to “instance variables” in Smalltalk, and to “data members” in C++. Data attributes need not be declared; like local variables, they spring into existence when they are first assigned to. For example, if x is the instance of MyClass created above, the following piece of code will print the value 16 , without leaving a trace:

The other kind of instance attribute reference is a method . A method is a function that “belongs to” an object. (In Python, the term method is not unique to class instances: other object types can have methods as well. For example, list objects have methods called append, insert, remove, sort, and so on. However, in the following discussion, we’ll use the term method exclusively to mean methods of class instance objects, unless explicitly stated otherwise.)

Valid method names of an instance object depend on its class. By definition, all attributes of a class that are function objects define corresponding methods of its instances. So in our example, x.f is a valid method reference, since MyClass.f is a function, but x.i is not, since MyClass.i is not. But x.f is not the same thing as MyClass.f — it is a method object , not a function object.

9.3.4. Method Objects ¶

Usually, a method is called right after it is bound:

In the MyClass example, this will return the string 'hello world' . However, it is not necessary to call a method right away: x.f is a method object, and can be stored away and called at a later time. For example:

will continue to print hello world until the end of time.

What exactly happens when a method is called? You may have noticed that x.f() was called without an argument above, even though the function definition for f() specified an argument. What happened to the argument? Surely Python raises an exception when a function that requires an argument is called without any — even if the argument isn’t actually used…

Actually, you may have guessed the answer: the special thing about methods is that the instance object is passed as the first argument of the function. In our example, the call x.f() is exactly equivalent to MyClass.f(x) . In general, calling a method with a list of n arguments is equivalent to calling the corresponding function with an argument list that is created by inserting the method’s instance object before the first argument.

In general, methods work as follows. When a non-data attribute of an instance is referenced, the instance’s class is searched. If the name denotes a valid class attribute that is a function object, references to both the instance object and the function object are packed into a method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.

9.3.5. Class and Instance Variables ¶

Generally speaking, instance variables are for data unique to each instance and class variables are for attributes and methods shared by all instances of the class:

As discussed in A Word About Names and Objects , shared data can have possibly surprising effects with involving mutable objects such as lists and dictionaries. For example, the tricks list in the following code should not be used as a class variable because just a single list would be shared by all Dog instances:

Correct design of the class should use an instance variable instead:

9.4. Random Remarks ¶

If the same attribute name occurs in both an instance and in a class, then attribute lookup prioritizes the instance:

Data attributes may be referenced by methods as well as by ordinary users (“clients”) of an object. In other words, classes are not usable to implement pure abstract data types. In fact, nothing in Python makes it possible to enforce data hiding — it is all based upon convention. (On the other hand, the Python implementation, written in C, can completely hide implementation details and control access to an object if necessary; this can be used by extensions to Python written in C.)

Clients should use data attributes with care — clients may mess up invariants maintained by the methods by stamping on their data attributes. Note that clients may add data attributes of their own to an instance object without affecting the validity of the methods, as long as name conflicts are avoided — again, a naming convention can save a lot of headaches here.

There is no shorthand for referencing data attributes (or other methods!) from within methods. I find that this actually increases the readability of methods: there is no chance of confusing local variables and instance variables when glancing through a method.

Often, the first argument of a method is called self . This is nothing more than a convention: the name self has absolutely no special meaning to Python. Note, however, that by not following the convention your code may be less readable to other Python programmers, and it is also conceivable that a class browser program might be written that relies upon such a convention.

Any function object that is a class attribute defines a method for instances of that class. It is not necessary that the function definition is textually enclosed in the class definition: assigning a function object to a local variable in the class is also ok. For example:

Now f , g and h are all attributes of class C that refer to function objects, and consequently they are all methods of instances of C — h being exactly equivalent to g . Note that this practice usually only serves to confuse the reader of a program.

Methods may call other methods by using method attributes of the self argument:

Methods may reference global names in the same way as ordinary functions. The global scope associated with a method is the module containing its definition. (A class is never used as a global scope.) While one rarely encounters a good reason for using global data in a method, there are many legitimate uses of the global scope: for one thing, functions and modules imported into the global scope can be used by methods, as well as functions and classes defined in it. Usually, the class containing the method is itself defined in this global scope, and in the next section we’ll find some good reasons why a method would want to reference its own class.

Each value is an object, and therefore has a class (also called its type ). It is stored as object.__class__ .

9.5. Inheritance ¶

Of course, a language feature would not be worthy of the name “class” without supporting inheritance. The syntax for a derived class definition looks like this:

The name BaseClassName must be defined in a namespace accessible from the scope containing the derived class definition. In place of a base class name, other arbitrary expressions are also allowed. This can be useful, for example, when the base class is defined in another module:

Execution of a derived class definition proceeds the same as for a base class. When the class object is constructed, the base class is remembered. This is used for resolving attribute references: if a requested attribute is not found in the class, the search proceeds to look in the base class. This rule is applied recursively if the base class itself is derived from some other class.

There’s nothing special about instantiation of derived classes: DerivedClassName() creates a new instance of the class. Method references are resolved as follows: the corresponding class attribute is searched, descending down the chain of base classes if necessary, and the method reference is valid if this yields a function object.

Derived classes may override methods of their base classes. Because methods have no special privileges when calling other methods of the same object, a method of a base class that calls another method defined in the same base class may end up calling a method of a derived class that overrides it. (For C++ programmers: all methods in Python are effectively virtual .)

An overriding method in a derived class may in fact want to extend rather than simply replace the base class method of the same name. There is a simple way to call the base class method directly: just call BaseClassName.methodname(self, arguments) . This is occasionally useful to clients as well. (Note that this only works if the base class is accessible as BaseClassName in the global scope.)

Python has two built-in functions that work with inheritance:

Use isinstance() to check an instance’s type: isinstance(obj, int) will be True only if obj.__class__ is int or some class derived from int .

Use issubclass() to check class inheritance: issubclass(bool, int) is True since bool is a subclass of int . However, issubclass(float, int) is False since float is not a subclass of int .

9.5.1. Multiple Inheritance ¶

Python supports a form of multiple inheritance as well. A class definition with multiple base classes looks like this:

For most purposes, in the simplest cases, you can think of the search for attributes inherited from a parent class as depth-first, left-to-right, not searching twice in the same class where there is an overlap in the hierarchy. Thus, if an attribute is not found in DerivedClassName , it is searched for in Base1 , then (recursively) in the base classes of Base1 , and if it was not found there, it was searched for in Base2 , and so on.

In fact, it is slightly more complex than that; the method resolution order changes dynamically to support cooperative calls to super() . This approach is known in some other multiple-inheritance languages as call-next-method and is more powerful than the super call found in single-inheritance languages.

Dynamic ordering is necessary because all cases of multiple inheritance exhibit one or more diamond relationships (where at least one of the parent classes can be accessed through multiple paths from the bottommost class). For example, all classes inherit from object , so any case of multiple inheritance provides more than one path to reach object . To keep the base classes from being accessed more than once, the dynamic algorithm linearizes the search order in a way that preserves the left-to-right ordering specified in each class, that calls each parent only once, and that is monotonic (meaning that a class can be subclassed without affecting the precedence order of its parents). Taken together, these properties make it possible to design reliable and extensible classes with multiple inheritance. For more detail, see The Python 2.3 Method Resolution Order .

9.6. Private Variables ¶

“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam ) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.

Since there is a valid use-case for class-private members (namely to avoid name clashes of names with names defined by subclasses), there is limited support for such a mechanism, called name mangling . Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam , where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class.

Name mangling is helpful for letting subclasses override methods without breaking intraclass method calls. For example:

The above example would work even if MappingSubclass were to introduce a __update identifier since it is replaced with _Mapping__update in the Mapping class and _MappingSubclass__update in the MappingSubclass class respectively.

Note that the mangling rules are designed mostly to avoid accidents; it still is possible to access or modify a variable that is considered private. This can even be useful in special circumstances, such as in the debugger.

Notice that code passed to exec() or eval() does not consider the classname of the invoking class to be the current class; this is similar to the effect of the global statement, the effect of which is likewise restricted to code that is byte-compiled together. The same restriction applies to getattr() , setattr() and delattr() , as well as when referencing __dict__ directly.

9.7. Odds and Ends ¶

Sometimes it is useful to have a data type similar to the Pascal “record” or C “struct”, bundling together a few named data items. The idiomatic approach is to use dataclasses for this purpose:

A piece of Python code that expects a particular abstract data type can often be passed a class that emulates the methods of that data type instead. For instance, if you have a function that formats some data from a file object, you can define a class with methods read() and readline() that get the data from a string buffer instead, and pass it as an argument.

Instance method objects have attributes, too: m.__self__ is the instance object with the method m() , and m.__func__ is the function object corresponding to the method.

9.8. Iterators ¶

By now you have probably noticed that most container objects can be looped over using a for statement:

This style of access is clear, concise, and convenient. The use of iterators pervades and unifies Python. Behind the scenes, the for statement calls iter() on the container object. The function returns an iterator object that defines the method __next__() which accesses elements in the container one at a time. When there are no more elements, __next__() raises a StopIteration exception which tells the for loop to terminate. You can call the __next__() method using the next() built-in function; this example shows how it all works:

Having seen the mechanics behind the iterator protocol, it is easy to add iterator behavior to your classes. Define an __iter__() method which returns an object with a __next__() method. If the class defines __next__() , then __iter__() can just return self :

9.9. Generators ¶

Generators are a simple and powerful tool for creating iterators. They are written like regular functions but use the yield statement whenever they want to return data. Each time next() is called on it, the generator resumes where it left off (it remembers all the data values and which statement was last executed). An example shows that generators can be trivially easy to create:

Anything that can be done with generators can also be done with class-based iterators as described in the previous section. What makes generators so compact is that the __iter__() and __next__() methods are created automatically.

Another key feature is that the local variables and execution state are automatically saved between calls. This made the function easier to write and much more clear than an approach using instance variables like self.index and self.data .

In addition to automatic method creation and saving program state, when generators terminate, they automatically raise StopIteration . In combination, these features make it easy to create iterators with no more effort than writing a regular function.

9.10. Generator Expressions ¶

Some simple generators can be coded succinctly as expressions using a syntax similar to list comprehensions but with parentheses instead of square brackets. These expressions are designed for situations where the generator is used right away by an enclosing function. Generator expressions are more compact but less versatile than full generator definitions and tend to be more memory friendly than equivalent list comprehensions.

Table of Contents

  • 9.1. A Word About Names and Objects
  • 9.2.1. Scopes and Namespaces Example
  • 9.3.1. Class Definition Syntax
  • 9.3.2. Class Objects
  • 9.3.3. Instance Objects
  • 9.3.4. Method Objects
  • 9.3.5. Class and Instance Variables
  • 9.4. Random Remarks
  • 9.5.1. Multiple Inheritance
  • 9.6. Private Variables
  • 9.7. Odds and Ends
  • 9.8. Iterators
  • 9.9. Generators
  • 9.10. Generator Expressions

Previous topic

8. Errors and Exceptions

10. Brief Tour of the Standard Library

  • Report a Bug
  • Show Source

IMAGES

  1. Python List Comprehension

    python list comprehension referenced before assignment

  2. List Comprehension in python with example

    python list comprehension referenced before assignment

  3. List Comprehension in Python (with 10 Examples)

    python list comprehension referenced before assignment

  4. Python Tutorial On List Comprehension With Examples

    python list comprehension referenced before assignment

  5. List Comprehension in python with example

    python list comprehension referenced before assignment

  6. List Comprehension in Python with Example

    python list comprehension referenced before assignment

VIDEO

  1. List Comprehension In Python In Under A Minute! ✅⚡⏲️ #coding #programming #python #shorts

  2. List Comprehension #python #list

  3. PYTHON LIST COMPREHENSION||CODEJAL||

  4. Learn Python List Comprehension

  5. List Comprehensions in Python

  6. List Comprehension in Python

COMMENTS

  1. python

    1. I am trying to generate a list based on this JSON: city_list = [. city. for city in metroextractor_cities['regions'][region]['cities'] for region in metroextractor_cities['regions']] Python says the variable region is referenced before assignment: UnboundLocalError: local variable 'region' referenced before assignment. I don't really see that.

  2. When to Use a List Comprehension in Python

    Here, you instantiate an empty list, squares.Then, you use a for loop to iterate over range(10).Finally, you multiply each number by itself and append the result to the end of the list.. Work With map Objects. For an alternative approach that's based in functional programming, you can use map().You pass in a function and an iterable, and map() will create an object.

  3. Python List Comprehension: Tutorial With Examples

    A Python list comprehension is a language construct. It's used to create a Python list based on an existing list. Sounds a little vague, but after a few examples, that 'ah-ha!' moment will follow, trust me. The basic syntax of a list comprehension is: [ <expression> for item in list if <conditional> ]

  4. Python List Comprehension (With Examples)

    # create a new list using list comprehension square_numbers = [num ** 2 for num in numbers] If we compare the two codes, list comprehension is straightforward and simpler to read and understand. So unless we need to perform complex operations, we can stick to list comprehension.

  5. List Comprehensions in Python (With Examples and Video)

    Python list comprehensions are a more Pythonic way to create, modify, and filter lists. This is incredibly helpful because it represents a single tool to complete a number of tasks, rather than relying on for loops, map() functions, or filter() functions. This is great as you can apply one method, rather trying to fit a use case to a scenario.

  6. Comprehensive Guide to Python List Comprehensions

    The general syntax of list comprehension looks as below. It consists of three parts: iteration, transformation, and filtering. squares = [num**2 for num in range(1,11) if num%2==0] Iteration: This is the first part that gets executed in a list comprehension that iterates through all the elements in the iterable.

  7. Fix "local variable referenced before assignment" in Python

    Building Your First Convolutional Neural Network With Keras # python # artificial intelligence # machine learning # tensorflow Most resources start with pristine datasets, start at importing and finish at validation.

  8. Python local variable referenced before assignment Solution

    Trying to assign a value to a variable that does not have local scope can result in this error: UnboundLocalError: local variable referenced before assignment. Python has a simple rule to determine the scope of a variable. If a variable is assigned in a function, that variable is local. This is because it is assumed that when you define a ...

  9. Local variable referenced before assignment in Python

    If a variable is assigned a value in a function's body, it is a local variable unless explicitly declared as global. # Local variables shadow global ones with the same name You could reference the global name variable from inside the function but if you assign a value to the variable in the function's body, the local variable shadows the global one.

  10. The Do's and Don'ts of Python List Comprehension

    1. Don't forget about the list () constructor. Some people may think that list comprehension is a cool and Pythonic feature to show off their Python skills, and they tend to use it even when there are better alternatives. One such case is the use of the list() constructor. Consider some trivial examples below.

  11. Understanding List Comprehensions in Python 3

    Introduction. List comprehensions offer a succinct way to create lists based on existing lists. When using list comprehensions, lists can be built by leveraging any iterable, including strings and tuples. Syntactically, list comprehensions consist of an iterable containing an expression followed by a for clause.

  12. How to Fix Local Variable Referenced Before Assignment Error in Python

    value = value + 1 print (value) increment() If you run this code, you'll get. BASH. UnboundLocalError: local variable 'value' referenced before assignment. The issue is that in this line: PYTHON. value = value + 1. We are defining a local variable called value and then trying to use it before it has been assigned a value, instead of using the ...

  13. Python List Comprehension: single, multiple, nested, & more

    We've got a list of numbers called num_list, as follows: num_list = [4, 11, 2, 19, 7, 6, 25, 12] Learn Data Science with. Using list comprehension, we'd like to append any values greater than ten to a new list. We can do this as follows: new_list = [num for num in num_list if num > 10] new_list. Learn Data Science with.

  14. 9 Things to Know to Master List Comprehensions in Python

    expression. 2. Create a List. It's not surprising at all that the most popular usage is to create a list concisely. Suppose that we don't know list comprehensions, we'll probably do something like the below when it comes to the creation of a list. To do that, first, we'll declare an empty list.

  15. Python

    List comprehension offers a shorter syntax when you want to create a new list based on the values of an existing list. Example: Based on a list of fruits, you want a new list, containing only the fruits with the letter "a" in the name. Without list comprehension you will have to write a for statement with a conditional test inside:

  16. 5. Data Structures

    You might have noticed that methods like insert, remove or sort that only modify the list have no return value printed - they return the default None. 1 This is a design principle for all mutable data structures in Python.. Another thing you might notice is that not all data can be sorted or compared. For instance, [None, 'hello', 10] doesn't sort because integers can't be compared to ...

  17. PEP 572

    Unparenthesized assignment expressions are prohibited for the value of a keyword argument in a call. Example: foo(x = y := f(x)) # INVALID foo(x=(y := f(x))) # Valid, though probably confusing. This rule is included to disallow excessively confusing code, and because parsing keyword arguments is complex enough already.

  18. python

    I think you are using 'global' incorrectly. See Python reference.You should declare variable without global and then inside the function when you want to access global variable you declare it global yourvar. #!/usr/bin/python total def checkTotal(): global total total = 0

  19. 9. Classes

    Note how the local assignment (which is default) didn't change scope_test's binding of spam.The nonlocal assignment changed scope_test's binding of spam, and the global assignment changed the module-level binding.. You can also see that there was no previous binding for spam before the global assignment.. 9.3. A First Look at Classes¶. Classes introduce a little bit of new syntax, three new ...

  20. Referenced before assignment Python

    That just defines a function that will, when run, call whatever bar means. As long as we've defined bar before we call it—and we have—everything is fine. However, you have a number of similar problems in your code. For example, let's look at this part: elif aon == 2: def first_number(): print "First number: ".

  21. python

    In Python (and almost all programming languages I'm aware of), return statement affects only the current method. So putting return in a separate method doesn't stop the execution of __init__ method, and the value that you returned is completely lost, because you aren't assigning it to any variable.