saroele
saroele

Reputation: 10109

how to make my own mapping type in python

I have created a class MyClassthat contains a lot of simulation data. The class groups simulation results for different simulations that have a similar structure. The results can be retreived with a MyClass.get(foo) method. It returns a dictionary with simulationID/array pairs, array being the value of foo for each simulation.

Now I want to implement a method in my class to apply any function to all the arrays for foo. It should return a dictionary with simulationID/function(foo) pairs.

For a function that does not need additional arguments, I found the following solution very satisfying (comments always welcome :-) ):

def apply(self, function, variable):
    result={}
    for k,v in self.get(variable).items():
        result[k] = function(v)
    return result

However, for a function requiring additional arguments I don't see how to do it in an elegant way. A typical operation would be the integration of foo with bar as x-values like np.trapz(foo, x=bar), where both foo and bar can be retreived with MyClass.get(...)

I was thinking in this direction:

def apply(self, function_call):
    """
    function_call should be a string with the complete expression to evaluate
    eg: MyClass.apply('np.trapz(QHeat, time)')

    """
    result={}
    for SID in self.simulations:
        result[SID] = eval(function_call, locals=...)

    return result

The problem is that I don't know how to pass the locals mapping object. Or maybe I'm looking in a wrong direction. Thanks on beforehand for your help.

Roel

Upvotes: 1

Views: 326

Answers (2)

Dirk
Dirk

Reputation: 2388

I tried to recreate (the relevant part of) the class structure the way I am guessing it is set up on your side (it's always handy if you can provide a simplified code example for people to play/test).

What I think you are trying to do is translate variable names to variables that are obtained from within the class and then use those variables in a function that was passed in as well. In addition to that since each variable is actually a dictionary of values with a key (SID), you want the result to be a dictionary of results with the function applied to each of the arguments.

class test:
    def get(self, name):
        if name == "valA":
            return {"1":"valA1", "2":"valA2", "3":"valA3"}
        elif name ==  "valB":
            return {"1":"valB1", "2":"valB2", "3":"valB3"}
    def apply(self, function, **kwargs):
        arg_dict = {fun_arg: self.get(sim_args) for fun_arg, sim_args in kwargs.items()}
        result = {}
        for SID in arg_dict[kwargs.keys()[0]]:
            fun_kwargs = {fun_arg: sim_dict[SID] for fun_arg, sim_dict in arg_dict.items()} 
            result[SID] = function(**fun_kwargs)
        return result

def joinstrings(string_a, string_b):
    return string_a+string_b

my_test = test()
result = my_test.apply(joinstrings, string_a="valA", string_b="valB")
print result

So the apply method gets an argument dictionary, gets the class specific data for each of the arguments and creates a new argument dictionary with those (arg_dict).

The SID keys are obtained from this arg_dict and for each of those, a function result is calculated and added to the result dictionary.

The result is:

{'1': 'valA1valB1', '3': 'valA3valB3', '2': 'valA2valB2'}

The code can be altered in many ways, but I thought this would be the most readable. It is of course possible to join the dictionaries instead of using the SID's from the first element etc.

Upvotes: 1

mg.
mg.

Reputation: 8012

You have two ways. The first is to use functools.partial:

foo = self.get('foo')
bar = self.get('bar')
callable = functools.partial(func, foo, x=bar)
self.apply(callable, variable)

while the second approach is to use the same technique used by partial, you can define a function that accept arbitrary argument list:

def apply(self, function, variable, *args, **kwds):
    result={}
    for k,v in self.get(variable).items():
        result[k] = function(v, *args, **kwds)
    return result

Note that in both case the function signature remains unchanged. I don't know which one I'll choose, maybe the first case but I don't know the context on you are working on.

Upvotes: 2

Related Questions