chbot95
chbot95

Reputation: 11

Change only certain arguments in a class/method, hold others constant

I have a class & method, each with several arguments: my_class(a,b,c).my_method(d,e,f) and I'd like to be able to only change a single argument, while holding the others constant.

Constantly copy-pasting the other constant arguments seems bad, so I'd like to create a new object wrapper_fct where I reference my_class but only provide the one argument I want to change, b, without always having to specify the remaining arguments. How would wrapper_fct() look like?

For example, wrapper_fct(my_class, b1) would return my_class(a,b1,c).my_method(d,e,f), wrapper_fct(my_class, b2) would return my_class(a,b2,c).my_method(d,e,f).

Here's an example in practice:

Loop through just the variable b and evaluate several classes/methods for each new instance of b, and append the results in a list. I can currently do this in a for loop:

mylist1 = [] # init lists (append results here)
mylist2 = []
mylist2 = []

for b in  [1,2,3,4,5]:
   mylist1.append( my_class1(a,b,c).my_method(d,e,f) )
   mylist2.append( my_class2(a,b,c).my_method(d,e,f) )
   mylist3.append( my_class3(a,b,c).my_method(d,e,f) )
   ...

But it seems better to create a function loop_through_B() and use the wrapper_fct(my_class,b) as specified above. Not sure if it's the ideal solution, but maybe something like:

def loop_through_B(input_class, b_values = [1,2,3,4,5])
    mylist = []
    for b in b_values:
        mylist.append( wrapper_fct(input_class,b) )
    return mylist        

loop_through_B(my_class1) # would I also have to specify the method here as well?
loop_through_B(my_class2)
loop_through_B(my_class3)

Extra Question: how would I add the ability to vary method arguments, or even multiple class & method arguments?

Upvotes: 0

Views: 341

Answers (2)

chbot95
chbot95

Reputation: 11

After @chepner pointed me in the right direction, I think the best solution is to use the lambda function:

wrapper_fct = lambda b: my_class1(a,b,c).my_method(d,e,f)

In this case, I can vary b as much as I want while holding the class arguments a,c, and method arguments d,e,f constant. Note that with lambda functions, I can also vary the method arguments and/or the class arguments. For example:

wrapper_fct_multiple = lambda b, e: my_class1(a,b,c).my_method(d,e,f)

It is also possible to do this with functools.partial, but it's not obvious to me how I would specify both class & method arguments with functools.

Anyway, here is the solution implementation using lambda:

# define the "wrapper function" outside the loop
wrapper_fct = lambda b: my_class1(a,b,c).my_method(d,e,f)

# define the function I want to use to loop through B:
def loop_through_B(class_wrapper, b_values)
    mylist = []
    for b in b_values:
        mylist.append( class_wrapper(b) )
    return mylist  

# run:
loop_through_B(wrapper_fct, b_values=[1,2,3,4,5])

# Can make additional wrapper_fct2, wrapper_fct3, for my_class2, my_class3 ...  

Upvotes: 1

martineau
martineau

Reputation: 123473

You can pass the method a dictionary of arguments, and change what the method sees by selectively updating it when calling the method.

Here's what I mean:


class MyClass:
    def __init__(self, a, b, c):
        self.a, self.b, self.c = a, b, c

    def my_method(self, kwargs):
        return sum((kwargs[key] for key in kwargs.keys()))

    def __repr__(self):
      classname = type(self).__name__
      args = ', '.join((f'{v!r}' for v in (self.a, self.b, self.c)))
      return f'{classname}({args})'


instance = MyClass('a','b','c')
print(instance)  # -> MyClass('a', 'b', 'c')

kwargs = dict(d=1, e=2, f=3)
print(instance.my_method(kwargs))              # -> 6
print(instance.my_method(dict(kwargs, e=38)))  # -> 42

Upvotes: 0

Related Questions