1

Say I define some class property based on another:

class X:
    foo = 42
    bar = foo + 5

# X.bar == 47

this works fine. However, foo is not available if I use a list (or dict, etc.) comprehension:

class X:
    foo = 42
    bar = [foo + i for i in range(3)]

# NameError: name 'foo' is not defined

This raises two questions:

  1. Why are the locals() at the point of assignment of bar not passed to the comprehension? (The "class definition" scope behaves a lot like any other scope otherwise (even allowing if statements and such) so this surprised me.)
  2. Is there an alternative way to reference foo in the comprehension? (Note that X.foo also causes a NameError as X is not defined at that point.)

I've tested this on Python 3.8, 3.9 and 3.10 and all behave identically.

Seb
  • 4,422
  • 14
  • 23
  • I don't think it's really any nicer than your workaround, but FWIW this works: `f = functools.partial(operator.add, foo); bar = [*map(f, range(3))]`. Similar attempts with `foo` inside the lambda don't work. – slothrop Jul 05 '23 at 15:58
  • 1
    [Maybe in 3.13](https://discuss.python.org/t/pep-709-one-behavior-change-that-was-missed-in-the-pep/26691) (see the first and the last message there). – Kelly Bundy Jul 05 '23 at 16:00
  • 1
    To make matters worse, if you happen to have a `foo` defined at global scope for other reasons, it will work, but with the wrong value. – tdelaney Jul 05 '23 at 16:10
  • Whoops, should have searched a little better! Appreciate the comments though. – Seb Jul 05 '23 at 17:46

1 Answers1

1

One workaround I have come up with involves replacing the list comprehension with a for loop:

class X:
    foo = 42
    bar = []
    
    for i in range(3):
        bar.append(foo + i)

# X.bar == [42, 43, 44]

but I am still interested in more direct answers to the questions raised.

Seb
  • 4,422
  • 14
  • 23
  • 1
    @tdelaney Or [use a for-clause](https://stackoverflow.com/a/76622313/12671057) (just added to the other question). Here it would be `bar = [foo + i for foo in [foo] for i in range(3)]`. – Kelly Bundy Jul 05 '23 at 16:35
  • @KellyBundy - Oops, I tested with foo = 0 and didn't notice I was using the same variable twice. So, `bar = (lambda x: [x+i for i in range(3)])(foo)`. – tdelaney Jul 05 '23 at 16:50