1

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.

2
  • 10
    Why are you writing the same function four times…? If there's any variability in that function, use a variable inside it. Don't repeat the same code, and most certainly don't start auto-generating repeated code. Commented Jun 26, 2017 at 8:31
  • 1
    @deceze Because it's very late at night so I'm not thinking straight and doing stupid things. Thanks for your help. Commented Jun 26, 2017 at 8:49

4 Answers 4

7

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)
...
Sign up to request clarification or add additional context in comments.

1 Comment

I really should have thought of this myself. Thanks for your help.
1

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

Comments

1

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)

1 Comment

Thank you. Your second piece of code using globals() was exactly what I was looking for. Much appreciated.
1

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):

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.