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

py. prefix

Calls any function or class of the standard library or of an installed module; objects stay Python objects until you convert them.

R2014b

pyrun

Executes Python statements in the interpreter, passes MATLAB data in as named variables and returns named Python variables. The variables persist between calls.

R2021b

pyrunfile

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 pyrun or pyrunfile code (see Scripts and Code Sections for live scripts).

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, matlabengine, and is the subject of the page on the MATLAB Engine API for Python in this section.

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.

flowchart LR subgraph inproc["InProcess mode (default)"] direction TB A1["MATLAB code with py. calls"] --> A2["Python interpreter loaded inside the MATLAB process"] A2 --> A3["Shared memory space: lowest call overhead"] A3 --> A4["A crash in native Python code ends MATLAB"] end subgraph outproc["OutOfProcess mode"] direction TB B1["MATLAB code with py. calls"] --> B2["Separate MATLABPyHost process with its own interpreter"] B2 --> B3["Data crosses a process boundary: call overhead, 2 GB limit"] B3 --> B4["A crash or Ctrl+C ends only the Python process"] end
Aspect InProcess (default) OutOfProcess

Where the interpreter runs

Inside the MATLAB process

In a separate process named MATLABPyHost

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 terminate(pyenv) before using Python again

Size of data passed per call

Limited by memory

At most 2 GB including supporting information

Freeing Python objects

Immediate

clear of a Python object is asynchronous

clear classes

Used to unload modules (see below)

Terminates the MATLABPyHost process and clears all Python objects

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.

pyenv

pyenv(Version="3.13") on Windows for a registered installation, or the full path of the executable on any platform, for example pyenv(Version="/usr/bin/python3.13"). The setting persists across MATLAB sessions.

PYTHONHOME environment variable

Meant for cases where pyenv cannot be used, such as MATLAB apps. If you also set a version with pyenv, the two must agree. MATLAB reads it only while the status is NotLoaded. getenv("PYTHONHOME") shows it; setenv and unsetenv change it for the current session only.

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): call pyenv freely.

  • In-process interpreter already loaded: restart MATLAB, then call pyenv with the new settings.

  • Out-of-process interpreter already loaded: call terminate(pyenv) first (it throws an error for an in-process interpreter), then pyenv(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)

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 a requirements.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: or Python list with no properties. and a line with the Python representation, followed by a hint to convert with string, double, datetime or cell. A number returned as py.int is 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 with char(py.str(obj)).

  • Help. py.help("list.append") shows the Python help text. Tab completion after py. does not list Python names, and py.help with no argument is not supported.

  • import. In MATLAB, import py.textwrap.wrap shortens a later call to wrap('another string'), but only when the name does not clash with a MATLAB function. The documentation cautions against import py.* and plain import pythonmodule: when a MATLAB and a Python function share a name, MATLAB then calls the Python one. Undo a mistaken import with clear import.

  • Function handles. A handle @py.module.function can be passed to Python functions, for example to py.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 dict curly braces take the key, patient{"billing"}. Python itself is zero-based; the documentation’s own phrase is that you access the elements with x(1) through x(len). Logical indexing, square brackets and arbitrary index arrays are not supported, and numel returns 1.

  • None. A MATLAB <missing> string passed to Python becomes None.

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{:}).

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 double, single

float (or int, when a Python type hint of the called function asks for an integer and no precision is lost; since R2024b)

complex double, single

complex

int8 to uint64

int

NaN, Inf

float("nan"), float("inf")

string scalar, char vector

str

<missing> in a string

None

logical

bool

dictionary, struct

dict

table, timetable

pandas.DataFrame

datetime, duration

datetime.datetime, datetime.timedelta

function handle @py.module.function

module.function (to Python functions only)

MATLAB to Python: arrays

With NumPy installed in the selected environment, a MATLAB array becomes a NumPy array:

MATLAB array Python value

double (real)

numpy.array(dtype=np.float64)

single (real)

numpy.array(dtype=np.float32)

int8, uint8, int16, uint16, int32, uint32, int64, uint64 (real)

