0

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?

10
  • In Python static means something different from the same keyword in Java. Don't use it to manage per-class data. Use classmethods for that. Commented May 11, 2017 at 15:23
  • And, no, MyClass is not yet defined when the ACTIONS mapping is created. That name is set after the class statement has completed. Commented May 11, 2017 at 15:24
  • Last but not least, don't use __name style attributes unless you are building a base class where subclasses should not have to worry about what names might clash (like in a framework). There is no Java-esque privacy model here, those attributes are not inaccessible from outside callers. all they do is give the attributes a different namespace. Commented May 11, 2017 at 15:26
  • I'm not trying to manage any sort of class data, I think. What are you referring to? Commented May 11, 2017 at 15:26
  • 2
    So yes, you can set MyClass.ACTIONS after the class is built, but you can't then expect ACTIONS to work in do(constant), that's not how class scoping works in Python. You'd have to use MyClass.ACTIONS instead. Don't use a class just to bundle a bunch of functions either, classes should not be used just to namespace functions, use modules for that. Commented May 11, 2017 at 15:27

1 Answer 1

5

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")
Sign up to request clarification or add additional context in comments.

1 Comment

That is really interesting, I've learned a couple of valuable Python concepts. Thank you for your insight.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.