MattoTodd
MattoTodd

Reputation: 15219

Python - Getting access to class instance via module property (by string name)

Ok, let me try and explain this to the best of my ability. Lets say I have a class called Foobar:

class Foobar():
    def __init__(self, foo, bar, choo):
        self.foo = foo
        self.bar = bar
        self.choo = choo

    def doIt(self):
        return self.foo + self.bar

and I want to have a set of 10 possibilities of instances of Foobar that I will use in my application. Now my end user does not know or care about the values of foo, bar and choo, but would would like to pick one of the 10 possibilities, which I will label.

So I would like to store their choice in a db (but I only want to store the label, which represents a particular instance of the Foobar class). So that way i can just grab the instance I need.

So I tried setting up all my instances in a module like so (lets call it "my_instances.py")

import Foobar

label_one = Foobar("eenie", "meenie", "miney")
label_two = Foobar("teeny", "toony", "tiny")
...
label_ten = Foobar("biggie", "boogie", "baggie")

and that way, once I have the string from the db that represents their choice, I can grab that instance like so (I'm ignoring how you would get the string, but showing how I'm obtaining the instance).

import my_instances

my_object = getattr(my_instances, 'label_one')
result = my_object.doIt()

I am getting a TypeError: unbound method doIt() must be called with Foobar instance as first argument.

So it appears I'm getting Foobar, but not a real instance of it. Any help would be greatly appreciated. I believe I've explained my scenario enough that is you see a simpler workaround for what I am trying to do, please suggest it.

Upvotes: 1

Views: 1367

Answers (3)

senderle
senderle

Reputation: 151147

I can't reproduce your error, (using from foobar import Foobar) so it seems there's something in your code that you're not showing us. But it's better not to do dynamic lookup anyway. Why not create a factory function that returns a dict of foobars?

def get_foobars(x, y, z):
    foobars = {}
    foobars["label_one"] = Foobar(x, y, z)
    foobars["label_two"] = Foobar(z, y, x)
    ...

Or, if you don't want the function to create new foobars every time,

foobars = {}
foobars["label_one"] = Foobar(...

def get_foobars():
    return foobars

Or, more tersely,

foobars = {'label_one':Foobar("eenie", "meenie", "miney"), 
           'label_two':Foobar(...),
           ... } 

Upvotes: 1

Hugh Bothwell
Hugh Bothwell

Reputation: 56694

@payne's lookup table creates ten instances of Foobar and then returns the one you want to use; this seems wasteful. Why not instantiate on demand, like so?

class Foobar():
    def __init__(self, foo, bar, choo):
        self.foo = foo
        self.bar = bar
        self.choo = choo

    def doIt(self):
        return self.foo + self.bar


makeFooLookup = {
    "first":  ("eenie", "meenie", "miney"),
    "second": ("teeny", "toonie", "tiny"),
    "third":  ("biggie", "baggie", "boogie")
}
def makeFoo(label, lookup=makeFooLookup):
    return Foobar(*lookup[label])

Upvotes: 1

payne
payne

Reputation: 14187

As the comment suggests, you likely have a name collision between Foobar the class and Foobar the module.

However, it might be clearer to explicitly define a lookup table:

foobars = {
  'label_one': Foobar("eenie", "meenie", "miney"),
  'label_two': Foobar("teeny", "toony", "tiny"),
  ...
  'label_ten': Foobar("biggie", "boogie", "baggie")
}

Usage:

result = foobars['label_one'].do_it()

Dynamic symbol lookups can be clever, but aren't necessarily clearer.

Upvotes: 4

Related Questions