Wolfram Research

Function Repository Resource:

PythonObject (1.0.0) current version: 1.4.0 »

Source Notebook

Create a Wolfram Language representation of a Python object

Contributed by: Igor Bakshee

ResourceFunction["PythonObject"][<||>]

represents a Python object defined in an ExternalSessionObject.

ResourceFunction["PythonObject"][session,"cmd"]

evaluates cmd in the specified running ExternalSessionObject and creates a reference to the resulting external object.

ResourceFunction["PythonObject"]["cmd"]

starts a new external session and evaluates cmd.

ResourceFunction["PythonObject"][][prop]

gives the specified property of the ResourceFunction["PythonObject"][].

ResourceFunction["PythonObject"][][func[args,opts]]

calls the function func defined in the Python object with the specified arguments and options.

ResourceFunction["PythonObject"][][All][args,opts]]

invokes the callable Python object with the specified arguments and options.

Details and Options

ResourceFunction["PythonObject"][] is a Wolfram-Language representation of a Python-side object created with ExternalEvaluate.
ResourceFunction["PythonObject"][session,"cmd"] effectively executes ExternalEvaluate[session,"cmd"] and returns either its output or ResourceFunction["PythonObject"][]. The ResourceFunction["PythonObject"] is returned when the output contains ExternalObject[] or ExternalFunction[].
ResourceFunction["PythonObject"][] is also returned for some specific types of ExternalEvaluate failures, such as the ones resulting from an error in passing a valid object to the Wolfram Language.
ResourceFunction["PythonObject"][session,"cmd",True] returns ResourceFunction["PythonObject"][] even for simplest objects, such as integers or strings, or a Failure object if the result of executing "cmd” does not represent a Python object.
ResourceFunction["PythonObject"][session,"cmd",False] returns the raw output of ExternalEvaluate.
ResourceFunction["PythonObject"][session,"cmd",,Initializationinit] performs the specified initialization of the Python session prior to creating the Python object. With the default setting InitializationAutomatic, ResourceFunction["PythonObject"] attempts to detect, install, and import packages required for successful execution of"cmd". Other possible values of init are:
"cmd1;cmd2;…" execute the specified commands
None no initialization
The commands "cmdi" typically include one or more import statements. If needed, the necessary packages are installed automatically using the resource function PythonEvaluateWithDependencies.
ResourceFunction["PythonObject"] takes the option "Configuration" that makes using Python objects more convenient. Configurations are typically provided by Wolfram Research or other developers. See Author Notes for details. Possible option values include:
ResourceFunction["function"] resource function
"Custom" interactively defined
Automatic automatic
None no configuration
Available properties of a Python object p can be queried with p["Properties"]. With a few exceptions, the list does not include "non-public" names that begin with an underscore "_".
Properties of a ResourceFunction["PythonObject"] include object-specific properties as well as the following properties:
"Session" ExternalSessionObject
"PythonReference" a reference to the Python-side object
"ReferenceValid" whether the reference corresponds to an active Python session
"Information" Python-side information
"FullInformation" recursive information for modules
"RawInformation" Python-side help
"Configuration" configuration
Object-specific properties include:
"IsCallable" callable object
"IsModule" module
"IsClass" class
"IsFunction" function
"IsBuiltin" built-in function
The default property is "PythonReference".
ResourceFunction["PythonObject"][]["Information","obj"] gives information on function or class objects defined in the parent module or methods of the parent class.
For callable objects, "Information" also contains the expected call signature including:
"Arguments" arguments
"Options" options
In ResourceFunction["PythonObject"], optional arguments of functions and callable objects are given as Wolfram Language options.
For a ResourceFunction["PythonObject"] p that represents a Python module, p["FullInformation",type] gives a list of attributes of the specified type defined in the module itself and its submodules, recursively. The type can be one of the following:
"Functions" functions
"Classes" classes
"Modules" modules
ResourceFunction["PythonObject"][session,{"cmd1","cmd2",}] returns a list of objects.
ResourceFunction["PythonObject"][session,{"cmd1","cmd2",},{policy1,policy2,}] applies policyi when creating an object from cmdi.
ResourceFunction["PythonObject"][{"cmd1","cmd2",},] creates all obji in the same ExternalSession.
In ResourceFunction["PythonObject"][][prop] and ResourceFunction["PythonObject"][][func[args,opts]], prop, func and options names can be specified as a string or, as long as a name does not include underscores "_", a symbol.
When there is more than one function with the specified name in the Python object, the function name must be given as a string, in the fully qualified dot notation "module1.module2….name", or as a symbol in the corresponding nested context module1`module2`…`name.
Option names in opts can be given as strings or symbols in any context.
ResourceFunction["PythonObject"][]["Assign"["var"val]] sets the value of the ResourceFunction["PythonObject"] variable to the specified value.
For a ResourceFunction["PythonObject"] p representing a callable object, such as classes, callable instances, or functions, the Python-side object can be invoked with p[All][args,opts].
Arguments and options must be given as exportable Python expressions, ExternalFunction object or ResourceFunction["PythonObject"].
ResourceFunction["PythonObject"][session,"TemporaryPackage"[assoc]] creates a Python object from a package specified by the association assoc deployed in a temporary directory.
DeleteObject[p] deletes the Python-side reference to the ResourceFunction["PythonObject"] p.
Deleting ResourceFunction["PythonObject"][] is typically not necessary in short sessions or if the session is about to be closed anyway.
A ResourceFunction["PythonObject"][] is specific to a particular ExternalSessionObject, which may not be persistent.
DeleteObject[p["Session"]] kills the Python session in which the ResourceFunction["PythonObject"] p is defined.

Examples

Basic Examples (2) 

Create a Python object for a package:

In[1]:=
p = ResourceFunction["PythonObject"]["camelcase"]
Out[1]=

Explore its contents:

In[2]:=
p["Information"]
Out[2]=

Create an instance of a class in the package:

In[3]:=
instance = p["CamelCase()"]
Out[3]=

Examine its contents:

In[4]:=
instance["Information"]
Out[4]=

Get a the value of an instance variable:

In[5]:=
instance["stop_words"]
Out[5]=

Find the signature of a method:

In[6]:=
instance["Information", "hump"]
Out[6]=

Call the method:

In[7]:=
instance["hump"["hello world"]]
Out[7]=

Uninstall the package and close the session to clean up:

In[8]:=
ResourceFunction["PythonPackageUninstall"][p["Session"], p["Module"]]
Out[8]=
In[9]:=
DeleteObject[p["Session"]]

Define a simple class in Python:

In[10]:=
session = StartExternalSession["Python"]
Out[10]=
In[11]:=
ExternalEvaluate[session, "class MyClass:
	cvar = 'myClassVariable'
	def __init__(self, x):
		self.x = x
		print('instance initialized with '+str(x))
	def __call__(self, x):
		self.x = x
		return 'instance called with '+str(x)
	def f(self,x,y):
		self.x = x
		self.y = y
		return 'f() fired with '+str(x)+', '+str(y)
"]
Out[11]=

Create a Python object referring to the class:

In[12]:=
p = ResourceFunction["PythonObject"][session, "MyClass"]
Out[12]=

Get the value of a class variable from Python:

In[13]:=
p["cvar"]
Out[13]=

Create an instance of the class:

In[14]:=
instance = p[All][1]
Out[14]=

Check the instance variable:

In[15]:=
instance["x"]
Out[15]=

Set the value of the variable and check the new value:

In[16]:=
instance["Assign"["x" -> 2]]
Out[16]=
In[17]:=
instance["x"]
Out[17]=

Call a method function of the instance:

In[18]:=
instance["f"[2, 3]]
Out[18]=

Check the new instance variables:

In[19]:=
{instance["x"], instance["y"]}
Out[19]=

Close the session:

In[20]:=
DeleteObject[session]

Scope (19) 

Basic Usage (4) 

A single Python object:

In[21]:=
ResourceFunction["PythonObject"]["object()"]
Out[21]=
In[22]:=
DeleteObject[%["Session"]]

Multiple objects:

In[23]:=
ResourceFunction["PythonObject"][{"object()", "object()"}]
Out[23]=
In[24]:=
DeleteObject[First[%]["Session"]]

Create a PythonObject[] only for those objects that do not have a Wolfram Language representation:

In[25]:=
session = StartExternalSession["Python"];
In[26]:=
commands = {"1", "'string'", "datetime.datetime.now()", "object()"};
In[27]:=
ResourceFunction["PythonObject"][session, commands]
Out[27]=

Create Python objects for all objects, even the simplest:

In[28]:=
ResourceFunction["PythonObject"][session, commands, True]
Out[28]=
In[29]:=
DeleteObject[session]

Define an instance object in Python and access its Python-side property:

In[30]:=
session = StartExternalSession["Python"];
In[31]:=
ExternalEvaluate[session, "class MyClass:
	def __init__(self, x):
		self.x = x*x
	def f(self,y):
		return y+100
"]
Out[31]=
In[32]:=
p = ResourceFunction["PythonObject"][session, "MyClass(2)"]
Out[32]=
In[33]:=
p["x"]
Out[33]=

Assign a new value to the property:

In[34]:=
p["Assign"["x" -> 3]]
Out[34]=
In[35]:=
p["x"]
Out[35]=

Call a method:

In[36]:=
p["f"[1]]
Out[36]=

Alternatively, define a Python object referring to the class and construct an instance on the Wolfram-Language side:

In[37]:=
class = ResourceFunction["PythonObject"][session, "MyClass"]
Out[37]=
In[38]:=
instance = class[All][3]
Out[38]=

The new value of the instance variable:

In[39]:=
instance["x"]
Out[39]=

Call a member function:

In[40]:=
p["f"[2]]
Out[40]=
In[41]:=
DeleteObject[session]

Object Types (7) 

Interactively defined object:

In[42]:=
ResourceFunction["PythonObject"]["object()"]
Out[42]=
In[43]:=
DeleteObject[%["Session"]]

A module in Python’s standard library:

In[44]:=
ResourceFunction["PythonObject"]["uuid"]
Out[44]=
In[45]:=
DeleteObject[%["Session"]]

A class in the standard library:

In[46]:=
ResourceFunction["PythonObject"]["uuid.UUID"]
Out[46]=
In[47]:=
DeleteObject[%["Session"]]

An instance of a class in the standard library:

In[48]:=
ResourceFunction["PythonObject"]["uuid.uuid4()"]
Out[48]=
In[49]:=
DeleteObject[%["Session"]]

A function in the standard library:

In[50]:=
ResourceFunction["PythonObject"]["uuid.uuid4"]
Out[50]=
In[51]:=
DeleteObject[%["Session"]]

A built-in function:

In[52]:=
ResourceFunction["PythonObject"]["math.cos"]
Out[52]=
In[53]:=
DeleteObject[%["Session"]]

An installable package, with its classes and functions:

In[54]:=
session = StartExternalSession["Python"];
In[55]:=
ResourceFunction["PythonObject"][session, "requests"]
Out[55]=
In[56]:=
ResourceFunction["PythonObject"][session, "requests.Request"]
Out[56]=
In[57]:=
ResourceFunction["PythonObject"][session, "requests.get"]
Out[57]=

Uninstall when the package no longer needed:

In[58]:=
ResourceFunction["PythonPackageUninstall"][session, "requests"]
Out[58]=
In[59]:=
DeleteObject[session]

Callable Objects (3) 

A class:

In[60]:=
session = StartExternalSession["Python"];
In[61]:=
ExternalEvaluate[session, "class MyClass:
	def __init__(self):
		print('instance created')
"]
Out[61]=
In[62]:=
class = ResourceFunction["PythonObject"][session, "MyClass"]
Out[62]=

Create an instance of the class:

In[63]:=
class[All][]
Out[63]=
In[64]:=
DeleteObject[session]

A callable instance of the class:

In[65]:=
session = StartExternalSession["Python"];
In[66]:=
ExternalEvaluate[session, "class MyClass:
	def __call__(self):
		return 'instance called'
"]
Out[66]=
In[67]:=
instance = ResourceFunction["PythonObject"][session, "MyClass()"]
Out[67]=

Call the instance:

In[68]:=
instance[All][]
Out[68]=
In[69]:=
DeleteObject[session]

A function:

In[70]:=
session = StartExternalSession["Python"];
In[71]:=
ExternalEvaluate[session, "def func():
	return 'func() called'
"]
Out[71]=
In[72]:=
func = ResourceFunction["PythonObject"][session, "func"]
Out[72]=

Call the function:

In[73]:=
func[All][]
Out[73]=
In[74]:=
DeleteObject[session]

Calling Conventions (5) 

Call a function defined in a module via the parent module object:

In[75]:=
session = StartExternalSession["Python"];
In[76]:=
p = ResourceFunction["PythonObject"][session, "requests"]
Out[76]=
In[77]:=
url = "https://wolfram.com";
In[78]:=
p["get"[url]]
Out[78]=
In[79]:=
%["status_code"]
Out[79]=

Alternatively, define a callable function object and call it directly:

In[80]:=
get = ResourceFunction["PythonObject"][session, "requests.get"]
Out[80]=
In[81]:=
get[All][url]
Out[81]=
In[82]:=
%["status_code"]
Out[82]=

Clean up:

In[83]:=
ResourceFunction["PythonPackageUninstall"][session, "requests"]
Out[83]=
In[84]:=
DeleteObject[session]

Define a Python class:

In[85]:=
session = StartExternalSession["Python"];
In[86]:=
ExternalEvaluate[session, "class MyClass:
	def __init__(self, x, y = 0, z=0):
		self.x = x
		self.y = y
		self.z = z
"]
Out[86]=

Create a Python object referring to the class:

In[87]:=
p = ResourceFunction["PythonObject"][session, "MyClass"]
Out[87]=

In PythonObject, optional arguments of a callable Python object become standard Wolfram Language options:

In[88]:=
KeyTake[p["Information"], {"Arguments", "Options"}]
Out[88]=

Create an instance and check its variables:

In[89]:=
instance = p[All][1, "z" -> 2]
Out[89]=
In[90]:=
instance /@ {"x", "y", "z"}
Out[90]=

For comparison, specify the optional argument in the Python code and check the new instance variables:

In[91]:=
ExternalEvaluate[session, "o=MyClass(10,z=20);[o.x, o.y, o.z]"]
Out[91]=
In[92]:=
DeleteObject[session]

Define a Python class in which the constructor takes a function as an argument:

In[93]:=
session = StartExternalSession["Python"];
In[94]:=
ExternalEvaluate[session, "class CallF:
	cvar = 'myClassVariable'
	def __init__(self, f):
		self.f = f
	def call(self):
		return self.f()
"]
Out[94]=

Create a Python object referring to the class:

In[95]:=
p = ResourceFunction["PythonObject"][session, "CallF"]
Out[95]=

To create an an instance of the class, define a function on the Python side:

In[96]:=
ExternalEvaluate[session, "def my_func():
	print('my_func() called')
"]
Out[96]=

Supply the ExternalFunction object to the class constructor::

In[97]:=
p[All][%]
Out[97]=

Invoke a method of the class:

In[98]:=
%["call"[]]

Alternatively, initialize an instance with a PythonObject:

In[99]:=
ResourceFunction["PythonObject"][session, "my_func"]
Out[99]=
In[100]:=
p[All][%]
Out[100]=

Invoke the method:

In[101]:=
%["call"[]]
In[102]:=
DeleteObject[session]

Define a Python function which expects a specific type of a Python object, for instance, the enum Color, as an option:

In[103]:=
session = StartExternalSession["Python"];
In[104]:=
package = "TemporaryPackage"[<|"Name" -> "enumarg",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "from enum import Enum
class Color(Enum):
	RED = 1
	GREEN = 2
	BLUE = 3

def do_color(color=Color.BLUE):
    if not isinstance(color, Color):
        raise TypeError('color is not an instance of the Color Enum')
    return color.value
"|>];
In[105]:=
p = ResourceFunction["PythonObject"][session, package]
Out[105]=

A Python object for the enum:

In[106]:=
clr = p["Color"]
Out[106]=

Supply an enum value to the function:

In[107]:=
p["do_color"["color" -> clr["RED"]]]
Out[107]=
In[108]:=
DeleteObject[session]

Define a class in Python and a corresponding PythonObject:

In[109]:=
session = StartExternalSession["Python"];
In[110]:=
ExternalEvaluate[session, "class MyClass:
	def f(self, x, y=0):
		self.z = x + y
"];

Create a Python object referring to the class:

In[111]:=
p = ResourceFunction["PythonObject"][session, "MyClass()"]
Out[111]=

Call a method function and get the instance variable using strings as names:

In[112]:=
p["f"[1, "y" -> 2]]
p["z"]
Out[112]=

Alternatively, use symbols as names:

In[113]:=
p[f[3, y -> 4]]
p[z]
Out[113]=
In[114]:=
DeleteObject[session]

Options (5) 

Initialization (3) 

Without initialization, PythonObject fails for this command because the required package is not available in the current session:

In[115]:=
session = StartExternalSession["Python"];
In[116]:=
ResourceFunction["PythonObject"][session, "camelcase", Initialization -> None]
Out[116]=

In fact, the package is not even installed:

In[117]:=
KeyExistsQ[
 ResourceFunction["PythonPackageList"][session], "camelcase"]
Out[117]=

With the default setting InitializationAutomatic, the package is automatically installed and imported:

In[118]:=
p = ResourceFunction["PythonObject"]["camelcase", Initialization -> Automatic]
Out[118]=

The package is now installed:

In[119]:=
KeyExistsQ[
 ResourceFunction["PythonPackageList"][p["Session"]], "camelcase"]
Out[119]=

Uninstall to clean up:

In[120]:=
ResourceFunction["PythonPackageUninstall"][p["Session"], "camelcase"]
Out[120]=
In[121]:=
DeleteObject[p["Session"]]

Specify a custom initialization:

In[122]:=
p = ResourceFunction["PythonObject"]["c()", Initialization -> "from camelcase import CamelCase as c"]
Out[122]=

Use the new object:

In[123]:=
p["hump"["hello world"]]
Out[123]=

Uninstall the package and close the session to clean up:

In[124]:=
ResourceFunction["PythonPackageUninstall"][p["Session"], "camelcase"]
Out[124]=
In[125]:=
DeleteObject[p["Session"]]

Configuration (2) 

Without configuration, the raw Python’s uuid4() function returns an instance of the UUID class:

In[126]:=
p = ResourceFunction["PythonObject"]["uuid.uuid4", "Configuration" -> None]
Out[126]=
In[127]:=
p[All][]
Out[127]=
In[128]:=
p["Configuration"]
Out[128]=
In[129]:=
DeleteObject[p["Session"]]

Specify a configuration defined in the resource function UUIDPythonObjectConfiguration:

In[130]:=
p = ResourceFunction["PythonObject"]["uuid.uuid4", "Configuration" -> ResourceFunction["UUIDPythonObjectConfiguration"]]
Out[130]=

The resource function changes the return value of the Python object to a string:

In[131]:=
p[All][]
Out[131]=
In[132]:=
DeleteObject[p["Session"]]

Applications (3) 

Create a PythonObject to efficiently reuse data on the Python side, even when you do not have access to the Python code.

In[133]:=
session = StartExternalSession["Python"]
Out[133]=

Execute some code that defines a big object and keep a reference to that object:

In[134]:=
blackBoxCode = "range(1,100)";
In[135]:=
p = ResourceFunction["PythonObject"][session, blackBoxCode, True]
Out[135]=

Use the object:

In[136]:=
ExternalEvaluate[session, "sum(" <> p[] <> ")"]
Out[136]=
In[137]:=
DeleteObject[p["Session"]]

Make a function defined in an external module available in your Wolfram Language session:

In[138]:=
get = ResourceFunction["PythonObject"]["requests.get"]
Out[138]=

The function signature:

In[139]:=
get["Information"]
Out[139]=

Call the function:

In[140]:=
response = get[All]["https://wolfram.com"]
Out[140]=

Information about the returned object:

In[141]:=
response["Information"]
Out[141]=

The value of one of the available variables:

In[142]:=
response["status_code"]
Out[142]=

Another variable refers to a more complex object:

In[143]:=
headers = response["headers"]
Out[143]=
In[144]:=
headers["Information"]
Out[144]=

Get the signature of one of the methods of the new object:

In[145]:=
headers["Information", "popitem"]
Out[145]=

Call the method several times:

In[146]:=
Table[headers["popitem"[]], {3}]
Out[146]=

Uninstall the package to clean up:

In[147]:=
ResourceFunction["PythonPackageUninstall"][p["Session"], "requests"]
Out[147]=
In[148]:=
DeleteObject[get["Session"]]

Create a Python object for an external package, for instance, Topoly:

In[149]:=
topoly = ResourceFunction["PythonObject"]["topoly"]
Out[149]=

Explore its contents:

In[150]:=
%["Information"]
Out[150]=

Find the signature of a function from the package:

In[151]:=
topoly["Information", "conway"]
Out[151]=

Prepare and plot a list of coordinates that represent a 51knot:

In[152]:=
coordinates51 = CompressedData["
1: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
dw5t
"];
In[153]:=
Graphics3D[Tube[coordinates51, .1]]
Out[153]=

Call the Topoly function and display its result, in this case, a plot of the fingerprint matrix for the coordinates of the knot:

In[154]:=
filename = "map_knot_5_1";
In[155]:=
PrintTemporary["This may take a while..."];
topoly["conway"[coordinates51, "tries" -> 10, "matrix" -> True, "matrix_map" -> True, "map_filename" -> filename]];
In[156]:=
Import[FileNameJoin[{$HomeDirectory, filename <> ".png"}]]
Out[156]=
In[157]:=
DeleteObject[topoly["Session"]]

Properties and Relations (12) 

Some common properties:

In[158]:=
p = ResourceFunction["PythonObject"]["object()"]
Out[158]=
In[159]:=
p["Properties"]
Out[159]=

Curated properties:

In[160]:=
Select[%, StringStartsQ[#, RegularExpression["^[A-Z]"]] &]
Out[160]=
In[161]:=
DeleteObject[p["Session"]]

Contents of a package:

In[162]:=
p = ResourceFunction["PythonObject"]["requests"]
Out[162]=
In[163]:=
p["Information"]
Out[163]=

Recurse into submodules:

In[164]:=
p["FullInformation"]
Out[164]=

All modules in the package:

In[165]:=
p["FullInformation", "Modules"]
Out[165]=

Contents of a module:

In[166]:=
module = p["utils"]
Out[166]=
In[167]:=
module["Information"]
Out[167]=
In[168]:=
DeleteObject[p["Session"]]

Information on a class, including the class variable and the methods:

In[169]:=
session = StartExternalSession["Python"];
In[170]:=
ExternalEvaluate[session, "class MyClass:
	cvar = 'myClassVariable'
	def __init__(self, x):
		self.x = x
		print('instance initialized with '+str(x))
	def add_two(self,x,y):
		return x+y
"]
Out[170]=
In[171]:=
p = ResourceFunction["PythonObject"][session, "MyClass"]
Out[171]=
In[172]:=
p["Information"]
Out[172]=

Information on a class instance, including the class and instance variables and the methods:

In[173]:=
instance = p[All][1]
Out[173]=
In[174]:=
instance["Information"]
Out[174]=
In[175]:=
DeleteObject[p["Session"]]

Obtain the signature of a callable object:

In[176]:=
p = ResourceFunction["PythonObject"]["requests"]
Out[176]=
In[177]:=
p["Information", "Request"]
Out[177]=
In[178]:=
DeleteObject[p["Session"]]

Access Python's "built-ins", mathematical functions defined by the C standard:

In[179]:=
p = ResourceFunction["PythonObject"]["math"]
Out[179]=
In[180]:=
p["FullInformation", "Functions"]
Out[180]=
In[181]:=
p["Information", "nextafter"]
Out[181]=
In[182]:=
p["nextafter"[0, 1]]
Out[182]=
In[183]:=
DeleteObject[p["Session"]]

Use the "PythonReference" property to access the object on the Python side with ExternalEvaluate:

In[184]:=
p = ResourceFunction["PythonObject"]["object()"]
Out[184]=
In[185]:=
ExternalEvaluate[p["Session"], "dir(" <> p["PythonReference"] <> ")"]
Out[185]=

The explicit "PythonReference" specification can be omitted since it is the default property:

In[186]:=
p["PythonReference"]
Out[186]=
In[187]:=
p[]
Out[187]=
In[188]:=
DeleteObject[p["Session"]]

"PythonReference" becomes stale if you delete the Python object:

In[189]:=
p = ResourceFunction["PythonObject"]["object()"];
p["ReferenceValid"]
Out[189]=
In[190]:=
DeleteObject[p]
p["ReferenceValid"]
Out[190]=

"PythonReference" also stales if you close the session in which it is defined:

In[191]:=
p = ResourceFunction["PythonObject"]["object()"];
DeleteObject[p["Session"]]
p["ReferenceValid"]
Out[191]=

Even if not listed in "Properties", native Python attributes are also accessible:

In[192]:=
p = ResourceFunction["PythonObject"]["object()"];
In[193]:=
p["__dir__()"]
Out[193]=
In[194]:=
p["__doc__"]
Out[194]=
In[195]:=
DeleteObject[p["Session"]]

Get the Python-side help:

In[196]:=
p = ResourceFunction["PythonObject"]["object()"];
In[197]:=
p["RawInformation", "__init__"]
In[198]:=
DeleteObject[p["Session"]]

PythonObject is similar to ExternalObject returned by ExternalEvaluate:

In[199]:=
session = StartExternalSession["Python"];
In[200]:=
ResourceFunction["PythonObject"][session, "requests"]
Out[200]=
In[201]:=
ExternalEvaluate[session, "requests"]
Out[201]=

PythonObject is also similar to ExternalFunction object defined to call a Python function:

In[202]:=
efget = ExternalEvaluate[session, "from requests import get; get"]
Out[202]=
In[203]:=
get = ResourceFunction["PythonObject"][session, "requests.get"]
Out[203]=
In[204]:=
get["IsFunction"]
Out[204]=

Call the two functions:

In[205]:=
url = "https://wolfram.com";
In[206]:=
{o, p} = {efget[url], get[All][url]}
Out[206]=

The major difference between the produced objects is that the contents of the latter are accessible directly in the Wolfram Language without any Python programming:

In[207]:=
p["Information"]
Out[207]=
In[208]:=
Snippet[p["text"], 5]
Out[208]=

Uninstall the package to clean up:

In[209]:=
ResourceFunction["PythonPackageUninstall"][p["Session"], "requests"]
Out[209]=
In[210]:=
DeleteObject[p["Session"]]

For callable objects, PythonObject[][All] is used only for calling the object and otherwise returns unevaluated:

In[211]:=
p = ResourceFunction["PythonObject"]["object"]
Out[211]=
In[212]:=
p[All][]
Out[212]=
In[213]:=
p[All]
Out[213]=

Create and deploy a Python package with one unique function name and two identically named functions in two modules:

In[214]:=
session = StartExternalSession["Python"];
In[215]:=
p = ResourceFunction["PythonObject"][session, "TemporaryPackage"[<|"Name" -> "fnames", "__init__.py" -> "from . import mod1, mod2", "mod1.py" -> "def unique(): print('in unique()')\ndef funcion(): print('in fmod1.funcion()')", "mod2.py" -> "def funcion(): print('in mod2.funcion()')"|>]]
Out[215]=

Call the unique function simply by its name, given as a string or a symbol:

In[216]:=
p["unique"[]]
p[unique[]]

However, trying to use non-uniquely named functions the same way gives an error:

In[217]:=
p["funcion"[]]
Out[217]=

Use the qualified dot notation for strings, or use symbols in proper contexts:

In[218]:=
p["mod1.funcion"[]]
p["mod2.funcion"[]]
In[219]:=
p[mod1`funcion[]]
p[mod2`funcion[]]

Alternatively, create another PythonObject for one of the modules:

In[220]:=
p1 = ResourceFunction["PythonObject"][session, "fnames.mod1"]
Out[220]=

In that context, the function name is unique and the function can be called just by its name:

In[221]:=
p1["funcion"[]]
p1[funcion[]]
In[222]:=
DeleteObject[session]

Possible Issues (5) 

PythonObject cannot be created by Python statements that do not return an object:

In[223]:=
session = StartExternalSession["Python"];
In[224]:=
pfun = "def func(): print(123)";
In[225]:=
ResourceFunction["PythonObject"][session, pfun]
Out[225]=

Define the function using ExternalEvaluate and give the function name to PythonObject:

In[226]:=
ExternalEvaluate[session, pfun];
In[227]:=
p = ResourceFunction["PythonObject"][session, "func"]
Out[227]=

Use the object:

In[228]:=
p[All][]
In[229]:=
DeleteObject[p["Session"]]

If "cmd" in PythonObject[session,"cmd"] contains multiple statements, all the statements are executed, but the PythonObject is currently created from the first statement:

In[230]:=
session = StartExternalSession["Python"];
In[231]:=
p = ResourceFunction["PythonObject"][session, "1+1; print('proceeding...'); object()", True]
Out[231]=
In[232]:=
ExternalEvaluate[session, p[]]
Out[232]=

We plan to improve on that in a future update; in the meantime, use separate statements for each object you want to create:

In[233]:=
ResourceFunction["PythonObject", ResourceVersion->"1.0.0"][session, {"1+1; print('proceeding...')", "object()"}]
Out[233]=
In[234]:=
DeleteObject[session]

If a Python object cannot be created, PythonObject might return a syntax error:

In[235]:=
session = StartExternalSession["Python"];
In[236]:=
ResourceFunction["PythonObject"][session, "import uuid"]
Out[236]=

Use ExternalEvaluate if you want to import the package manually:

In[237]:=
ExternalEvaluate[session, "import uuid"]
In[238]:=
DeleteObject[session]

Signatures of some callable objects can contain Missing elements:

In[239]:=
session = StartExternalSession["Python"];
In[240]:=
p = ResourceFunction["PythonObject"][session, "math"]
Out[240]=
In[241]:=
p["Information", "log"]
Out[241]=

You can still use such objects, but need to obtain their signatures manually:

In[242]:=
p["RawInformation", "log"]
In[243]:=
p["log"[10.]]
Out[243]=

Also, for such objects, you cannot use standard Wolfram Language options and should rather supply optional arguments:

In[244]:=
p["log"[10., "base" -> 10.]]
Out[244]=
In[245]:=
p["log"[10., 10.]]
Out[245]=

Alternatively, you can remove missing elements using the PythonObject API function "Signatures":

In[246]:=
ResourceFunction["PythonObject"]["API", "Signatures"[
  "math", <|"log" -> <|"Arguments" -> {"x"}, "Options" -> {"base" -> N[E]}|>|>]]
In[247]:=
p = ResourceFunction["PythonObject"][session, "math"]
Out[247]=

Now the "log" function works the standard way:

In[248]:=
p["log"[10., "base" -> 10.]]
Out[248]=
In[249]:=
DeleteObject[session]

PythonObject is a work in progress. Not all packages are currently supported:

In[250]:=
ResourceFunction["PythonObject"][session, "wolframclient"]
Out[250]=

Version History

  • 1.4.0 – 07 February 2024
  • 1.3.0 – 29 November 2022
  • 1.2.0 – 23 June 2022
  • 1.1.0 – 08 March 2022
  • 1.0.0 – 23 November 2021

Related Resources

Author Notes

The current treatment of multiple statements in "cmd" is suboptimal. PythonObject should really be created from the last statement, not the first. This is a TODO item, see "Possible Issues".
PythonObject[session,"TemporaryPackage"[assoc]] created specifically to simplify examples in this documentation and is not recommended for any other use.

Developer Notes

Besides giving access to "raw" Python code, PythonObject is also the first step towards a full-fledged framework for creating Python package wrappers and developing Wolfram Language applications backed by Python code. To this end, it is possible to configure the wrappers to adhere closely to the Wolfram Language conventions. The framework also provides utility functions that make developing a Python package wrapper simpler.

Configuration

Configurations can be defined interactively in the current Wolfram Language session or saved in a repository function and loaded on demand. In the future, saved configurations can be made discoverable automatically.

Configurations are defined through one or more API functions including:

“NumberOfOptionalArguments” promote options to optional arguments
"Signatures" set signatures of callable objects
"PostProcess" apply post-processing to Python results
"PreProcess" preprocess arguments before sending to Python
"Rename" use Wolfram Language naming conventions in Python objects

Currently, "Configuration"Automatic uses the custom configuration, if defined, or the first non-custom configuration applied in the current Wolfram Language session.

Note : Although configurations can be loaded before or after a PythonObject is created, they do not apply retroactively. To see the expected behavior, in most cases you have to re-create the object (that is, create a new PythonObject instance) after loading your configuration.

NumberOfOptionalArguments

In general, the convention to represent Python’s optional arguments as Wolfram Language options works well, but occasionally the design can be improved by keeping optional arguments at the same level with required positional arguments.

PythonObject["API","NumberOfOptionalArguments"["package",<|target1n1,|>]]

promotes the first n Python’s optional arguments in targeti in the specified package to Wolfram Language arguments.

"package" must be the package name or the name of the top-level module. targeti can be:

"func" function
{"class","method"} method of the class

A sample package that implements an "add_two" function":

In[1]:=
package = "TemporaryPackage"[<|"Name" -> "addtwo",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "def add_two(x,y=0):
	return x+y
"|>];

Define a custom configuration in which the add_two function takes an optional argument, rather than an option:

In[2]:=
PythonObject["API", "NumberOfOptionalArguments"["addtwo", <|"add_two" -> 1|>]]

Create a Python object for the package with a custom configuration:

In[3]:=
session = StartExternalSession["Python"]
In[4]:=
p = PythonObject[session, package, "Configuration" -> "Custom"]

The signature of the add_two function:

In[5]:=
p["Information", "add_two"]

Call the function with one and two arguments:

In[6]:=
p["add_two"[1]]
In[7]:=
p["add_two"[2, 3]]

For comparison, with no configuration, the second summand needs to be supplied as an option:

In[8]:=
p1 = PythonObject[session, package, "Configuration" -> None]
In[9]:=
p1["add_two"[2, "y" -> 3]]

Set the number of optional arguments for several functions and methods in the package:

In[10]:=
package = "TemporaryPackage"[<|"Name" -> "opargs",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "class MyClass:
	def __init__(self,x,y=0):
		print('init done')
	def __call__(self,t,u=0,v=1):
		return 'call done'
	def add_three(self,x,y,z=0):
		return 'MyClass.add_three called'
def add_many(x,y=0,t=0,u=0):
	return 'mod.add_many called'
"|>];
In[11]:=
PythonObject["API", "NumberOfOptionalArguments"[
  "opargs", <|
   "add_many" -> 2, {"MyClass", "__init__"} -> 1, {"MyClass", "__call__"} -> 1, {"MyClass", "add_three"} -> 1|>]]
In[12]:=
p = PythonObject[session, package, "Configuration" -> "Custom"]

The number of optional arguments for the class constructor is set using the "__init__" method above:

In[13]:=
p["MyClass"[1]]
In[14]:=
p1 = p["MyClass"[1, 2]]

Call the class instance:

In[15]:=
p1[All][1]
In[16]:=
p1[All][1, 2]

Call a method:

In[17]:=
p1["add_three"[1, 2]]
In[18]:=
p1["add_three"[1, 2, 3]]

Call the add_many function in the module mod:

In[19]:=
p["add_many"[1, 2]]
In[20]:=
p["add_many"[1, 2, 3]]

For this function, one optional argument is left as an option:

In[21]:=
p["Information", "add_many"]
In[22]:=
p["add_many"[1, 2, 3, "u" -> 4]]

Signatures

PythonObject["API","Signatures"["package",<|target1assoc1,|>]]

adds missing elements to signatures of the callable objects in the specified package.

Specifications of targeti can be the same as for "NumberOfOptionalArguments".

Possible elements of associ include:

"Arguments" list of arguements
"Options" list of options
"IsMethod" whether the callable is a method

Signatures of some callable objects in this package contain missing elements:

In[23]:=
p = PythonObject[session, "math"]
In[24]:=
PythonObject["API", "MissingSignatures"[p]]
In[25]:=
p["Information", "log"]

Supply the signature of the function "log":

In[26]:=
PythonObject["API", "Signatures"[
  "math", <|"math.log" -> <|"Arguments" -> {"x"}, "Options" -> {"base" -> N[E]}|>|>]]

Now the function can be called as usual:

In[27]:=
p = PythonObject[session, "math"]
In[28]:=
p["Information", "log"]
In[29]:=
p["log"[10, "base" -> 10]]

PostProcess

PythonObject["API","PostProcess"["package",func]]

applies the specified function to results received from the package.

The post-processor function func[e,{p,f},{farg1,farg2,}]receives the result of the computation e; the caller object p, the name of the function or method f, and the list of arguments sent to f, including the arguments with default values.

Define a post-processor function for the module "uuid", which converts the instances of the UUID class to their string values:

In[30]:=
post[p_PythonObject, __] := ExternalEvaluate[p["Session"], StringTemplate["str(``)"][p[]]] /; p["FullName"] === "uuid.UUID"
post[res_, __] := res
In[31]:=
PythonObject["API", "PostProcess"["uuid", post]]

The Python object for the uuid.uuid4() function from the package:

In[32]:=
p = PythonObject["uuid.uuid4", "Configuration" -> "Custom"]

Call the function:

In[33]:=
p[All][]

For comparison, with no configuration, the function returns an instance of the UUID class:

In[34]:=
p1 = PythonObject["uuid.uuid4", "Configuration" -> None]
In[35]:=
p1[All][]

PreProcess

PythonObject["API","PreProcess"["package",func]]

applies the specified function to Wolfram Language values before sending them to the package.

The preprocessor function func[{p,f},{arg1,arg2,}]receives the caller object p, the name of the function or method f, and the list of arguments argi to be sent to f.

A sample package with a function that expects an immutable Python object, a tuple:

In[36]:=
package = "TemporaryPackage"[<|"Name" -> "preprocess",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "def do_tuple(t):
    if not isinstance(t, tuple):
        raise TypeError(str(t)+' is not a tuple')
    print('do_tuple called')
"|>];

Define a custom configuration in which a Wolfram Language list is automatically converted to a tuple before sending it to the do_tuple function:

In[37]:=
tuple[p_, list_List] := PythonObject[p["Session"], StringTemplate["tuple(``)"][list], True]
In[38]:=
pre[{p_, "preprocess.do_tuple"}, {list_List}] := {tuple[p, list]}
pre[_, args_] := args
In[39]:=
PythonObject["API", "PreProcess"["preprocess", pre]]

The Python object for the package:

In[40]:=
p = PythonObject[session, package, "Configuration" -> "Custom"]

Call the function:

In[41]:=
p["do_tuple"[{1, 2, 3}]]

For comparison, with no configuration, the function does not accept a mutable array object:

In[42]:=
p1 = PythonObject[session, package, "Configuration" -> None]
In[43]:=
p1["do_tuple"[{1, 2, 3}]]

Rename

PythonObject["API","Rename"["package"]]

uses heuristic to rename function names, class names, methods and options in the package.

PythonObject["API","Rename"["package",{rules1,}]]

sets up the specified name replacement rules.

Name replacements help to use Wolfram Language naming conventions.

rulesi are applied in the specified order and can be either in the form suitable for the use in StringReplace or Automatic.

Automatic removes underscores and converts names to CamelCase.

The replacement rules allow the use of curated names, but do not mandate them. The user can always use the original Python names.

A sample package with one function:

In[44]:=
package = "TemporaryPackage"[<|"Name" -> "onefunc",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "def my_function(x):
	return 'my_function called'
"|>];

Allow calling the function by its CamelCase name:

In[45]:=
PythonObject["API", "Rename"["onefunc"]]

Create a Python object and call the function by its new name:

In[46]:=
p = PythonObject[package]
In[47]:=
p["Information"]
In[48]:=
p["MyFunction"[1]]
In[49]:=
func = p["MyFunction"]
In[50]:=
func[All][1]

Set up custom naming rules for the package:

In[51]:=
package = "TemporaryPackage"[<|"Name" -> "cnames",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "mvar = 'module variable'
class MyClass:
	clvar = 'class variable'
	def __init__(self,x,asp_rat=0):
		self.instvar = x
		self.asp_rat = asp_rat
		print('init done')
	def my_plot(self,method='m'):
		return 'MyClass.my_plot called with method = '+str(method)
def get_approx(iter=1):
	return 'mod.get_approx called with iter = '+str(iter)
"|>];
In[52]:=
PythonObject["API", "Rename"["cnames", {{"asp_rat" -> "AspectRatio", "get_approx" -> "Approximation", "iter" -> "Iterations"}, Automatic}]]
In[53]:=
p = PythonObject[session, package]
In[54]:=
p["Information"]

Call the module function using the new names:

In[55]:=
p["Approximation"[Iterations -> 1]]
In[56]:=
p[Approximation[Iterations -> 1]]
In[57]:=
p["Approximation"["Iterations" -> 1]]
In[58]:=
p["NameReplacementRules"]

Configure

PythonObject["API","Configure"["package",{func1,func2,}]]

creates a configuration that includes all funci["package"].

Configuration functions funci are expected to be in the form "apif"[#,]&, where "apif" are one of the individual configuration functions.

In[59]:=
package = "TemporaryPackage"[<|"Name" -> "onefunction",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "def my_function(x):
	return 'my_function called'
"|>];

Allow calling my_function by a different name:

In[60]:=
PythonObject["API", "Configure"[
  "onefunction", { "Rename"[#, "my_function" -> "Func"] &}]]
In[61]:=
p = PythonObject[package]
In[62]:=
p["Information"]
In[63]:=
p["Func"[1]]

Miscellaneous Utilities

PythonObject["API","Reset"["package"]]

clears configuarations and cached values for the package.

PythonObject["API","Reset"[]]

clears configuarations and cached values for all packages defined in the current Wolfram Language session.

PythonObject["API","Reset"[All]] is the same as PythonObject["API","Reset"[]].


PythonObject["API","MissingSignatures"[p]]

gives a list of callables in the PythonObject p with missing or incomplete signatures.

License Information