0

Why this doesn't work (Python 3.6.9)? What workarounds or best practices exist for this situation, if any? Are there some rules I'm missing about class variable definition?

I'd like to have some "static" dictionaries for my class. The following block of code

class A:
  y = [1.0, 2.0, 3.0, 4.0]
  # This class variable is also "dynamically" defined, but Python has no problem with it.
  t = y + y
  z = {f : int(f) for f in y}
  a = {z[f] for f in z}  # Fails here

  def __init__(self):
    print(self.t)

A()

fails with

Traceback (most recent call last):
  File "testfile.py", line 1, in <module>
    class A:
  File "testfile.py", line 6, in A
    a = {z[f] for f in z}
  File "testfile.py", line 6, in <setcomp>
    a = {z[f] for f in z}
NameError: name 'z' is not defined

Python documentation says: "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." I understand that it might be "not a good idea" to define a dictionary or list as a class variable, but Python seems to be prohibiting it altogether when defined from the keys of another class variable dictionary.

What am I missing?

cydonian
  • 1,686
  • 14
  • 22
  • 1
    Basically just don't use comprehensions in a class body. – user2357112 Mar 26 '20 at 00:20
  • 1
    @Prune Why does it work in the dictionary comprehension that uses `y`? – Barmar Mar 26 '20 at 00:23
  • Because I flipped my comprehension inside out ... it's the expression that fails to bind, not the iterator. Deleting my earlier comment ... – Prune Mar 26 '20 at 00:30
  • The long and the short of it is that it is best to avoid list-comprehensions in class definition scopes. A list comprehension is essentially implemented as a function, and as you probably know already class scopes do not create enclosing scopes (which is why you can't access `y` inside a method, you need `A.y` or `self.y`). In this case, the leftmost iterable in a possibly chained `for x in foo for y in bar` ... part of a list comprehension is special-cased, because it is essentially passed as an argument to a function which executes your list comprehension. – juanpa.arrivillaga Mar 26 '20 at 00:44

0 Answers0