3

I want to construct a dict as a class variable using another class variable. This does work in a loop (see order1 below), but not in a dict comprehension (see order2 below). I get an error message that choices was not defined.

A list comprehension, however, does work (see order3), but that's not what I want. Edit: In fact, order3 also gives an error, but on invocation, not on definition, because it defines a generator object, not a list.

class Foo():
    ALICE = 'TXT42'
    BOB = 'TXT4711'
    CHARLIE = 'TXT23'

    # List of lists for usage in a Django ChoiceField
    choices = (
        (ALICE, 'Alice'),
        (BOB, 'Bob'),
        (CHARLIE, 'Charlie'),
    )

    # Now I want to define an order for my constants in a dict, with
    # order[ALICE] < order[BOB] < order[CHARLIE]

    # This works, but is clumsy
    order1 = {}
    for i in range(len(choices)):
        order1[choices[i][0]] = i

    # This gives an error (but works if 'choices' is global):
    # "NameError: global name 'choices' is not defined"
    order2 = { choices[i][0]: i for i in range(len(choices)) }

    # This gives a list of dicts, not a dict (but doesn't throw an error)
    # My mistake: it gives a generator object. It will throw the same
    # error, if you try to access it or make it a list, 
    # e.g. order3 = list(...)
    order3 = ( { choices[i][0]: i } for i in range(len(choices)) )

When I try the whole code in a function definition instead of a class, there is no error. It also works when choices really is a global variable, but not when it is a class variable.

Apparently, there is a small difference in the possibilities of a list comprehension and a dict comprehension in the context of class variables. I consider this a bug for now, but feel free to give another explanation.

Python versions tried: 2.7.10 and 3.4.3 under Debian GNU/Linux


Edit: Thanks to @BrenBarn for finding the duplicate I didn't find. It explains the problem I encountered here. It's not a bug, but not really a feature either. ;-)

Dubu
  • 315
  • 1
  • 10

1 Answers1

1

This dictionary comprehension will be suitable for your needs:

order2 = {choice[0]: idx for idx, choice in enumerate(choices)}
gtlambert
  • 11,711
  • 2
  • 30
  • 48