views:

95

answers:

4

I have this code that executes when a player attempts to eat something:

def eat(target='object'):
    global current_room
    global locations
    global inventory
    if target in inventory:
        items[target]['on_eat'] #This is showing no results.
    else:
        print 'You have no ' + target + ' to eat.'

and this code for items(trimmed)

items = {
'strawberry': {
    'weight': 1,
    'text': 'The strawberry is red',
    'on_eat': "normal_eat('strawberry', 'pretty good, but not as sweet as you expected')"
    },
'trees': {
    'weight': 50,
    'text': 'The trees are tall with large, leaf filled branches blocking out a majority of sunlight.',
    'on_eat': "forcesay('Eating trees? What the hell is your problem?')"
    }
}

Is there a valid way of calling items[whatever]['on_eat'] without doing something silly like exec() or eval()? If not, alternative formatting as an example would also be appreciated.

Before this the items[everyitems]['on_eat'] values were not strings, but that executed the on_eat for every item as soon as the code was ran.

I have seen many answers to similar questions, but they don't deal with arguments for functions unique- to better put that, they were more like this

+1  A: 

You can use PyPy to interpret the value "manually" or re-invent the python interpreter yourself...?

This is a joke, please don't downvote me... too much...

elcuco
+5  A: 

You can store your function and function arguments as a partial:

from functools import partial

items = { 
'strawberry': { 
    'weight': 1, 
    'text': 'The strawberry is red', 
    'on_eat': partial(normal_eat, 'strawberry', 'pretty good, but not as sweet as you expected') 
    }, 
'trees': { 
    'weight': 50, 
    'text': 'The trees are tall with large, leaf filled branches blocking out a majority of sunlight.', 
    'on_eat': partial(forcesay, 'Eating trees? What the hell is your problem?')
    } 

def eat(target='object'):  
    # those globals are probably not necessary
    if target in inventory:  
        items[target]['on_eat']()  #Add ()'s to call the partial
    else:  
        print 'You have no ' + target + ' to eat.'
Paul McGuire
That's odd; It works. Well I suppose it makes sense, can you/someone tell me how this goes down speed/cpu/steps wise? Also, why don't the partials just execute haphazardly once items is defined like when items[blah]['on_eat'] values were ""less/normal function calls?
mask_man
Because the partial doesn't actually *call* the function when the partial is created in the on_eat elements of your items dict. All it does is capture the function to be called and any arguments to call it with. The function gets called when you access the partial and call it with ()'s. Try this: z = partial(min, 2, 4, 6) Nothing happens. Now call z: z() You get back the answer 2. min was not called when the partial was created; it was only called when you invoked the partial as z(). Speedwise, it should be pretty close to the same as just calling the original function.
Paul McGuire
Thanks. Also, what if I want to do multiple things at once with items['magical transportation food from Dante\'s Inferno']['on_eat'] that prints, changes current_room, calls normal_eat, prints again, explodes users speakers, ect.I assume I would have to make a separate function that does all this?
mask_man
Or just modify your data structure to store a list of partials instead of just a single partial, and call them one after the other. This is kind of what I do in pyparsing with parse actions.
Paul McGuire
A: 

you can use the code module

def eat(target='object'):
    import code
    console = code.InteractiveConsole(locals()) # make a python interpreter with local vars
    if target in inventory:
        console.push("items[target]['on_eat']")
    else:
        print 'You have no ' + target + ' to eat.'
sorry i missed a closing quote after object
well then edit it!
aaronasterling
A: 

An alternative to partial functions is to write items like this

items = {
'strawberry': {
    'weight': 1,
    'text': 'The strawberry is red',
    'on_eat': (normal_eat,('strawberry', 'pretty good, but not as sweet as you expected'))
    },
'trees': {
    'weight': 50,
    'text': 'The trees are tall with large, leaf filled branches blocking out a majority of sunlight.',
    'on_eat': (forcesay,('Eating trees? What the hell is your problem?',))
    }
}

and call it like this

def eat(target='object'):
    if target in inventory:
        func, args = items[target]['on_eat']
        func(*args)
    else:
        print 'You have no ' + target + ' to eat.'

You don't need those global statements there unless you will be reassigning them

gnibbler