Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List comprehension throws exception when list is empty

I have a function that filters list items if their date is in the past (smaller than current date).

meetings = []
def clean_old():
    meetings = [meeting for meeting in meetings if time.mktime(meeting) >= time.localtime()]

When the list is empty, this code crashes.

Why does it crash? It says for meeting in meetings, and if meetings is empty then everything should be alright.

How do I fix it and what is the explanation for that occurrence?

like image 282
Lior Avatar asked Nov 29 '25 22:11

Lior


1 Answers

I assume that you are seeing this exception:

UnboundLocalError: local variable 'meetings' referenced before assignment

What you're experiencing here doesn't actually have anything to do with list comprehensions. This error occurs because you initially defined meetings outside of your function, but you are trying to assign it a new value inside the function.

When Python sees that a variable is assigned a value inside a function, it treats that as a new variable, specific to that function. This prevents the function from accessing any variables from outside of the function that have the same name.

Internally, Python is doing something sort-of like this:

meetings_outside = []

def clean_old():
    meetings_inside = [meeting for meeting in meetings_inside if time.mktime(meeting) >= time.localtime()]

You can understand why this would fail: meetings_inside is being defined in terms of itself. When Python tries to look up the value of meetings_inside to begin iterating over its content, it fails because it hasn't been assigned a value yet.

How to deal with this depends on which version of Python you are using, and there the initial value of meetings is defined.

In Python 3, you can simply add nonlocal meetings to the top of your function. This will tell it that you are referring to an existing variable named meetings, not creating another one.

However you are probably using Python 2, which does not have the nonlocal keyword. It does have the global keyword, which does the same thing but only if meetings is defined at the top level of a module: outside of any other functions or classes.

For example, this would work in Python 2 if you had nothing else in your file:

import time

meetings = []

def clean_old():
    global meetings
    meetings = [meeting for meeting in meetings if time.mktime(meeting) >= time.localtime()]

clean_old()
print meetings
[]

However this would not, because meetings is defined inside of a function:

import time

def main():
    meetings = []

    def clean_old():
        global meetings
        meetings = [meeting for meeting in meetings if time.mktime(meeting) >= time.localtime()]

    clean_old()
    print meetings

main()
NameError: global name 'meetings' is not defined

You would need to work around the issue. The simplest way would be to modify the assignment to be:

 meetings[:] = [meeting for meeting in meetings if time.mktime(meeting) >= time.localtime()]

This tells Python that you are replacing all of the values inside of meetings, but aren't creating a new list object. (The : syntax is called "slicing" and is partially described in the Python tutorial.)

like image 59
Jeremy Avatar answered Dec 01 '25 13:12

Jeremy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!