user3766476
user3766476

Reputation: 173

Calling python dictionary of function from class

I'm relatively new to python and would like to make a class that has a dictionary that corresponds to different methods in the class. I currently have :

class Test:
  functions = {"Test1":test1, "Test2":test2}

  def test1(self, arg1):
    print "test1"

  def test2(self, arg1):
    print "test2" + arg1

  def call_me(self, arg):
    self.functions[arg](arg)

Then in my main.py I have the following:

from Test import Test

t = Test()

t.call_me('Test1')

When I call this function I get an error saying name test1 is not defined. Can anyone tell me what I am doing wrong? Any help would be greatly appreciated.

Upvotes: 4

Views: 9897

Answers (4)

Stephen Ellwood
Stephen Ellwood

Reputation: 434

I realise that this is an old question, but I was looking for help and came across it so I thought I would include an answer for future searchers....

What I wanted to (a) group some private functions under a class, and (b) use a dictionary to choose which function to call at run-time. The step I was missing was to make each method a static method. The prototype below shows the functionality:

class Test():

    @staticmethod
    def Foo():
        return 1

    @staticmethod
    def Baa():
        return 2

    my_dict = {"foo_val": Foo,"baa_val": Baa}

    def __init__(self):
        pass

to call it I use

my_funct = Test()
print(my_funct.my_dict["baa_val"]())

Upvotes: 0

abarnert
abarnert

Reputation: 366213

You've got multiple problems here.

First, in this line:

functions = {"Test1":test1, "Test2":test2}

At the time Python executes this line of code, there is nothing called test1 or test2, so you're going to get an immediate NameError. If you want to do things this way, you're going to have define functions after all of the functions have been defined, not before.


Next, on the same line, test1 and test2 at this point are plain-old functions. But you're trying to call them as if they were methods, with the magic self and everything. That isn't going to work. If you understand how methods work, it should be obvious that you can work around this in the call_me method:

def call_me(self, arg): self.functions[arg].__get__(self, type(self))(arg)

(In this case, you can also get away with just explicitly passing self as an extra argument. But make sure you understand why before doing that.)


Finally, you're trying to call call_me with the function test1, instead of the name 'Test1'. Presumably the whole reason you've created this mapping is so that you can use the names (dynamically, as strings), so let's actually use them:

t.call_me('Test1')

Note that if the only reason you can't use getattr is that the runtime names you want to look up aren't the same as the method names you want to define, you can always have a map of strings to strings, and look up the resulting strings with getattr, which avoids all the other problems here. Borrowing from aruisdante's answer, adding in the name lookup and remembering to pass arg:

functions = {"Test1": "test1", "Test2": "test2"}

def call_me(self, arg):
    return getattr(self, self.functions[arg])(arg)

Upvotes: 7

aruisdante
aruisdante

Reputation: 9115

You need string quotes around your argument, and the T needs to be capitalized:

t.call_me('Test1')

However, python already has the functionality you're trying to replicate built into it via the getattr method. I.E. you can just do:

def call_me(self, arg):
    return getattr(self, arg)()

Note that in this case, the name must be exactly the same as the method name or it will raise an AttributeError, so it would be:

t.call_me('test1')

UPDATE

So now that you've edited your question, it's clear what the problem is:

class Test:
    functions = {"Test1":test1, "Test2":test2}

This is defining functions at the static/class scope. At this point, test1 and test2 haven't actually been created yet, and they aren't bound to a class instance (so no way to know what self should be). The 'correct' solution if you wanted to have arbitrary mappings (so getattr doesn't fit the bill) would be to move this inside an __init__():

class Test:
    def __init__(self):
        self._functions = {"Test1":self.test1, "Test2":self.test2}

    def call_me(self, arg):
        return self._functions[arg](arg)

Upvotes: 7

MattDMo
MattDMo

Reputation: 102942

In your dict, "Test1" and "Test2" are capitalized, while the corresponding functions are not. Change the dict keys to lowercase and everything should work.

Upvotes: 0

Related Questions