numpy.array of the same integer dtype

complex double, complex single

numpy.array(dtype=np.complex128), numpy.array(dtype=np.complex64)

logical

numpy.array(dtype=np.bool)

char vector

str

cell vector

tuple

1-by-N or N-by-1 string array

list (automatic since R2026a)

datetime array, duration array

numpy.datetime64 array, numpy.timedelta64 array

char matrix, cell matrix, string array with both sizes above 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

py.str

string or char

object with a __str__ method

char

py.int, py.float, py.long

double, single, int8 to uint64

py.bool

logical, double, single, integer types

py.bytes

uint8

py.array.array, py.memoryview

double, single, integer types

py.numpy.ndarray

double, single, integer types, or string

py.list, py.tuple

cell, then the proper function for each element; or double, single, integer types, logical or string when all elements fit

py.dict

dictionary (since R2024a) or struct

pandas.DataFrame

table or timetable (since R2024a)

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.

sequenceDiagram participant M as MATLAB participant P as Python (heatflow_field) M->>M: T = zeros(5,6) with hot top edge (double matrix) M->>P: py.heatflow_field.summarize(T) Note over M,P: double matrix becomes a float64 numpy.ndarray of the same shape P->>P: np.argmax, mean, max P-->>M: Python dict with float and int values M->>M: double(s{"mean"}), double(s{"row"}) + 1 M->>P: py.heatflow_field.laplacian(T,dx) P-->>M: py.numpy.ndarray of shape 3 by 4 M->>M: double(...) gives a MATLAB matrix

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 pyrun or pyrunfile cannot be modified after you return an instance to MATLAB; to change the class definition restart the interpreter, terminate(pyenv) followed by pyenv(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.

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

Python Error: TypeError: ...

The Python code raised an exception.

Read the Python documentation of that function and reproduce the call in a Python interpreter.

Unable to resolve the name py.myfunc

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 py.sys.path, a module name that shadows another module, or a syntax error in your module.

Check pyenv, py.sys.path, and py.importlib.import_module('mymod'), which shows the underlying Python error.

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, py.pyfunc(pyType(a)), or convert each element of a list separately.

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.getattr and py.setattr instead), and attaching new attributes to an object (use py.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 as tkinter;

  • sparse arrays, nonscalar structure arrays, categorical, containers.Map and MATLAB objects as arguments;

  • more than 2 GB per call in out-of-process mode.

Common errors and pitfalls

Symptom Cause Fix

Unable to resolve the name py.mymod.func

The module is not on py.sys.path, Python is not installed, or the module name clashes with another module.

Insert the folder with insert(py.sys.path,int32(0),folder); check pyenv and the module name.

A Python function complains that it expected an int

MATLAB numbers are double; the function has no type hint for conversion.

Pass int32(n) or py.int(n).

TypeError ... takes at most 1 argument (3 given)

The wrong number of positional arguments; a cell array was not expanded.

Pass cell contents with c{:}, or build the container with py.list.

A result prints as Python list with no properties.

Results are Python objects until converted.

Convert with string, double, cell, struct, dictionary or table.

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 (clear classes and py.importlib.reload) or terminate an out-of-process interpreter.

pyenv has no effect, or an error after pyenv(Version=...)

The interpreter is already loaded in this session.

Restart MATLAB for in-process, or terminate(pyenv) for out-of-process.

A NumPy object array or string array cannot be converted

Multidimensional string arrays, char matrices and cell matrices have no automatic conversion.

Use pystringarray (R2026a, NumPy 2.0 or later), cellstr, or pass a cell vector.

A sparse matrix cannot be passed

Sparse arrays are not supported.

Convert with full, or pass the [i,j,v] triplets.

MATLAB crashes in a native extension

The interpreter lives in the MATLAB process.

Develop and debug in OutOfProcess mode.

Python prints to the Command Window at different times

Output of print goes to Python’s standard output, which MATLAB forwards.

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 requirements.txt in a project-owned virtual environment, and record pyenv in the test log.

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.