Strinnityk
Strinnityk

Reputation: 618

Python: Unresolved reference to class from static variable

So I got this code:

class MyClass:

    ACTIONS = {
        "ACTION_A": MyClass.__a,
        "ACTION_B": MyClass.__b
    }

    @staticmethod
    def do(constant):
        ACTIONS[constant]()

    @staticmethod
    def __a():
        print("A")

    @staticmethod
    def __b():
        print("B")

I'm trying to map the private __a and __b functions to a static dictionary so I'm able to execute functions with the method do.

When trying to run this code I get the error: "Unresolved reference 'MyClass'" on each line of the ACTIONS dictionary.

Any idea on how make this work properly?

Upvotes: 0

Views: 4253

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1124238

You shouldn't be using a class in the first place. All you are doing is creating a namespace, use a module for that. Create a new module in a package and put all your functions in that:

def _a():
    print("A")

def _b():
    print("B")


ACTIONS = {
    'ACTION_A': _a,
    'ACTION_B': _b,
}

def do(constant):
    ACTIONS[constant]()

Note that I used single underscore names. Python uses double-underscore names in classes to create an additional per-class namespace. MyClass.__a becomes MyClass._MyClass__a to avoid clashes with subclasses (so they can freely re-use names without fear of breaking the implentation of a superclass), there is no privacy model in Python.

You could use a decorator to register the _a and _b functions:

def do(constant):
    ACTIONS[constant]()

ACTIONS = {}

def action(name):
    def decorator(f):
        ACTIONS[name] = f
        return f

@action('ACTION_A')
def _a():
    print("A")

@action('ACTION_B')
def _b()
    print("B")

The specific error you see is due to the MyClass name not being set until the whole class statement has completed. You'd have to set that dictionary afterwards:

class MyClass:
    @classmethod
    def do(cls, constant):
        cls.ACTIONS[constant]()

    @staticmethod
    def _a():
        print("A")

    @staticmethod
    def _b():
        print("B")

MyClass.ACTIONS = {
    'ACTION_A': MyClass._a,
    'ACTION_B': MyClass._b,
}

Note that do is a class method, you can't just access ACTIONS as a global, you need to use either MyClass.ACTIONS or, as I used above, a class method then reference the object on cls.

You could work around having to set ACTIONS after the class statement by using names instead, and make def do a class method:

class MyClass:
    ACTIONS = {
        'ACTION_A': '_a',
        'ACTION_B': '_b',
    }

    @classmethod
    def do(cls, constant):
        getattr(cls, cls.ACTIONS[constant])()

    @staticmethod
    def _a():
        print("A")

    @staticmethod
    def _b():
        print("B")

Upvotes: 5

Related Questions