Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python scoping issue with dictionary comprehension inside class level code

Minimal example

class foo:
    loadings = dict(hi=1)
    if 'hi' in loadings:
        print(loadings['hi']) 
        # works
        print({e : loadings[e] for e in loadings})
        # NameError global name 'loadings' not defined

I tried referencing the class namespace as well but that isn't working either

class foo:
    loadings = dict(hi=1)
    if 'hi' in loadings:
        print(loadings['hi'])
        #works
        print({e : foo.loadings[e] for e in foo.loadings})
        #NameError: name 'foo' is not defined

And of course, this works as expected

class foo:
    loadings = dict(hi=1)
    if 'hi' in loadings:
        print(loadings['hi'])

print({e : foo.loadings[e] for e in foo.loadings})

I want to understand why this scope issue is happening then, if I am trying to do something insane understand the best way to do it otherwise. My feeling was that the first code snip should have worked as is, but of course it does not.

The goal

I am creating a DataManager class/module for some csv/json files along with canned database queries, a one stop shop for my program and obtaining data. There is some static data and some dynamic data so it seemed like a great use of static and non-static data members in the same class. While I understand that these could be module level variables, I like the concept of having static class data members (possibly because of a bias from Java). Any help is much appreciated

My solution (for now)

I ended up unfurling the list comprehension to stay in class scope, in the above it would become something like this

class foo:
    loadings = dict(hi=1)
    temp = dict()
    for e in loadings:
        temp[e] = loadings[e] # keep in mind this is a minimal example, I probably wouldn't do (just) this
    print(temp) # works
    del temp

It is not pretty but it works for now

like image 940
user25064 Avatar asked Apr 01 '14 14:04

user25064


1 Answers

Per the Name and Binding docs:

The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes comprehensions and generator expressions since they are implemented using a function scope. This means that the following will fail:

class A:
    a = 42
    b = list(a + i for i in range(10))

See this answer for more details.


In Python2, it is possible to use list comprehensions, since they were implemented without using a function scope:

dict([(e,loadings[e]) for e in loadings])

But this code would break if run in Python3. So here is an alternative workaround which would work in Python2 and Python3:

class Foo:
    def loadings():
        load = dict(hi=1)
        if 'hi' in load:
            print(load['hi']) 
            print({e:load[e] for e in load})
        return load
    loadings = loadings()

print(Foo.loadings)
like image 147
unutbu Avatar answered Oct 31 '22 07:10

unutbu