2

I have the following class in Python that I am trying to use for calling a set of its own methods through a dictionary that has the pointers of the available functions:

class Test():

    functions = {
        'operation_a' : Test.function_a;
        'operation_b' : Test.function_b;
    }

    def run_operations(operation, *args, **kwargs):

        try:
            functions[str(operation)](self, args, kwargs)
        except KeyError:
            // some log ...

    def function_a(self, *args, **kwargs):
        print A

    def function_b(self, *args, **kwargs):
        print B

This first approach seems to be incorrect since the Python interpreter cannot find class 'Test' (NameError: 'Test' is not defined). I could not find a way (either importing the package, the package and the module, from package.module import *... etc.) Therefore, I have 3 solutions for this issue that already worked for me:

  1. define the operations dictionary within the class constructor (__init__()),
  2. move the callable functions to a different class (in my case, the class is in a different module, I did not try with a class within the same module),
  3. define within the same class the functions as @staticmethod

However, I still do not know why the initial approach does not seem to be correct. Therefore, how can I reference to a function within the same class before instantiation?

4 Answers 4

11

The class object doesn't exist before the end of the class statement's body. But the functions are available in the namespace after the def statement's body. So what you want is:

class Test(object):

    def run_operations(self, operation, *args, **kwargs):
        try:
            function = self.functions[operation]
        except KeyError:
            # some log ...
        else:
            function(self, args, kwargs)

    def function_a(self, *args, **kwargs):
        print "A"

    def function_b(self, *args, **kwargs):
        print "B"

    functions = {
        'operation_a' : function_a,
        'operation_b' : function_b,
        }

edit: As alko mentionned, you could also use getattr with the current instance and method name to get the method, but it means all methods become potential 'operations', which is both unexplicit and a potential security issue. One way to still use getattr with explicit "selection" of the legit operations is to just add a marker to the relevant functions, ie:

def operation(func):
    func.is_operation = True
    return func

class Test(object):
    def run_operations(self, operation, *args, **kwargs):
        method = getattr(self, operation, None)
        if method is None:
            # raise or log or whatever
        elif not method.is_operation:
            # raise or log or whatever
        else:
            method(*args, **kwargs)

    @operation
    def operation_a(self, *args, **kwargs):
        print "A"

    @operation
    def operation_b(self, *args, **kwargs):
        print "B"

    def not_an_operation(self):
        print "not me"

Yet another solution is to use an inner class as namespace for the operations, ie:

class Test(object):

    def run_operations(self, operation, *args, **kwargs):
        method = getattr(self.operations, operation, None)
        if method is None: 
            # raise or log or whatever
        else:
            method(self, *args, **kwargs)

    class operations(object):
        @classmethod
        def operation_a(cls, instance, *args, **kwargs):
            print "A"

        @classmethod
        def operation_b(cls, instance, *args, **kwargs):
            print "B"

There are still other possible solutions. Which one is "best" depends on your needs, but unless you're building a framework the dict-based one is as simple, readable and effective as possible.

Sign up to request clarification or add additional context in comments.

Comments

3
class Test():


    def run_operations(operation, *args, **kwargs):

        try:
            functions[str(operation)](self, args, kwargs)
        except KeyError:
            // some log ...

    def function_a(self, *args, **kwargs):
        print A

    def function_b(self, *args, **kwargs):
        print B

    functions = {
        'operation_a' : function_a, #now you can reference it since it exists
        'operation_b' : function_b, #you do not prefix it with class name
    }

3 Comments

So, in the end, the position of the functions within the class it is relevant since Python is an scripting language... is this correct? I mean, the interpreter has to read all class statements top-to-bottom before function names are included in the namespace.
Wrong conclusion... The relative position of the statements is relevant since all Python statements are executable, but that doesn't mean "the interpreter has to read all class statements top-to-bottom before function names are included in the namespace". The code if first compiled to byte-code, then all top-level statements are executed. The execution of the class statement imply creating a namespace, executing all statements - including def statements - in this namespace, then creating a class object from this namespace and binding this class object in the containing namespace.
Thanks bruno thats more than I was willing to type so I just figured I would let him have a slightly wrong conclusion as long as it got him working code :P
2

Do you really need a dict?

getattr(Test(), 'function_a')
<bound method Test.function_a of <__main__.Test object at 0x019011B0>>

all instance methods:

>>> import inspect
>>> dict(filter(lambda x: inspect.ismethod(x[1]), inspect.getmembers(Test())))
{'run_operations': <bound method Test.run_operations of <__main__.Test object at 0x01901D70>>, 'function_b': <bound method Test.
function_b of <__main__.Test object at 0x01901D70>>, 'function_a': <bound method Test.function_a of <__main__.Test object at 0x0
1901D70>>}   

3 Comments

I prefer to use it with a dict for extending this class afterwards and changing the permitted operations for the new subclass. In addition, I find that it is much easier to use a dict in this way than an inspect method, since in case that you include additional functions that are not "callable" with the "run_operations" function, how would you distinguish them by using this introspective approach?
@Ricardo what I meant is that you can have dictionary of names, not actual functions.
@Ricardo: yes it's better to have some explicit marking of the legit "operation" functions / methods. Yet you can still use a getattr based solution, cf my edited answer.
0

You simply can't, because the class is not defined yet. Class definitions, like everything else in Python, are executable code, and the class name is not assigned to the namespace until the definition is executed.

Comments

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.