Python interface: PyROOT
With PyROOT, ROOT’s Python-C++ bindings, you can use ROOT from Python. PyROOT is HEP’s entrance to all C++ from Python, for example, for frameworks and their steering code. The PyROOT bindings are automatic and dynamic: no pre-generation of Python wrappers is necessary.
With PyROOT you can access the full ROOT functionality from Python while benefiting from the performance of the ROOT C++ libraries.
PyROOT is compatible with both Python2 (>= 2.7) and Python3.
The usage of PyROOT requires working knowledge of Python.
For detailed information on Python, refer to the Python Language Reference.
Together with ROOT 6.22, a major revision of PyROOT has been released. The new PyROOT has extensive support for modern C++ (it operates on top of cppyy), is more pythonic and is able to interoperate with widely-used Python data-science libraries (for example, NumPy, pandas, Numba).
Therefore, we strongly recommend to use the new PyROOT.

Getting started
When ROOT is installed, you can use PyROOT both from the Python prompt and from a Python script. The entry point to PyROOT is the ROOT
module, which you must import first:
import ROOT
Then you can access the ROOT C++ classes, functions, etc. via the ROOT
module.
Example
This examples shows how you can create a histogram (an instance of the TH1F class) and randomly fill it with a gaussian distribution.
h = ROOT.TH1F("myHist", "myTitle", 64, -4, 4)
h.FillRandom("gaus")
Interactive graphics
Just like from C++, you can also create interactive ROOT graphics from Python with PyROOT.
Example
A one-dimensional function is created and drawn:
>>> import ROOT
>>> f = ROOT.TF1("f1", "sin(x)/x", 0., 10.)
>>> f.Draw()
When the above code is executed from the Python prompt, a new canvas opens and displays the drawn function:
Figure: Example of graphics generated with PyROOT.
Note
You can also write the above code in a script file and execute it with Python. In that case, the script runs to completion and the Python process is terminated, so the generated canvas disappears.
If you want to keep the Python process alive and thus inspect your canvas, execute the script with:
python -i script_name.py
User interface
Besides being the entry point to all ROOT functionality, the ROOT
module also provides an interface to configure PyROOT and to manipulate PyROOT objects at a lower level.
Configuration options
After importing the ROOT module, you can access the PyROOT configuration object as ROOT.PyConfig
. Such an object has a set of properties that you can modify to steer the behaviour of PyROOT. For the configuration to be taken into account, it needs to be applied right after the ROOT
module is imported.
Example
import ROOT
ROOT.PyConfig.DisableRootLogon = True
ROOT.PyConfig.IgnoreCommandLineOptions = False
The available configuration options are:
DisableRootLogon
(defaultFalse
): Just like in C++, PyROOT also supports rootlogon. When PyROOT starts, it looks for a file called.rootlogon.py
in the user’s home directory. If such file exists, PyROOT imports it. You can use it to make some settings every time PyROOT is launched, for example, defining some style for your plots:
# Content of .rootlogon.py
import ROOT
myStyle = ROOT.TStyle('MyStyle','My graphics style')
myStyle.SetCanvasColor(ROOT.kBlue) # My canvases are blue!
ROOT.gROOT.SetStyle('MyStyle')
If PyROOT cannot find .rootlogon.py
in the user’s home directory, it looks for the equivalent in C++ (.rootlogon.C
), first in ROOT’s etc directory, then in the user’s home directory and finally in the current working directory.
Note that it is also possible to use both the Python and the C++ rootlogons, since the latter can be loaded from the former, for example with ROOT.gROOT.LoadMacro('.rootlogon.C')
.
If you want to disable the rootlogon functionality, set PyConfig.DisableRootLogon
to True
.
-
IgnoreCommandLineOptions
(defaultTrue
): If a PyROOT script is run with some command line arguments, ROOT ignores them by default, so you can process them as you wish. However, by settingPyConfig.IgnoreCommandLineOptions
toFalse
, those arguments are forwarded to ROOT for parsing, for example, to enable the batch mode from the command line.
For a complete list of the arguments accepted by ROOT, → see Starting ROOT with command line options. -
ShutDown
(defaultTrue
): When the application is finished, more precisely during PyROOT’s cleanup phase, the ROOT C++ interpreter is shut down by default.
If PyROOT is executed as part of a longer-running application that needs the C++ interpreter, you can setPyConfig.ShutDown
toFalse
to prevent that shutdown. -
StartGUIThread
(defaultTrue
): Unless executed from IPython, a Jupyter notebook or in interactive mode, PyROOT starts a thread that periodically polls for ROOT events (for example GUI events) to process them. If a given PyROOT application does not need this event processing, you can prevent the creation of the thread by settingPyConfig.StartGUIThread
toFalse
.
Enabling batch mode
When running in batch mode, PyROOT does not display any graphics. You can activate the batch mode as follows:
-
Pass
-b
as a command line argument, for example,python -b my_pyroot_script.py
.
For this to work, you must setPyConfig.IgnoreCommandLineOptions
toFalse
inside the PyROOT script, see → Configuration options. -
Call
gROOT.SetBatch
in the PyROOT script, right after importingROOT
:
import ROOT
ROOT.gROOT.SetBatch(True)
Low-level manipulation of objects
When instantiating a C++ class from Python via PyROOT, both a C++ object and its Python proxy object are created. Such a Python object forwards any access to its internal C++ object, thus acting as a proxy. PyROOT provides functions to inspect or manipulate Python proxies and their C++ counterparts, based on the functionality provided by cppyy.
You can access these functions with the ROOT.NameOfFunction
pattern as follows:
-
AddressOf(obj)
: When applied to a Proxy objectobj
, it returns an indexable buffer of length 1, whose only element is the address of the C++ object proxied byobj
. The address of the buffer is the same as the address of the address of the C++ object. This function is kept for backwards compatibility with old PyROOT versions. -
addressof(obj, field, byref)
: Similarly toAddressOf
, you can use it to obtain the address of an internal C++ object from its Python proxy. However,addressof
returns that address as an integer, not as an indexable buffer. Furthermore,addressof
accepts two more parameters:field
andbyref
. You can usefield
to specify the name of a field in a struct, in order to get the address of that field. If you setbyref
toTrue
,addressof
returns the address of the address of the C++ object. This function is equivalent to cppyy’saddressof
.
Example
>>> import ROOT
>>> ROOT.gInterpreter.Declare("struct MyStruct { int foo; float bar; };")
True
>>> mys = ROOT.MyStruct()
>>> ROOT.addressof(mys) # Address of mys' C++ object
94352024283040L
>>> ROOT.addressof(mys, 'foo') # Address of "foo" field (same as address of object)
94352024283040L
>>> ROOT.addressof(mys, 'bar') # Address of "bar" field
94352024283044L
>>> ROOT.addressof(mys, byref = True) # Address of address of mys' C++ object
140376843834640L
Moreover, you can use addressof
in conjunction with TTree::Branch
from Python as shown in ttree_branch.py.
-
AsCObject
: Equivalent to cppyy’sas_cobject
. -
BindObject
: Equivalent to cppyy’sbind_object
. -
MakeNullPointer(class_proxy)
: Equivalent toBindObject(0, class_proxy)
, it returns a proxy object of the class represented byclass_proxy
that is bound to a C++ null pointer.
For example,MakeNullPointer(TTree)
returns aTTree
proxy object that internally points to null. This function is kept for backwards compatibility with old PyROOT versions. -
SetOwnership(obj, python_owns)
: A Python proxy can either own or not own its internal C++ object. If a Python proxy owns its C++ object and the proxy is being destroyed, the C++ object is deleted too. This ownership can be modified for a given Python proxy withSetOwnership
.
For example, by callingSetOwnership(obj, False)
, you make sure that the C++ object proxied byobj
is not deleted by PyROOT whenobj
is garbage collected. This is useful for example, if you know that the deletion will happen in C++ and you want to prevent a double delete.
Use this functionality with care not to produce any memory leaks.
Loading user libraries and Just-In-Time compilation (JITting)
With PyROOT you can use any C++ library from Python, not only the ROOT libraries. This is possible because of the automatic and dynamic bindings between Python and C++ that PyROOT provides. Without any prior generation of wrappers, at execution time, PyROOT can load C++ code and call into it.
This enables you to write high-performance C++, compile it and use it from Python. The following options are available, ordered by complexity and performance:
- Just-in-time compilation of small strings: Small functions and classes to be used from Python. Especially useful for testing and debugging.
- Just-in-time compilation of entire files: Small or medium-size C++ code in single files.
- Loading C++ libraries, JITting headers: Larger C++ code in libraries. Allows for optimizing code for high performance. Headers compiled just in time.
- Loading C++ libraries with dictionaries: Load large and very large, high-performance C++ projects, no just-in-time compilation.
Just-in-time compilation of small strings
ROOT has a C++ interpreter, which can process C++ code. Sometimes such a code is short, e.g. for the definition of a small function or a class or for rapid exploration or debugging. To do this, place the C++ code in a Python string, which is passed to the interpreter.
The code is just-in-time compiled (jitted) and it is immediately available for invocation, as shown in the following example. Here, the constructor of the C++ class A
and the function f
are called from Python after defining them via the interpreter.
Example
import ROOT
# Write some C++ code in a string
cpp_code = """
// Function definition
int f(int i) { return i*i; }
// Class definition
class A {
public:
A() { cout << "Hello PyROOT!" << endl; }
};
"""
# Inject the code in the ROOT interpreter
ROOT.gInterpreter.ProcessLine(cpp_code)
# We find all the C++ entities in Python, right away!
a = ROOT.A() # prints Hello PyROOT!
x = ROOT.f(3) # x = 9
Just-in-time compilation of entire files
If you want to use the C++ code in a header, you can use the interpreter to include and compile it on the fly.
Example
There is a header called my_header.h
with the following content:
int f(int i) { return i*i; }
class A {
public:
A() { cout << "Hello PyROOT!" << endl; }
};
In Python, you can access it like this:
# Make the header known to the interpreter
ROOT.gInterpreter.ProcessLine('#include "my_header.h"')
# We find all the C++ entities in Python, right away!
a = ROOT.A() # prints Hello PyROOT!
x = ROOT.f(3) # x = 9
Loading C++ libraries, JITting headers
You can dynamically load C++ libraries with PyROOT, and call the functions from the library. The advantage of this method is that
all code in the library needs to be compiled only once, possibly with optimizations (-O2
, -O3
). You can use it again from Python without any further JITting.
However, it is necessary to JIT the headers to make the code usable in Python.
Example
With the following header my_header.h
:
int f(int i);
class A {
public:
A();
};
and the source file:
#include "my_header.h"
int f(int i) { return i*i; }
A::A() { cout << "Hello PyROOT!" << endl; }
you can create the library my_cpp_library.so
. In Python, you can load and use it as follows:
# First include header, then load C++ library
ROOT.gInterpreter.ProcessLine('#include "my_header.h"')`
ROOT.gSystem.Load('./my_cpp_library.so')
# We find all the C++ entities in Python, right away!
a = ROOT.A() # prints Hello PyROOT!
x = ROOT.f(3) # x = 9
Loading C++ libraries with dictionaries
For larger analysis frameworks, one may not want to compile the headers each time the Python interpreter is started. One may also want to read or write custom C++/C objects in ROOT files, and use them with RDataFrame or similar tools.
A large analysis framework might further have multiple libraries. In these cases, you generate ROOT dictionaries, and add these to the libraries, which provides ROOT with the necessary information on how to generate Python bindings on the fly. This is what the large LHC experiments do to steer their analysis frameworks from Python.
- Create one or multiple C++ libraries.
For example create a library as a CMake project that uses ROOT, see → CMake details - [Optional] Add
ClassDef
macros for classes that should be read or written from or into files. -
Generate a dictionary of all classes that should receive I/O capabilities, i.e. that can be written into ROOT files, see → Generating dictionaries
Use aLinkDef.h
file to select which classes or functions ROOT should be included in the dictionary.The corresponding cmake instructions would look similar to this:
# Generate G__AnalysisLib.cxx with all the necessary class info: ROOT_GENERATE_DICTIONARY(G__AnalysisLib inc/classA.h inc/classB.h ... LINKDEF LinkDef.h) add_library(AnalysisLib SHARED src/classA.cxx src/classB.cxx ... G__AnalysisLib.cxx) # Important: Here, all dictionary info is directly compiled into the library. # Ensure dictionary is generated before compiling the library: add_dependencies(AnalysisLib G__AnalysisLib)
- Implement the C++ side, and compile the library using CMake.
-
On the Python side, load the libraries with high-performance C++ in one step:
import ROOT ROOT.gSystem.Load('./libAnalysisLib.so')
Note If either ROOT or the headers that were used to create the dictionaries were moved to a different location, and ROOT cannot find them (it returns an error in this case), the location of the headers has to be communicated to ROOT:
import ROOT ROOT.gInterpreter.AddIncludePath('path/to/headers/') ROOT.gSystem.Load('./libAnalysisLib.so')
The loading of C++ libraries can even be automated using the
__init__.py
of the Python package.
TPython: running Python from C++
With ROOT you can execute Python code from C++ via the TPython class.
The following example shows how you can use Exec
to execute a Python statement. Eval
is used to evaluate a Python expression and get its result back in C++ and Prompt
to start an interactive Python session.
Example
root [0] TPython::Exec( "print(1 + 1)" )
2
root [1] auto b = (TBrowser*)TPython::Eval( "ROOT.TBrowser()" )
(TBrowser *) @0x7ffec81094f8
root [2] TPython::Prompt()
>>> i = 2
>>> print(i)
2
For a more complete description of the TPython
interface, see →
TPython
.
JupyROOT: (Py)ROOT for Jupyter notebooks
Jupyter notebooks provide a web-based interface that you can use for interactive computing with ROOT.
How to use ROOT with Jupyter notebooks
Two kernels (or language flavours) allow to run ROOT from Jupyter: Python (2 and 3) and C++. In order to use such kernels, there are mainly two options:
- Local ROOT installation: to install ROOT locally, see → Installing ROOT.
In addition to ROOT, two Python packages are required that you can install, for example, withpip
:
pip install jupyter
pip install metakernel
When all requirements fulfilled, you can start a Jupyter server with the Python and C++ kernels from a terminal:
root --notebook
- SWAN: CERN provides an online service to create and run Jupyter notebooks, called SWAN. With this option no installation is required: a browser and a working Internet connection are enough. Both the Python and C++ kernels are available in SWAN.
Many ROOT tutorials are available as Jupyter notebooks. For example, most PyROOT tutorials are listed with two buttons below to obtain their notebook version and to open them in SWAN, respectively.
JavaScript graphics
The ROOT graphics are also available in Jupyter, both in Python and C++. Moreover, you can choose between two modes:
- Static (default): The graphics are displayed as a static image in the output of the notebook cell.
- Interactive: The graphics are shown as an interactive JavaScript display. In order to activate this mode, include
%jsroot on
in a cell. Once enabled, the mode stays on until it is disabled with%jsroot off
(i.e. no need to enable it in every cell).
Example
%jsroot on
c = ROOT.TCanvas()
f1 = ROOT.TF1("func1", "sin(x)", 0, 10)
f1.Draw()
c.Draw() # Necessary to make the graphics show!
If you execute the above code in a cell, the output shows the following interactive canvas:
Note
The creation and drawing of a canvas are necessary when displaying ROOT graphics in a notebook. If no canvas is drawn in the cell, no graphics output is shown.
New PyROOT: Backwards-incompatible changes
The new PyROOT has the following backwards-incompatible changes with respect to its predecessor:
- Instantiation of function templates must be done using square brackets instead of parentheses.
Example
Consider the following code snippet:
>>> import ROOT
>>> ROOT.gInterpreter.Declare("""
template<typename T> T foo(T arg) { return arg; }
""")
>>> ROOT.foo['int'] # instantiation
cppyy template proxy (internal)
>>> ROOT.foo('int') # call (auto-instantiation from arg type)
'int'
Note that the above code does not affect class templates, which can be instantiated either with parenthesis or square brackets:
>>> ROOT.std.vector['int'] # instantiation
<class cppyy.gbl.std.vector<int> at 0x5528378>
>>> ROOT.std.vector('int') # instantiation
<class cppyy.gbl.std.vector<int> at 0x5528378>
- Overload resolution in new cppyy has been significantly rewritten, which sometimes can lead to a different overload choice (but still a compatible one).
Example
For example, for the following overloads of std::string
:
string (const char* s, size_t n) (1)
string (const string& str, size_t pos, size_t len = npos) (2)
when invoking ROOT.std.string(s, len(s))
, where s
is a Python string. The new PyROOT picks (2) whereas the old picks (1).
- The conversion between
None
and C++ pointer types is not allowed anymore. UseROOT.nullptr
instead:
>>> ROOT.gInterpreter.Declare("""
class A {};
void foo(A* a) {}
""")
>>> ROOT.foo(ROOT.nullptr) # ok
>>> ROOT.foo(None) # fails
TypeError: void ::foo(A* a) =>
TypeError: could not convert argument 1
- Old PyROOT has
ROOT.Long
andROOT.Double
to pass integer and floating point numbers by reference. In the new PyROOT, you must usectypes
instead.
>>> ROOT.gInterpreter.Declare("""
void foo(int& i) { ++i; }
void foo(double& d) { ++d; }
""")
>>> import ctypes
>>> i = ctypes.c_int(1)
>>> d = ctypes.c_double(1.)
>>> ROOT.foo(i); i
c_int(2)
>>> ROOT.foo(d); d
c_double(2.0)
- When a character array is converted to a Python string, the new PyROOT only considers the characters before the end-of-string character:
>>> ROOT.gInterpreter.Declare('char MyWord[] = "Hello";')
>>> mw = ROOT.MyWord
>>> type(mw)
<class 'str'>
>>> mw # '\x00' is not part of the string
'Hello'
- Any Python class derived from a base C++ class now requires the base class to define a virtual destructor:
>>> ROOT.gInterpreter.Declare("class CppBase {};")
True
>>> class PyDerived(ROOT.CppBase): pass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: CppBase not an acceptable base: no virtual destructor
- There are the following name changes for what concerns cppyy APIs and proxy object attributes:
Old PyROOT/cppyy | New PyROOT/cppyy |
---|---|
cppyy.gbl.MakeNullPointer(klass) | cppyy.bind_object(0, klass) |
cppyy.gbl.BindObject / cppyy.bind_object | cppyy.bind_object |
cppyy.AsCObject | libcppyy.as_cobject |
cppyy.add_pythonization | cppyy.py.add_pythonization |
cppyy.compose_method | cppyy.py.compose_method |
cppyy.make_interface | cppyy.py.pin_type |
cppyy.gbl.nullptr | cppyy.nullptr |
cppyy.gbl.PyROOT.TPyException | cppyy.gbl.CPyCppyy.TPyException |
buffer.SetSize(N) | buffer.reshape((N,)) |
obj.__cppname__ | type(obj).__cpp_name__ |
obj._get_smart_ptr | obj.__smartptr__ |
callable._creates | callable.__creates__ |
callable._mempolicy | callable.__mempolicy__ |
callable._threaded | callable.__release_gil__ |
- New PyROOT does not parse command line arguments by default anymore.
For example, when runningpython my_script.py -b
, the-b
argument is not parsed by PyROOT, and therefore the batch mode is not activated. If you want to enable the PyROOT argument parsing again, start your Python script with:
import ROOT
ROOT.PyConfig.IgnoreCommandLineOptions = False
- In new PyROOT, use
addressof
to retrieve the address of fields in astruct
.
>>> ROOT.gInterpreter.Declare("""
struct MyStruct {
int a;
float b;
};
""")
>>> s = ROOT.MyStruct()
>>> ROOT.addressof(s, 'a')
94015521402096L
>>> ROOT.addressof(s, 'b')
94015521402100L
>>> ROOT.addressof(s)
94015521402096L
In old PyROOT, AddressOf
could be used for that purpose too, but its behaviour was inconsistent.
AddressOf(o)
returned a buffer whose first position contained the address of object o
, but
Address(o, 'field')
returned a buffer whose address was the address of the field, instead
of such address being contained in the first position of the buffer.
Note that in the new PyROOT AddressOf(o)
can still be invoked, and it still returns a buffer
whose first position contains the address of object o
.
- In old PyROOT, there were two C++ classes called
TPyMultiGenFunction
andTPyMultiGradFunction
which inherited fromROOT::Math::IMultiGenFunction
andROOT::Math::IMultiGradFunction
, respectively. The purpose of these classes was to serve as a base class for Python classes that wanted to inherit from the ROOT::Math classes. This allowed to define Python functions that could be used for fitting in Fit::Fitter.
In the new PyROOT,TPyMultiGenFunction
andTPyMultiGradFunction
do not exist anymore, since their functionality is automatically provided by new cppyy: if a Python class inherits from a C++ class, a wrapper C++ class is automatically generated. That wrapper class redirects any call from C++ to the methods implemented by the Python class.
Example
You can make your Python function classes inherit directly from the ROOT::Math C++ classes.
import ROOT
from array import array
class MyMultiGenFCN( ROOT.Math.IMultiGenFunction ):
def NDim( self ):
return 1
def DoEval( self, x ):
return (x[0] - 42) * (x[0] - 42)
def Clone( self ):
x = MyMultiGenFCN()
ROOT.SetOwnership(x, False)
return x
def main():
fitter = ROOT.Fit.Fitter()
myMultiGenFCN = MyMultiGenFCN()
params = array('d', [1.])
fitter.FitFCN(myMultiGenFCN, params)
fitter.Result().Print(ROOT.std.cout, True)
if __name__ == '__main__':
main()
- When iterating over an
std::vector<std::string>
from Python, the elements returned by the iterator are no longer of type Pythonstr
, butcppyy.gbl.std.string
. This is an optimization to make the iteration faster (copies are avoided) and it allows to call modifier methods on thestd::string
objects.
>>> import cppyy
>>> cppyy.cppdef('std::vector<std::string> foo() { return std::vector<std::string>{"foo","bar"};}')
>>> v = cppyy.gbl.foo()
>>> type(v)
<class cppyy.gbl.std.vector<string> at 0x4ad8220>
>>> for s in v:
... print(type(s)) # s is no longer a Python string, but an std::string
...
<class cppyy.gbl.std.string at 0x4cd41b0>
<class cppyy.gbl.std.string at 0x4cd41b0>
- When obtaining the boolean value of a C++ instance proxy, both old and new PyROOT return
False
when such proxy points to null. On the other hand, when the proxy points to a C++ object, old PyROOT just returnsTrue
, while new PyROOT has slightly modified this behaviour: in new cppyy, if__len__
is available, the result of__len__
is used to determine truth. This is done to comply with the default Python behaviour, where__len__
is tried if__bool__
is not present (see → object.bool).
Example
The following code shows how the insertion of __len__
changes the boolean value of an instance proxy.
>>> import ROOT
>>> ROOT.gInterpreter.Declare('class A {};')
>>> a = ROOT.A()
>>> bool(a)
True
>>> ROOT.A.__len__ = lambda self : 0
>>> bool(a) # False because len(a) == 0
False
- In new cppyy, buffer objects are represented by the
LowLevelView
type. If such a buffer points to null, it is not iterable, unlike in the old PyROOT.
For example, in the following code,GetX()
returns a null pointer and therefore an exception is thrown when callinglist(x)
:
>> import ROOT
>> g = ROOT.TGraph()
>> x = g.GetX()
>> xl = list(x) # throws ReferenceError
The code above can be protected by checking for the validity of x
:
>>> xl = list(x) if x else 'your default'