Function Repository Resource:

ToPythonFunction

Source Notebook

Convert a Wolfram Language function to a Python function

Contributed by: Igor Bakshee

ResourceFunction["ToPythonFunction"][func]

gives a string of Python code corresponding to the Wolfram Language function func.

ResourceFunction["ToPythonFunction"][session,func,]

deploys the function in the running Python session and returns an ExternalFunction object.

Details and Options

Supported types of functions func include:
body&pure functions in formal parameters #,#2,
Function[{x,y,},body,]pure functions in named arguments
ffunction f defined in the Wolfram Language or C code
"code"Python code string
The following options can be given:
MethodAutomaticmethod to use
Prolog{}Python commands executed before deploying the function
Epilog{}Python functions applied to the result
If the function func is specified as a Python code string, all options of ResourceFunction["ToPythonFunction"] are ignored with the exception of Prolog.
Possible values for the Method option include:
"BuiltIns"attempt to use Python built-in functions and the standard library
"Callback"execute a callback to the Wolfram Language session
"numpy" or "np"use the numpy package, if possible; otherwise "BuiltIns"
Automatic"BuiltIns", if possible; otherwise "Callback"
The method "BuiltIns" currently supports only pure functions and functions defined by a single downvalue f[x_,y_,]:=body ; further, the function body can contain only elementary mathematical functions, with no restrictions on arguments, nor can the function have optional arguments, options or attributes.
The method "Callback" supports arbitrary functions.
In ResourceFunction["ToPythonFunction"][,func,domain,], domain specifies which of the built-in modules to use for the conversion. Possible values include:
Realsuse the "math" module
Complexesuse the "cmath" module
Automaticdetermine heuristically
The default Automatic domain resolves to Reals if the function body does not explicitely contains complex numbers; otherwise to Complexes.
If the heuristic for Method"BuiltIns" fails to automatically convert Wolfram Language code to Python code, you might still be able to convert the code and deploy a Python function manually.
For Method"Callback", the Python code string is defined in terms of the Wolfram Lamguage session variable "wl_session", which can be created on the Python side as "from wolframclient.evaluation import WolframLanguageSession; wl_session = WolframLanguageSession()".
ResourceFunction["ToPythonFunction"][f,Method"Callback",] can return a list in the form {"def","setup"}, where "def" is a string representing the Python-side definition of the function f and "setup" is a Python code string to be executed before calling the function.

Examples

Basic Examples (2) 

Convert a pure function to a Python lambda function:

