#🔒 Class variable causes NameError in comprehension

28 messages · Page 1 of 1 (latest)

sinful grail
#

Does anyone have insight on why this happens?
My class looks like this and works:

class Breakfast:
    food_types = ["Spam", "Ham", "Eggs"]
    food = {
        f: 1 for f in food_types
    }

However, when I extend with like this:

class Breakfast:
    food_types = ["Spam", "Ham", "Eggs"]
    dislike = ["Spam"]  # new
    food = {
        f: 1 for f in food_types
        if f not in dislike  # new
    }

I get an error

NameError: name 'dislike' is not defined

Is the if-clause evaluated in a different scope or something like that?
Please don't suggest alternative ways to implement this, I am just trying to find out why this happens.

timber egretBOT
#

@sinful grail

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

sinful grail
#

!e

class Breakfast:
    food_types = ["Spam", "Ham", "Eggs"]
    food = {
        f: 1 for f in food_types
    }
print(Breakfast.food)
timber egretBOT
sinful grail
#

!e

class Breakfast:
    food_types = ["Spam", "Ham", "Eggs"]
    dislike = ["Spam"]  # new
    food = {
        f: 1 for f in food_types
        if f not in dislike  # new
    }
print(Breakfast.food)
timber egretBOT
sinful grail
#

Class variable causes NameError in comprehension

solemn python
#

!e

food_types = ["Spam", "Ham", "Eggs"]
dislike = ["Spam"]
print([1 if f not in dislike else 0 for f in food_types])

Testing

timber egretBOT
solemn python
#

yeah do the if else first then the for loop

quasi thicket
#

It’s a peculiarity with how class scopes work. Unlike functions or global variables, variables defined at the class level aren’t visible from inside functions.

sinful grail
#

I wonder why food_types works tho

#

Inside functions, you say? Is the if clause implemented as filter or so?

quasi thicket
#

In 3.11 and earlier, a comprehension is implemented as a function, that way the loop variables don't escape to outer scopes. In 3.12 that's changed for performance reasons, but the class variables are still not accessible.

#

The reason {f: 1 for f in food_types} works is that the outermost iterable is evaluated at the class scope and passed in, not in the comprehension's scope.

sinful grail
#

Ah, I see, interesting

#

Good to know, thanks a lot!

quasi thicket
#

Probably the easiest solution is just to make everything module-global.

sinful grail
#

I'd prefer the attributes in the namespace but it's not too bad to take out the "dislike" at least

#

Thanks a lot for the help in figuring this quirk out 😄

solemn python
#

Probably better if its in a function

#

a class function

quasi thicket
#

Well, then it's being recalculated every time, makes stuff more complicated.

solemn python
#

!e

class Breakfast:
    food_types = ["Spam", "Ham", "Eggs"]
    dislike = ["Spam"]  # new

    def food(self):
        food = {
            f: 1 if f not in self.dislike else 0 for f in self.food_types
        }
        return food
    
breakfast = Breakfast()
print(breakfast.food())
timber egretBOT
sinful grail
#

!close

timber egretBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.