Calling Python from MATLAB
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Written against MATLAB R2026b. MATLAB has been able to call Python since R2014b: put the prefix py. in front of any Python name and MATLAB loads a Python interpreter, imports the module, converts the arguments, calls the function and converts the result back. The feature has grown in steps that matter when you share code: pyenv and the PythonEnvironment object (R2019b) replaced pyversion, pyrun and pyrunfile arrived in R2021b, pyargs displays keyword arguments as name=value since R2022b, the Live Editor task Run Python Code and conversion of a Python dict with dictionary came in R2024a, isequal on Python objects and conversion of integers by Python type hints in R2024b, vectors without NumPy became memoryview objects in R2025a, and R2026a brought the External Languages panel and pystringarray. R2026b adds installation of Python from the Add-Ons panel and interruption of out-of-process Python with Ctrl+C. The release numbers come from the "Introduced in", "Since" and version-history notes of the R2026b pages listed under References, which were opened in a browser on 2026-10-23.
GNU Octave 11.3.0 is not MATLAB here, and it cannot run any of the MATLAB-to-Python code on this page. Octave 11.3.0 has no py. namespace and no pyenv, pyrun, pyrunfile, pyargs, pystringarray or terminate (an Octave 11.3.0 exist check returns 0 for each of them). So every MATLAB snippet that calls Python is labelled "not run here" and was checked line by line against the R2026b documentation; where the documentation shows an output, the page says so, and where it does not, no output is shown. What was run: the Python module of the heatflow example, in plain Python 3.9.6 with NumPy 2.0.2 outside MATLAB (labelled "Python output"; this interpreter is too old for MATLAB R2026b and was used only to check the Python logic), and the reference values of the same computation in Octave 11.3.0 (labelled "Octave 11.3.0 output"). The two agree. Passwords, tokens and real host names do not appear here; all file names are placeholders.
Ways to reach Python from MATLAB
MATLAB offers four routes into Python from the MATLAB side, and one route in the other direction.
| Route | What it does | Since |
|---|---|---|
|
Calls any function or class of the standard library or of an installed module; objects stay Python objects until you convert them. |
R2014b |
|
Executes Python statements in the interpreter, passes MATLAB data in as named variables and returns named Python variables. The variables persist between calls. |
R2021b |
|
Executes a Python script file the same way; the variables do not persist between calls. |
R2021b |
Run Python Code task |
A Live Editor task that generates |
R2024a |
MATLAB Engine API for Python |
The opposite direction: a Python program starts or connects to MATLAB and calls MATLAB functions. It is a separate package, |
R2014b |
The documentation sums the first two routes up this way: with the py. prefix MATLAB imports the entire module and can use all functions and classes of the Python code, while with pyrun and pyrunfile you must explicitly return Python objects to MATLAB using the outvars argument if you want to access Python data. Prefer the py. prefix for library calls and modules you write, and pyrun for a quick statement or a short script that needs MATLAB values as inputs.
In-process and out-of-process execution
Python can run in two places, and the choice is made with pyenv(ExecutionMode=...) before the interpreter is first used. The figure shows the difference.
| Aspect | InProcess (default) |
OutOfProcess |
|---|---|---|
Where the interpreter runs |
Inside the MATLAB process |
In a separate process named |
Performance |
Best; recommended for existing, stable Python code and performance-critical calls. |
Every call has overhead; the documentation says to call Python functions in-process unless you need isolation. |
Crashes in Python code |
Can bring MATLAB down |
Isolated from the MATLAB session |
Libraries that conflict with MATLAB’s own third-party libraries |
May fail |
The documented reason to choose this mode |
Interrupting a long call |
Not possible from MATLAB |
Ctrl+C terminates the process (R2026b); call |
Size of data passed per call |
Limited by memory |
At most 2 GB including supporting information |
Freeing Python objects |
Immediate |
|
|
Used to unload modules (see below) |
Terminates the |
Develop unstable Python code in OutOfProcess mode, so that a segmentation fault in an extension module or a runaway loop does not cost you your MATLAB session, and switch to InProcess for production use. The same trade-off exists in other languages that host Python: see "The same idea in other languages" below.
Supported Python versions
MATLAB needs a CPython installation of a version it supports; Python 2 support ended with R2022b (R2023a is the first release that lists only Python 3). The table is copied in structure from the "Versions of Python Compatible with MATLAB Products by Release" page, which MathWorks keeps current; check it before upgrading either product.
| MATLAB release | Python versions for the MATLAB interface | Change |
|---|---|---|
R2023a |
3.8, 3.9, 3.10 |
Python 2.7 no longer supported |
R2023b, R2024a |
3.9, 3.10, 3.11 |
|
R2024b, R2025a, R2025b |
3.9, 3.10, 3.11, 3.12 |
|
R2026a |
3.9, 3.10, 3.11, 3.12, 3.13 |
3.13 added |
R2026b |
3.10, 3.11, 3.12, 3.13, 3.14 |
3.14 added, 3.9 dropped |
A correction to common summaries. It is easy to read that "Python 3.14 was added and 3.9 dropped in R2026a". The compatibility page and the release notes put both changes in R2026b; R2026a added 3.13. The R2026b release notes list "Python version 3.9 is no longer supported" among the changes marked as errors, so code that pins pyenv(Version="3.9") must move to a newer interpreter when you upgrade MATLAB.
Platform requirements from the installation page:
-
Windows: the 64-bit Python; Python installed from the Microsoft Store is not supported.
-
Linux and macOS: if you build Python from source, configure it with
--enable-shared. -
macOS: MATLAB and Python must have the same architecture (Apple silicon or Intel).
Installing Python and choosing an environment
There are three ways to get a supported Python: use one you already have, install it from python.org, or (since R2026b) install the "MATLAB Support for Python" add-on from the Add-Ons panel, which MathWorks provides for a version compatible with the release (3.13 for R2026b). Then you tell MATLAB which one to use, in one of three ways:
| Way | Notes |
|---|---|
External Languages panel (R2026a) |
The simplest way; right-click an environment and choose "Set as Selected Environment". See the next section. |
|
|
|
Meant for cases where |
Prefer a virtual environment (an isolated folder of Python packages for one project) over the system interpreter, and point pyenv at the python executable inside it. The Python side of virtual environments, pip and requirements.txt is described in Virtual Environments and Packaging; the documentation also offers the MATLAB route through the External Languages panel, which uses the Python venv module and pip.
pyenv and the PythonEnvironment object
pyenv has three call forms:
pyenv % display the current environment
pyenv(Name=Value) % change Version or ExecutionMode
pe = pyenv(___) % also return a PythonEnvironment object
The two name-value arguments are Version (a version number such as 3.13 on Windows, or the full path of an executable on any platform) and ExecutionMode (InProcess, the default, or OutOfProcess). The returned PythonEnvironment object has read-only properties, and the documentation shows this display for a freshly selected, not yet loaded interpreter:
PythonEnvironment with properties:
Version: "3.13"
Executable: "C:\Users\username\AppData\Local\Programs\Python\Python313\python.exe"
Library: "C:\Users\username\AppData\Local\Programs\Python\Python313\python313.dll"
Home: "C:\Users\username\AppData\Local\Programs\Python\Python313"
Status: NotLoaded
ExecutionMode: InProcess
(That text is the documented example, not output captured here.) Status is NotLoaded until the first Python call, then Loaded; after terminate it is Terminated. In out-of-process mode two more properties appear, ProcessID and ProcessName (MATLABPyHost). An empty Version means no usable Python was found.
The rules for changing the environment follow from the fact that an interpreter cannot be swapped under running code:
-
Before the first Python call (status
NotLoaded): callpyenvfreely. -
In-process interpreter already loaded: restart MATLAB, then call
pyenvwith the new settings. -
Out-of-process interpreter already loaded: call
terminate(pyenv)first (it throws an error for an in-process interpreter), thenpyenv(Version=...); MATLAB removes standalone Python references from the workspace, and references held in variables become deleted handles.
% not run here: needs MATLAB with at least two installed Python versions
pe = pyenv;
if pe.Status == "Loaded" && pe.ExecutionMode == "OutOfProcess"
terminate(pe) % shut the MATLABPyHost process down
end
pyenv(Version="3.13",ExecutionMode="OutOfProcess");
py.list; % a Python call loads the interpreter
disp(pyenv.Status)
Source: MATLAB documentation: pyenv
The older function pyversion still exists but is documented as "not recommended"; use pyenv.
The External Languages panel (R2026a)
Since R2026a, the Python view of the External Languages panel does what scripts of pyenv calls did before. Open it with the "Open more panels" button of any sidebar, choose External Languages, and pick Python at the upper left. In the panel you can:
-
switch the selected environment (right-click, "Set as Selected Environment"), refreshing the list if you installed Python while MATLAB was open;
-
add an existing base or virtual environment, or create a virtual environment (a base environment, a name, a location, libraries to install; MATLAB uses
venv); -
manage libraries of an environment with
pip(add by name from PyPI or from arequirements.txt, update, uninstall), and "Generate Requirements" to replicate an environment; -
switch between InProcess and OutOfProcess with the Settings button, which may terminate the environment or restart MATLAB depending on the direction of the change;
-
remove an environment (recoverable) or delete a virtual environment (permanent; base environments cannot be deleted from the panel).
The documentation warns that installing or removing libraries with the panel runs pip against the environment itself, so it affects every other program on the machine that uses it, not only MATLAB. Scripts and CI jobs cannot click a panel; they use pyenv and the paths of a project-owned virtual environment instead.
Calling Python functions: the py. prefix
Anything in the Python standard library or in an installed module is reachable by prefixing its name with py.. You do not import modules first.
% Commands copied from the documentation; the output is not shown here
py.list({'This','is a','list'}) % built-in function list
py.textwrap.wrap('This is a string') % function wrap of module textwrap
py.math.radians(90) % returns a Python float, shown as a double
Things that are different from both MATLAB and Python:
-
Objects are displayed as Python objects. A call that returns a list shows
Python list with values:orPython list with no properties.and a line with the Python representation, followed by a hint to convert withstring,double,datetimeorcell. A number returned aspy.intis displayed with properties (denominator,imag,numerator,real), and its value is on the last line. -
Method calls. Use dot notation on an object,
res.append("Name4"), or function notation with the object first,append(res,"Name4"). If a Python class defines a method whose name is also a MATLAB converter (char,string,double), MATLAB calls the Python method; convert explicitly withchar(py.str(obj)). -
Help.
py.help("list.append")shows the Python help text. Tab completion afterpy.does not list Python names, andpy.helpwith no argument is not supported. -
import. In MATLAB,import py.textwrap.wrapshortens a later call towrap('another string'), but only when the name does not clash with a MATLAB function. The documentation cautions againstimport py.*and plainimport pythonmodule: when a MATLAB and a Python function share a name, MATLAB then calls the Python one. Undo a mistaken import withclear import. -
Function handles. A handle
@py.module.functioncan be passed to Python functions, for example topy.map. -
Indexing. Python containers can be indexed from MATLAB with one-based indices: round parentheses return a sub-sequence (still a Python list), curly braces return the element, and for a
dictcurly braces take the key,patient{"billing"}. Python itself is zero-based; the documentation’s own phrase is that you access the elements withx(1)throughx(len). Logical indexing, square brackets and arbitrary index arrays are not supported, andnumelreturns 1. -
None. A MATLAB<missing>string passed to Python becomesNone.
Where the module is found: the Python search path
A module of your own must be on the Python search path of the interpreter MATLAB loaded. The documented idiom adds a folder at the front of py.sys.path once per session:
% not run here; from the documentation's mymod example
if count(py.sys.path,pwd) == 0
insert(py.sys.path,int32(0),pwd);
end
The index passed to insert is an explicit int32 because Python needs an integer. The path belongs to the interpreter loaded in the current MATLAB session; changes do not reach other Python processes. A module whose name equals a standard-library or installed module (a file named random.py, for example) shadows it, so MATLAB may load the wrong one: give your modules distinctive names such as heatflow_field. If the import fails, py.importlib.import_module('mymod') shows the real Python error.
Keyword arguments
Python separates positional arguments from keyword arguments (print(*objects, sep=' ', end='\n')). From MATLAB, positional arguments are passed in order, and keyword arguments are passed either with MATLAB’s name=value syntax or with pyargs, always after the positional ones:
% Commands copied from the documentation; not run here
py.print("a","b",sep=",") % name=value form
py.print("a","b",pyargs("sep",",")) % pyargs form
D = py.dict(Joe=100,Jack=101); % **kwargs
py.complex(real=1,imag=2) % keyword arguments real and imag
The documentation’s rules: do not mix pyargs and name=value in one call, and name,value pairs (without pyargs) are not supported for Python keyword arguments. A Python signature with *args means "any number of positional arguments", which in MATLAB you pass with a cell-array expansion, py.print(words{:}).
Source: MATLAB documentation: pyargs
Type conversion
MATLAB converts arguments automatically when it can and returns Python objects when it cannot. The rule is simple to state and has important exceptions: arguments are converted on the way in, results are mostly left as Python objects and you convert them on the way out. The tables below follow the R2026b page "Pass Data Between MATLAB and Python from MATLAB".
MATLAB to Python: scalars
| MATLAB scalar | Python value |
|---|---|
real |
|
complex |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function handle |
|
MATLAB to Python: arrays
With NumPy installed in the selected environment, a MATLAB array becomes a NumPy array:
| MATLAB array | Python value |
|---|---|
|
|
|
|
|
|
complex |
|
|
|
|
|
cell vector |
|
1-by-N or N-by-1 |
|
|
|
|
no automatic conversion |
Without NumPy, numeric vectors and matrices become Python memoryview objects that point into the MATLAB array without copying (since R2025a). Before R2025a, a vector became a Python array.array. You can build an array.array from the view yourself: py.array.array('d',[1,2,3]).
Unsupported in either mode: sparse arrays, nonscalar struct arrays, categorical, containers.Map, and MATLAB objects. Convert them first: full(S) for a sparse matrix (for the sparse heatflow system see Sparse Matrices and Graphs), struct2table or a cell array of scalar structures for a struct array, string(c) for a categorical.
Explicit conversion on the MATLAB side. For the multidimensional string array that has no automatic conversion, R2026a added pystringarray:
npa = pystringarray(mla)
It converts a MATLAB string array of any size into a NumPy StringDType array of the same dimensions, and needs NumPy 2.0 or later. The documentation’s example converts the 2-by-3 array ["sandwich" "salad" missing; "samosa" "soup" "sushi"] and shows the result [['sandwich' 'salad' None] ['samosa' 'soup' 'sushi']], the missing element becoming None.
Python to MATLAB
When a Python function returns a scalar float, complex, bool, datetime or str (including a str inside a dict or a pandas.DataFrame), MATLAB converts it to double, complex double, logical, datetime or string. Everything else comes back as a Python object, which you convert with the MATLAB function named in the table:
| Python object returned | Convert with |
|---|---|
|
|
object with a |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Two details save time. A Python list of lists, or a tuple of arrays, cannot be converted with one call; the documentation has a troubleshooting page, "Error Converting Elements of list or tuple", which says to convert each element with string, double or the other converter. And when a Python function expects a specific NumPy array type, convert the MATLAB array in MATLAB before the call, py.pyfunc(pyType(a)).
A Python dictionary converts to a MATLAB dictionary (see Dictionaries and containers.Map) with keys and values converted to the equivalent MATLAB types when possible; if the converted types are not all the same, they are wrapped in cell arrays. To a struct, it converts only when the keys are valid MATLAB identifiers. In the other direction a MATLAB dictionary can be passed to a Python function directly or converted with py.dict:
% Adapted from the documentation's dictionary example; the output of the display is not shown here
dm = dictionary(["Robert" "Mary" "Jack"],[357 229 391]);
n = py.len(dm); % a MATLAB dictionary passed straight to Python
dp = py.dict(dm); % explicit conversion
dm2 = dictionary(dp); % back to a MATLAB dictionary (R2024a)
st = struct(dp); % or to a structure, when the keys are valid names
The documentation shows py.dict(dm) displayed as {'Robert': 357.0, 'Mary': 229.0, 'Jack': 391.0}, so numbers cross the boundary as float.
Dates and durations convert both ways (datetime to datetime.datetime, arrays to numpy.datetime64; see Dates and Times). Tables convert to and from pandas DataFrames with table(pd), timetable(pd) and py.pandas.DataFrame(...); for the MATLAB side of tables see Tables and Timetables. If the index of a DataFrame has time values, they become the row times of the timetable.
Numbers: why py.int appears
A MATLAB number is a double; a Python number without a fractional part is an int, and many Python functions refuse a float where they want an integer (the date constructor, range, an array shape). MATLAB handles this in two ways. Since R2024b it reads the Python type hints of the called function and converts a double to int when the hint asks for it and nothing is lost: py.datetime.date(2014,12,31) works with plain MATLAB numbers. Not every function carries a hint, and the documentation lists the limits: the typing module is not supported, hints from stub modules are not used, and pyrun, pyrunfile and the Run Python Code task do not convert by type hints at all. In those cases pass int32(5) or py.int(5) explicitly. Going the other way, a Python int is shown as Python int with properties: with the value on the last line, and double(x) gives the MATLAB number.
Example: the heatflow temperature field in NumPy
The heatflow plate keeps its temperature in a matrix T of size ny-by-nx (see Matrices and Arrays). A Python module that analyses a field with NumPy shows the whole round trip: a double matrix goes in as a numpy.ndarray, a dict and arrays come out. The Python file is:
"""Field analysis helpers that MATLAB calls through the py. prefix (heatflow example)."""
import numpy as np
def summarize(T):
"""Return summary numbers of a 2-D temperature field as a dict."""
T = np.asarray(T, dtype=float)
k = int(np.argmax(T))
row, col = divmod(k, T.shape[1])
return {"mean": float(T.mean()), "maximum": float(T.max()),
"row": row, "col": col, "ny": T.shape[0], "nx": T.shape[1]}
def laplacian(T, dx):
"""Five-point Laplacian of the interior points; the result is two rows and columns smaller."""
T = np.asarray(T, dtype=float)
return (T[:-2, 1:-1] + T[2:, 1:-1] + T[1:-1, :-2] + T[1:-1, 2:]
- 4.0 * T[1:-1, 1:-1]) / dx**2
def explicit_step(T, alpha, dx, dt):
"""One explicit time step; raises ValueError beyond the stability limit dt <= dx^2/(4 alpha)."""
if dt > dx**2 / (4.0 * alpha):
raise ValueError(f"dt={dt:g} exceeds the stability limit {dx**2 / (4.0 * alpha):g}")
T = np.asarray(T, dtype=float)
out = T.copy()
out[1:-1, 1:-1] += alpha * dt * laplacian(T, dx)
return out
Save it as heatflow_field.py next to the MATLAB script. The MATLAB script builds the same small plate as the indexing page (a hot top edge and a warm row below it), makes the module importable, calls the three functions and converts the results:
% File: callNumpy.m (not run here: needs MATLAB with Python 3.10 or later and NumPy)
nx = 6; ny = 5; dx = 0.01; alpha = 1.2e-4;
T = zeros(ny,nx);
T(1,:) = 100; % hot top edge
T(2,2:end-1) = 20; % warm row below it
modFolder = fileparts(mfilename("fullpath"));
if count(py.sys.path,modFolder) == 0
insert(py.sys.path,int32(0),modFolder);
end
s = py.heatflow_field.summarize(T); % T arrives as a float64 ndarray of shape (5,6)
meanT = double(s{"mean"});
peak = [double(s{"row"}) double(s{"col"})] + 1; % Python is zero-based
fprintf("mean %.4f, peak at (%d,%d)\n",meanT,peak(1),peak(2));
L = double(py.heatflow_field.laplacian(T,dx)); % ndarray back to a double matrix
disp(size(L)) % interior only: ny-2 by nx-2
dt = dx^2/(4*alpha)/2; % half of the stability limit
Tn = double(py.heatflow_field.explicit_step(T,alpha,dx,dt));
disp(Tn(2,:))
The MATLAB script was not run here. The Python module was run on the same field outside MATLAB, and the reference computation was run in Octave 11.3.0 with the shifted-block form of the stencil. Python output (Python 3.9.6, NumPy 2.0.2; the dict is the value summarize returns, with the zero-based row and col):
float64 (5, 6)
{'mean': 22.666666666666668, 'maximum': 100.0, 'row': 0, 'col': 0, 'ny': 5, 'nx': 6}
(3, 4) [400000. 600000. 600000. 400000.]
0.104167
[ 0. 25. 27.5 27.5 25. 0. ]
Octave 11.3.0 output of the reference computation (MATLAB uses the same formulas and prints the same numbers, possibly with a different layout):
5 6
mean = 22.6667
max = 100.0 at row 1, column 1 (1-based)
3 4
400000 600000 600000 400000
dt = 0.104167
0 25.0000 27.5000 27.5000 25.0000 0
Read the two together. The field has shape (5, 6) in both worlds, so a MATLAB ny-by-nx matrix and its NumPy array have the same shape and no transposition is needed; the interior Laplacian has shape (3, 4); the mean and the new second row agree. What does differ is the index base: Python reports the peak at row 0, column 0, MATLAB at (1,1), hence the + 1. The other difference is tie-breaking by memory order: argmax on a NumPy array scans in row-major order and MATLAB’s max(T(:)) scans down the columns first, so for a field with several equal maxima the two return different positions. In this field both find the corner first; a field whose maximum value appears at row 1, column 3 and at row 2, column 1 would give (1,3) in NumPy and (2,1) in MATLAB.
The sequence of conversions is drawn below.
Copies, orientation and big fields
Treat a matrix passed to Python as a copy: the documentation describes the conversion to a NumPy array, not shared storage (only the memoryview case without NumPy is documented as not copying), so changing the array in Python is not a way to change the MATLAB variable. For a field with many millions of points the conversion in both directions can dominate the run time of a cheap computation, so move data across the boundary in as few large calls as possible rather than once per grid row, and keep the data in Python between steps (hold the returned ndarray as a py.numpy.ndarray and pass it to the next call, converting only for the final result). A MATLAB array is stored column by column and a default NumPy array row by row; the example above shows the same shape on both sides, and the conversion is meant to keep the element at each subscript, so you mostly need to think about the storage order only when you write C or Fortran code that touches the raw memory (see Matrices and Arrays). Check shape on the Python side when a result has an unexpected orientation: a MATLAB row vector may arrive as a one-dimensional or as a 1-by-N array, and the page of the documentation does not promise which, so write Python code that accepts both with np.asarray(x).reshape(-1).
When NumPy is absent from the selected environment, the same call receives a memoryview instead of an ndarray, and np.asarray in the module would then fail with an import error. Install NumPy in the environment (pip install numpy, or the Manage Python Libraries window of the External Languages panel) and keep a requirements.txt for the project.
Running Python statements: pyrun and pyrunfile
pyrun and pyrunfile execute Python source rather than calling a named function. The syntax lines are:
pyrun(code)
outvars = pyrun(code,outputs)
outvars = pyrun(code,outputs,pyName=pyValue)
pyrunfile(file)
pyrunfile(file input)
outvars = pyrunfile(file,outputs)
outvars = pyrunfile(file,outputs,pyName=pyValue)
code is a string scalar or character vector for one statement, or a string array, character array or cell array of character vectors for several lines (MATLAB inserts newline characters), or a Python code object made with the built-in compile. outputs names the Python variables to return, as a string array; with several outputs write [a,b] = pyrun(...). pyName=pyValue passes MATLAB data into the Python namespace under the given name. The key difference between the two: variables created by pyrun persist between calls in the Python namespace, whereas variables created by pyrunfile do not.
Using the temperature field T of the previous section:
% not run here
m = pyrun(["import numpy as np","m = float(np.mean(T))"],"m",T=T);
[hi,lo] = pyrun(["hi = float(T.max())","lo = float(T.min())"],["hi","lo"],T=T);
and with a script file fieldstats.py, which finds the peak value and the number of rows of the field T that MATLAB provides:
import numpy as np
A = np.asarray(T)
peak = float(A.max())
rows = A.shape[0]
% not run here
[peak,rows] = pyrunfile("fieldstats.py",["peak","rows"],T=T);
peak = double(peak); % convert explicitly if the display shows a Python object
rows = double(rows);
Executing the same statements in plain Python with the same array gave a mean of 22.666666666666668, a peak of 100.0 and 5 rows (Python output, Python 3.9.6, NumPy 2.0.2), which is what the MATLAB calls return before conversion. A pyrunfile script can also receive command-line arguments, pyrunfile("greeting.py 'hello world'"), which the script reads from sys.argv as strings.
Limitations the documentation states for both functions:
-
A Python class defined with
pyrunorpyrunfilecannot be modified after you return an instance to MATLAB; to change the class definition restart the interpreter,terminate(pyenv)followed bypyenv(ExecutionMode="OutOfProcess"), or restart MATLAB for in-process execution. -
Classes with local variables initialised from other local variables through methods are not supported; put them in a module and use the
py.prefix. -
Conversion by Python type hints is not applied (see above).
-
Variables stay in the Python namespace unless you return them with
outputs.
The Run Python Code task (Live Editor, Task, Run Python Code; since R2024a) builds these calls for you in a live script, with the same persistence rules as pyrun.
Source: MATLAB documentation: pyrun
Source: MATLAB documentation: pyrunfile
Errors: Python exceptions in MATLAB
When Python raises an exception, MATLAB catches it and throws a matlab.exception.PyException (a class available since R2014b), which derives from MException. Its message has the form Python Error: Python class: message, and the property ExceptionObject holds the result of Python’s sys.exc_info, a tuple of exception type, value and traceback. You catch it with the ordinary try/catch of Errors, Warnings and Exceptions:
% not run here: needs the heatflow_field module on the Python path
T = zeros(5,6);
try
py.heatflow_field.explicit_step(T,1.2e-4,0.01,1.0); % dt far above the stability limit
catch ME
if isa(ME,"matlab.exception.PyException")
disp(ME.message) % expected form: "Python Error: ValueError: ..."
info = ME.ExceptionObject; % tuple from sys.exc_info
else
rethrow(ME)
end
end
The Python module raises a ValueError for that call. Run in plain Python the message of that exception is (Python output, Python 3.9.6):
ValueError dt=1 exceeds the stability limit 0.208333
MATLAB prefixes it with Python Error: and the Python class, so the text you see in the Command Window is expected to read Python Error: ValueError: dt=1 exceeds the stability limit 0.208333 (the exact wording can vary by release; the page was not run in MATLAB). Test the class of the exception with isa(ME,"matlab.exception.PyException") rather than the identifier: the identifier is commonly reported as MATLAB:Python:PyException, but the pages used for this page do not state it, so confirm it with ME.identifier in your release if your code depends on it. The documentation draws a line between three kinds of failure:
| What you see | Where it comes from | What to do |
|---|---|---|
|
The Python code raised an exception. |
Read the Python documentation of that function and reproduce the call in a Python interpreter. |
|
MATLAB could not load Python or find the module or function: no supported Python, a 32-bit Python on 64-bit Windows, Python in a nonstandard location, the module not on |
Check |
A conversion error, or a message about a matrix or numeric argument |
MATLAB could not convert a MATLAB value to the type the function expects, or an element of a returned list or tuple. |
Convert in MATLAB to the expected Python type first, |
On the Python side, the equivalent exceptions are described in Python Exceptions. The troubleshooting page of the documentation also links a MathWorks Support answer on debugging Python code called from MATLAB with Visual Studio Code, which this page does not reproduce.
Reloading a modified Python module
While you develop a Python module you change it and expect the next call to use the new code. MATLAB imports a module once per session, so by default the second py.newmod.myfunc still runs the first version. How to reload depends on the execution mode.
In-process interpreter, calls through the py. prefix. The documented recipe uses clear classes, then imports the module with importlib and reloads it:
% Steps copied from the documentation; the module newmod.py defines myfunc() returning 'version 1'
py.newmod.myfunc % shows version 1
% ... edit newmod.py so that myfunc returns 'version 2', and save the file ...
clear classes % call it from the base workspace
mod = py.importlib.import_module('newmod');
py.importlib.reload(mod);
py.newmod.myfunc % now shows version 2
The documentation adds that you should call clear classes from the command prompt (the base workspace), because inside a function workspace it may not clear all variables. It applies only to code called with the py. prefix. Code run with pyrun needs an out-of-process interpreter to be reloaded.
Out-of-process interpreter. Terminate the process and start a new one, possibly with different version settings, without restarting MATLAB:
% Steps adapted from the documentation's example
terminate(pyenv) % shut the MATLABPyHost process down
pyenv(Version="3.13"); % optional: change version or mode
py.list; % any Python call starts a new interpreter
In out-of-process mode clear classes is a heavier tool: it terminates the MATLABPyHost process and clears every Python object. For an in-process interpreter there is no way to unload the interpreter itself; restart MATLAB. The pattern is the same as in any language with a long-lived interpreter, and for heavy iteration many developers run the Python tests in a plain Python process (see Testing) and use MATLAB only for the integration.
Limitations
The documentation lists what the Python interface does not support. The ones most likely to matter:
-
closing the Python interpreter while in-process MATLAB runs; saving Python objects to a MAT-file; passing Python objects to workers of a parallel pool (see Background and Asynchronous Execution);
-
subclassing a Python class from MATLAB, nesting Python classes, customising dynamic attribute access, accessing static properties, and generating Python classes dynamically;
-
names that start with an underscore (use
py.getattrandpy.setattrinstead), and attaching new attributes to an object (usepy.setattr); -
Python modules generated by MATLAB Compiler SDK, and Python code that uses Cocoa (AppKit) on macOS;
-
starting MATLAB from Python modules such as
multiprocessing, and reading command-line arguments with modules such astkinter; -
sparse arrays, nonscalar structure arrays,
categorical,containers.Mapand MATLAB objects as arguments; -
more than 2 GB per call in out-of-process mode.
Common errors and pitfalls
| Symptom | Cause | Fix |
|---|---|---|
|
The module is not on |
Insert the folder with |
A Python function complains that it expected an |
MATLAB numbers are |
Pass |
|
The wrong number of positional arguments; a cell array was not expanded. |
Pass cell contents with |
A result prints as |
Results are Python objects until converted. |
Convert with |
An off-by-one result |
Python indices start at 0, MATLAB indices at 1. |
Add 1 to indices from Python, subtract 1 on the way in. |
Code works once, then the second edit has no effect |
The module is cached by the interpreter. |
Reload ( |
|
The interpreter is already loaded in this session. |
Restart MATLAB for in-process, or |
A NumPy |
Multidimensional |
Use |
A sparse matrix cannot be passed |
Sparse arrays are not supported. |
Convert with |
MATLAB crashes in a native extension |
The interpreter lives in the MATLAB process. |
Develop and debug in |
Python prints to the Command Window at different times |
Output of |
Return text as a value when you need it in a variable. |
Different results across machines |
Different Python or package versions in the selected environment. |
Pin the interpreter and a |
Testing and automation
Treat the Python module as a unit that has its own tests (pytest, see Testing) and add a small MATLAB test that checks the integration: the conversion of a known matrix and the error class for an invalid call. In a class-based test, testCase.verifyEqual(double(py.heatflow_field.laplacian(T,dx)),expected,AbsTol=1e-9) compares the converted result, and testCase.verifyError(@() py.heatflow_field.explicit_step(T,a,dx,1),"MATLAB:Python:PyException") is the form to use only after you have confirmed the identifier in your release; otherwise catch the exception and test its class. See Unit Testing with matlab.unittest. On a continuous-integration machine nobody can open the External Languages panel, so call pyenv(Version=...) at the top of the job or set PYTHONHOME, and install the dependencies from requirements.txt first (see Continuous Integration). None of this was run here.
The same idea in other languages
Hosting an interpreter in another program, converting values at the boundary, deciding whether the guest may crash the host, and handling exceptions across the boundary are problems every language faces. The Rust page Python Interop with PyO3 shows the mirror image of this page: instead of MATLAB calling Python, Rust code is called from Python (a native extension module built with maturin), or a Rust program embeds a Python interpreter. Its sections on mapping types and errors, the interpreter lock, and testing with pytest correspond to the sections here on type conversion, in-process and out-of-process execution, and exceptions. MATLAB’s in-process mode is the embedding case, and its out-of-process mode resembles running Python as a separate service. For the Python language itself, which the examples above assume you know, start at the Python section; the modules and packaging pages (Modules and Packages) explain the import path, and Numbers explains why Python distinguishes int and float. The C# page Native Interop covers the same boundary problem for managed code calling native code.
Where older books differ
The two books consulted for this section predate the feature or ignore it: the primer was written for R2010a, long before py. existed (R2014b), and the 2021 mathematics volume does not cover external language interfaces. Web articles and blog posts of 2014 to 2018 describe a Python 2.7 interface, the pyversion function to select an interpreter (replaced by pyenv in R2019b, and now documented as not recommended), the vector-as-array.array conversion (the behaviour before R2025a), py.list conversions by hand where R2024a added dictionary(pydict) and table(pandas), and the need to restart MATLAB whenever a module changed (still true for in-process execution, but out-of-process mode and importlib.reload are the documented ways around it). Python 2 is no longer supported from R2023a. Tutorials that call urlread or write temporary CSV files to exchange data with Python describe workarounds from before the direct interface; the interface now exchanges arrays directly, and for text exchange formats see JSON, XML and Web Access. Finally, summaries that put Python 3.14 and the end of Python 3.9 in R2026a are off by one release: they are R2026b changes.