In[1]:=
ResourceFunction["ToPythonFunction"][1 + Sinh[#1] + 2 #1 + #1^3 &]
Out[1]=

Deploy a Wolfram Language function in a Python session:

In[2]:=
func = E^I Log[#] &
Out[2]=
In[3]:=
session = StartExternalSession["Python"]
Out[3]=
In[4]:=
ResourceFunction["ToPythonFunction"][session, func]
Out[4]=

Evaluate the function value in Python:

In[5]:=
%[10.]
Out[5]=

Compare with the result in the Wolfram Language:

In[6]:=
func[10.]
Out[6]=

Clean up by closing the Python session:

In[7]:=
DeleteObject[session]

Scope (9) 

Elementary functions are convertible to Python using the Python standard library:

In[8]:=
ResourceFunction["ToPythonFunction"][
 Function[x, ((E^x)^(1/3) + x) ArcSin[x] Cos[Log[x^2]]]]
Out[8]=

Special functions are computed by a Wolfram Language callback:

In[9]:=
ResourceFunction["ToPythonFunction"][EllipticK]
Out[9]=

Pure functions in formal parameters # (#1), #2,…:

In[10]:=
ResourceFunction["ToPythonFunction"][Sin[#] &]
Out[10]=
In[11]:=
ResourceFunction["ToPythonFunction"][Sin[#] + Cos[#2] &]
Out[11]=

Pure functions in named arguments:

In[12]:=
ResourceFunction["ToPythonFunction"][Function[x, Sin[x]]]
Out[12]=
In[13]:=
ResourceFunction["ToPythonFunction"][
 Function[{x, y}, Sin[x] + Cos[y]]]
Out[13]=

Vector arguments:

In[14]:=
ResourceFunction["ToPythonFunction"][
 Function[vec, {Sin[vec[[1]]], Cos[vec[[2]]]}]]
Out[14]=

Built-in Wolfram Language functions:

In[15]:=
ResourceFunction["ToPythonFunction"][Abs]
Out[15]=

User-defined functions:

In[16]:=
func[x_] := Sin[x] + Cos[x]
In[17]:=
ResourceFunction["ToPythonFunction"][func]
Out[17]=

Python code strings:

In[18]:=
session = StartExternalSession["Python"]
Out[18]=
In[19]:=
ResourceFunction["ToPythonFunction"][Cos]
Out[19]=
In[20]:=
ResourceFunction["ToPythonFunction"][session, %]
Out[20]=
In[21]:=
DeleteObject[session]

Specify whether to use "math" or "cmath" module on the Python side by giving the optional argument domain:

In[22]:=
ResourceFunction["ToPythonFunction"][I Sin[#1] &, Reals]
Out[22]=
In[23]:=
ResourceFunction["ToPythonFunction"][Sin[#1] &, Complexes]
Out[23]=

Decide heuristically, depending whether the function body contains explicit complex values:

In[24]:=
ResourceFunction["ToPythonFunction"][Sin[#1] &]
Out[24]=
In[25]:=
ResourceFunction["ToPythonFunction"][I Sin[#1] &]
Out[25]=

Options (7) 

Method (4) 

Use a Python-side function from the standard library:

In[26]:=
ResourceFunction["ToPythonFunction"][Sin[#] &, Method -> "BuiltIns"]
Out[26]=

Implement a callback into a WolframLangauge session to compute the function:

In[27]:=
ResourceFunction["ToPythonFunction"][Sin[#] &, Method -> "Callback"]
Out[27]=

Use the Python package numpy:

In[28]:=
ResourceFunction["ToPythonFunction"][Sin[#] &, Method -> "numpy"]
Out[28]=

Use the default heuristic to decide between the methods:

In[29]:=
ResourceFunction["ToPythonFunction"][Sin[#] &]
Out[29]=
In[30]:=
ResourceFunction["ToPythonFunction"][EllipticK[#] &]
Out[30]=

Prolog (1) 

Use the Prolog option to ensure that the Python interpreter meets the requirements before deploying the function:

In[31]:=
session = StartExternalSession["Python"]
Out[31]=
In[32]:=
ResourceFunction["ToPythonFunction"][session, # &, Prolog -> {"import sys", "if sys.version_info.major<5: raise ValueError('Insufficient Python version')"}]
Out[32]=
In[33]:=
DeleteObject[session]

Epilog (2) 

Create a function which returns a Python array:

In[34]:=
session = StartExternalSession["Python"]
Out[34]=
In[35]:=
ResourceFunction["ToPythonFunction"][session, {#, 2 #, 4 #} &]
Out[35]=
In[36]:=
%[1]
Out[36]=

Use the Epilog option to convert the result to numpy.array:

In[37]:=
ResourceFunction["ToPythonFunction"][session, {#, 2 #, 4 #} &, Prolog -> {"import numpy"}, Epilog -> {"numpy.array"}]
Out[37]=
In[38]:=
%[1]
Out[38]=
In[39]:=
% // Normal
Out[39]=
In[40]:=
DeleteObject[session]

Applications (4) 

Functional Programming (4) 

Start a Python session and create a PythonObject for the Probabilistic Numerics Python package ProbNum:

In[41]:=
session = StartExternalSession["Python"]
Out[41]=
In[42]:=
p = ResourceFunction["PythonObject"][session, "probnum"]
Out[42]=

Deploy a Python function describing an ODE vector field:

In[43]:=
f = ResourceFunction["ToPythonFunction"][session, Function[{t, x}, 4 x (2 - x)]]
Out[43]=

Pass the deployed Wolfram Language function f back to Python as a field to solve the logistic ODE:

In[44]:=
t0 = 0; tmax = 1; y0 = {.1};
In[45]:=
solution = p["probsolve_ivp"[f, t0, tmax, y0]]
Out[45]=

Plot the solution:

In[46]:=
ListPlot[Transpose[
  Normal@{Normal[solution["locations"]], Flatten[Normal[
      ResourceFunction["PythonObject"][solution, "states", True][
       "mean"]]]}]]
Out[46]=
In[47]:=
DeleteObject[session]

Properties and Relations (5) 

Callback functions contain a template slot for the Wolfram Language session defined on the Python side:

In[48]:=
str = ResourceFunction["ToPythonFunction"][Sin, Method -> "Callback"]
Out[48]=

Given such a string as an argument, ToPythonFunction automatically fills the slot with the variable wl_session defined in the Python session:

In[49]:=
session = StartExternalSession["Python"]
Out[49]=
In[50]:=
ResourceFunction["ToPythonFunction"][session, str]
Out[50]=

Alternatively, you can fill the slot with the name of another variable of your choice:

In[51]:=
ResourceFunction["ToPythonFunction"][session, TemplateApply[str, "my_session"], Prolog -> {"from wolframclient.evaluation import WolframLanguageSession", "my_session=WolframLanguageSession()"}]
Out[51]=

Call the function:

In[52]:=
%[Pi/2.]
Out[52]=

Clean up:

In[53]:=
DeleteObject[session]

Possible Issues (4) 

For Method"BuiltIns" only functions with a single downvalue are currently supported:

In[54]:=
session = StartExternalSession["Python"]
Out[54]=
In[55]:=
func2[x_] := 1
func2[x_, y_] := 2
In[56]:=
ResourceFunction["ToPythonFunction"][session, func2, Method -> "BuiltIns"]
Out[56]=

Use callback into the Wolfram Language to define the function successfully:

In[57]:=
ResourceFunction["ToPythonFunction"][session, func2]
Out[57]=
In[58]:=
{%["a"], %["a", "b"]}
Out[58]=
In[59]:=
DeleteObject[session]

Method"BuiltIns" and Method"numpy" automatically handle only a subset of functions convertible to Python code:

In[60]:=
session = StartExternalSession["Python"]
Out[60]=
In[61]:=
double[x_] := If[EvenQ[x], x, 2 x]
In[62]:=
ResourceFunction["ToPythonFunction"][session, double, Method -> "BuiltIns"]
Out[62]=

Deploy the function manually:

In[63]:=
ResourceFunction[
 "ToPythonFunction"][session, "lambda x: x if x % 2 == 0 else 2*x"]
Out[63]=

Verify against the direct computation in the Wolfram Language:

In[64]:=
{%[3], %[4]}
Out[64]=
In[65]:=
double /@ {3, 4}
Out[65]=

Clean up:

In[66]:=
DeleteObject[session]

Functions with lists on the left-hand side in downvalues are currently processed with a callback:

In[67]:=
listfunc[{x_, y_}] := {x, y}
In[68]:=
ResourceFunction["ToPythonFunction"][listfunc]
Out[68]=

Extract values on the right-hand side of the function to compute the function in Python without callback:

In[69]:=
listfunc1[xy_] := {xy[[1]], xy[[2]]}
In[70]:=
ResourceFunction["ToPythonFunction"][listfunc1]
Out[70]=

For Method"Callback", functions in the contexts other than "Global`" or "System`" are not currently supported:

In[71]:=
session = StartExternalSession["Python"]
Out[71]=
In[72]:=
ResourceFunction["ToPythonFunction"][session, Developer`EmptyQ]
Out[72]=

Export the function by redefining it in the "Global`" context:

In[73]:=
eQ[args__] := Developer`EmptyQ[args]
In[74]:=
ResourceFunction["ToPythonFunction"][session, eQ]
Out[74]=

Use the Python function:

In[75]:=
%[#] & /@ {{}, {1}}
Out[75]=
In[76]:=
DeleteObject[session]

Version History

  • 1.1.0 – 07 June 2022
  • 1.0.0 – 15 March 2022

Related Resources

Author Notes

In a future version, the Method option can be extended to include other Python packages, such as arbitrary precision Mpmath, etc.


Handling of Python objects is very much underdeveloped. Currently, ToPythonFunction accepts the only case of Function[vars,p], where p is a PythonObject. This can be extended as needed.

Define a PythonObject representing a function that returns a random integer in the range [a,b]:

In[1]:=
session = StartExternalSession["Python"]
In[2]:=
ExternalEvaluate[session, "import random;
def ri(a,b): return random.randint(a,b)"];
In[3]:=
po = ResourceFunction["PythonObject"][session, "ri"]

Test the function by calling the Python object a few times:

In[4]:=
Table[po[All][0, 9], {3}]

Create a one-argument Python function from the object:

In[5]:=
pf = ToPythonFunction[session, Function[t, po]]

Call the new function a few times:

In[6]:=
Table[pf["dummy"][100, 200], {3}]
In[7]:=
DeleteObject[session]

License Information