Reputation: 279
I apologise if there is already an answer to my question, I've searched stack overflow for a while but found nothing that I could use.
I'm learning how to create classes at the moment and I've constructed classes for the explicit Runge-Kutta methods 1-4. The names of the classes are 'RK_1', 'RK_2', 'RK_3' and 'RK_4'. In order to test my code, I decided to solve the Legendre differential equation, which I also created a class for called 'Legendre'.
Now I wanted to solve the problem, so I wrote a function that uses a particular RK scheme and solves the Legendre problem. I wanted to do this for each one of my RK schemes, so I wrote the same function 4 times i.e
def solve_Legendre_1(p,Tmax,init,dt=0.001):
f = Legendre(p)
solver = RK_1(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
def solve_Legendre_2(p,Tmax,init,dt=0.001):
f = Legendre(p)
solver = RK_2(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
def solve_Legendre_3(p,Tmax,init,dt=0.001):
f = Legendre(p)
solver = RK_3(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
def solve_Legendre_4(p,Tmax,init,dt=0.001):
f = Legendre(p)
solver = RK_4(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
However, I realised there must be an easier way to do this. So I thought I might be able to use a loop and str.format() to change the name of the function and get it to take in its corresponding RK scheme, something like
for j in range(4):
def solve_Legendre_%s(p,Tmax,init,dt=0.001) % (j+1):
f = Legendre(p)
solver = RK_%s(init,f) % (j+1)
while solver.now() < Tmax:
solver(dt)
return solver.state()
but obviously this won't work. Does anyone know how I should approach this?
Thanks for your help.
Upvotes: 1
Views: 1225
Reputation: 360
You should add a parameter to you function. So you have one function to solve all your schemes :
def solve_Legendre(p,Tmax,init,dt=0.001, RK):
f = Legendre(p)
solver = RK(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
You can set a default value for RK if you want :
def solve_Legendre(p,Tmax,init,dt=0.001, RK=RK_1):
Upvotes: 1
Reputation: 2992
Your functions differ only by RK_x function, which you can simply pass by additional variable. This will minimize code redundancy:
def solve_Legendre(RK_func, p,Tmax,init,dt=0.001):
f = Legendre(p)
solver = RK_func(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
Now to your question - you can query globals for this. globals() function will return map with every object defined at global scope keyed by identifier. So you could write (using my previous function):
for j in range(4):
globals()['solve_Legendre_%d' % (j + 1)] = lambda *args, **kw_args: solve_Legendre(globals()['RK_%d' % (j + 1)], *args, **kw_args)
Upvotes: 1
Reputation: 3570
You can use arguments not only to give "usual" things, like numbers, lists or strings to a function, you can also use functions themselves as parameters:
>>> def test1():
print(1)
>>> def test2():
print(2)
>>> def solve_with_func(funcname):
funcname()
>>> solve_with_func(test1)
1
>>> solve_with_func(test2)
2
This way you can use the same logic in solve_with_func
and simply swap out the function that is executed.
This can of course be extended to lists of functions:
>>> def execute_all(funclist):
for f in funclist:
f()
>>> execute_all([test1, test2])
1
2
Upvotes: 1
Reputation: 95742
You can simply pass the RK_n()
function in as a parameter to avoid duplicating the other function:
def solve_Legendre(p,Tmax,init,dt=0.001, RK=RK_1):
f = Legendre(p)
solver = RK(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
and if you want you can bind that last parameter in advance:
import functools
solve_Legendre_1 = functools.partial(solve_Legendre, RK=RK_1)
solve_Legendre_2 = functools.partial(solve_Legendre, RK=RK_2)
...
Upvotes: 7