Defining a global function in a Python script
Solution 1
Accept the function as an argument:
def rk4(diff, # accept an argument of the function to call
x, dt)
k1=diff(x)*dt
k2=diff(x+k1/2)*dt
k3=diff(x+k2/2)*dt
k4=diff(x+k3)*dt
return x+(k1+2*k2+2*k3+k4)/6
Then, when you call rk4
, simply pass in the function to be executed:
from rk4 import rk4
import numpy as np
def diff(x):
return x
def mercury(u0,phi0,dphi):
x=np.array([u0,phi0])
dt=2
x=rk4(diff, # here we send the function to rk4
x, dt)
return x
mercury(1,1,2)
It might be a good idea for mercury
to accept diff
as an argument too, rather than getting it from the closure (the surrounding code). You then have to pass it in as usual - your call to mercury
in the last line would read mercury(diff, 1, 1, 2)
.
Functions are 'first-class citizens' in Python (as is nearly everything, including classes and modules), in the sense that they can be used as arguments, be held in lists, be assigned to names in namespaces, etc etc.
Solution 2
diff
is already a global in the module mercury.py
. But in order to use it in rk4.py
you would need to import it like this:
from mercury import diff
That's the direct answer to your question.
However, passing the diff
function to rk4
as suggested by @poorsod is much more elegant and also avoids a circular dependency between mercury.py
and rk4.py
, so I suggest you do it that way.
Dylan B
Updated on July 20, 2022Comments
-
Dylan B almost 2 years
I'm new to Python. I am writing a script that will numerically integrate a set of ordinary differential equations using a Runge-Kutta method. Since the Runge-Kutta method is a useful mathematical algorithm, I've put it in its own .py file, rk4.py.
def rk4(x,dt): k1=diff(x)*dt k2=diff(x+k1/2)*dt k3=diff(x+k2/2)*dt k4=diff(x+k3)*dt return x+(k1+2*k2+2*k3+k4)/6
The method needs to know the set of equations that the user is working with in order to perform the algorithm, so it calls a function
diff(x)
that will find give rk4 the derivatives it needs to work. Since the equations will change by use, I want diff() to be defined in the script that will run the particular problem. In this case the problem is the orbit of mercury, so I wrotemercury.py
. (This isn't how it will look in the end, but I've simplified it for the sake of figuring out what I'm doing.)from rk4 import rk4 import numpy as np def diff(x): return x def mercury(u0,phi0,dphi): x=np.array([u0,phi0]) dt=2 x=rk4(x,dt) return x mercury(1,1,2)
When I run mercury.py, I get an error:
File "PATH/mercury.py", line 10, in mercury x=rk4(x,dt) File "PATH/rk4.py", line 2, in rk4 k1=diff(x)*dt NameError: global name 'diff' is not defined
I take it since
diff()
is not a global function, when rk4 runs it knows nothing about diff. Obviously rk4 is a small piece of code and I could just shove it into whatever script I'm using at the time, but I think a Runge-Kutta integrator is a fundamental mathematical tool, just like the array defined in NumPy, and so it makes sense to make it a function that is called rather one that is defined in every script that uses it (which may be many). But I also can't go telling rk4.py to import a particular diff from a particular .py file, because that ruins the generality of rk4 that I want in the first place.Is there a way to define diff globally within a script like mercury.py so that when rk4 is called, it will know about diff?
-
Benjamin Hodgson over 11 years@JoranBeasley "Not very Pythonic"? No way! This is the only Pythonic solution to the problem. The standard library is absolutely full of functions that accept and return other functions, and the
@decorator
syntax would make no sense if this weren't a major language feature. -
Lukas Graf over 11 yearsWhat's not pythonic about that? Making use of functions being first class objects is very pythonic.
-
Dylan B over 11 yearsThanks! This is similar to how I've seen it done in languages like IDL that have native rk4 routines, and seems to let me preserve the structure I'd like.
-
Joran Beasley over 11 yearsOK ... I guess ... I still think just importing diff to rk4 is better and avoiding the circular dependancy by properly refactoring the code ... but yeah I didnt realize he was calling rk4 from in the mercury function ... sorry (Ill delete my earlier comment)
-
Joran Beasley over 11 yearsI think this is the more elegant solution imho ... passing functions around seems like a dangerous practice to me... (granted this solution requires a code refactor to avoid the circular dependency)
-
Lukas Graf over 11 years@JoranBeasley Yes, since
diff
is only used inrk4.py
it would probably make sense to refactor the code to put it there. But still, especially for a diff-function the pattern of passing in function objects lends itself very naturally. And I honestly don't see how it could be dangerous. -
Joran Beasley over 11 yearsyes I can see the argument for it ... It just still seems sketchy to me :P ... Ive done stuff like this in my code(passing functions) and 99% of the time im passing always the same function...(with the exceptions being the builtins like sum/max/sorted/etc...)
-
Benjamin Hodgson over 11 years@JoranBeasley the OP says "I also can't go telling rk4.py to import a particular diff from a particular .py file, because that ruins the generality of rk4 that I want in the first place." He wants each call to
rk4
to be able to define its owndiff
. The only way to do this is to pass it in as an argument.from...import
breaks reusability. -
Joran Beasley over 11 yearsahh ok I missed that stipulation ... then this makes perfect sense
-
Benjamin Hodgson over 11 years@JoranBeasley Don't be squeamish about passing functions around. It's a perfectly natural and Pythonic thing to do. As I said, the standard library has loads of functions-that-accept-functions. Take a look at the functools module.
-
Dylan B over 11 yearsIn this case I'm sure that won't be the case, as I'm going to be using different
diff
several times in the course of completing this assignment, and in my research in the past (I'm a physics student) I've had to apply thisrk4
method to diverse systems with different applications, often using their own unique definitions ofdiff
. -
Lukas Graf over 11 years@DylanB You also stated in your question But I also can't go telling rk4.py to import a particular diff from a particular .py file, I first missed that. So passing the diff-function is absolutely the right way to go here. Good question BTW!