Python-specific functionalities offered by ROOT.
This page lists the so-called "pythonizations", that is those functionalities offered by ROOT for classes and functions which are specific to Python usage of the package and provide a more pythonic experience.
This example shows how to use the @pythonization
decorator to add extra behaviour to C++ user classes that are used from Python via PyROOT. Let's first define a new C++ class. In this tutorial, we will see how we can "pythonize" this class, i.e. how we can add some extra behaviour to it to make it more pythonic or easier to use from Python. Note: In this example, the class is defined dynamically for demonstration purposes, but it could also be a C++ class defined in some library or header. For more information about loading C++ user code to be used from Python with PyROOT, please see: https://root.cern.ch/manual/python#loading-user-libraries-and-just-in-time-compilation-jitting
Next, we define a pythonizor function: the function that will be responsible for injecting new behaviour in our C++ class MyClass
. To convert a given Python function into a pythonizor, we need to decorate it with the @pythonization decorator. Such decorator allows us to define which which class we want to pythonize by providing its class name and its namespace (if the latter is not specified, it defaults to the global namespace, i.e. '::').
The decorated function - the pythonizor - must accept either one or two parameters:
MyClass
objects are represented as a string in Python (i.e. what would be shown when I print that object). For that purpose, I can define the following pythonizor function. There are two important things to be noted here:MyClass
.pythonizor_of_myclass
provides and injects a new implementation for __str__
, the mechanism that Python provides to define how to represent objects as strings. This new implementation always returns the string "This is a MyClass object". Once we have defined our pythonizor function, let's see it in action. We will now use the MyClass
class for the first time from Python: we will create a new instance of that class. At this moment, the pythonizor will execute and modify the class - pythonizors are always lazily run when a given class is used for the first time from a Python script.
Since the pythonizor already executed, we should now see the new behaviour. For that purpose, let's print my_object
(should show "This is a MyClass
object").
The previous example is just a simple one, but there are many ways in which a class can be pythonized. Typical examples are the redefinition of dunder methods (e.g. __iter__
and __next__
to make your objects iterable from Python). If you need some inspiration, many ROOT classes are pythonized in the way we just saw; their pythonizations can be seen at: https://github.com/root-project/root/tree/master/bindings/pyroot/pythonizations/pythonROOT/pythonizatio The @pythonization decorator offers a few more options when it comes to matching classes that you want to pythonize. We saw that we can match a single class, but we can also specify a list of classes to pythonize. The following code defines a couple of new classes:
Note that these classes belong to the NS
namespace. As mentioned above, the @pythonization decorator accepts a parameter with the namespace of the class or classes to be pythonized. Therefore, a pythonizor that matches both classes would look like this:
Both classes will have the new attribute:
In addition, @pythonization also accepts prefixes of classes in a certain namespace in order to match multiple classes in that namespace. To signal that what we provide to @pythonization is a prefix, we need to set the is_prefix
argument to True
(default is False
).
A common case where matching prefixes is useful is when we have a templated class and we want to pythonize all possible instantiations of that template. For example, we can pythonize the std::vector
(templated) class like so:
Since we defined a prefix to do the match, the pythonization will be applied both if we instantiate e.g. a vector of integers and a vector of doubles.
These are some examples of combinations of prefixes and namespaces and the corresponding classes that they match:
NS1::NS2
namespace.Prefix
in the global namespace.Prefix
in the NS
namespace Moreover, a pythonizor function can have a second optional parameter that contains the fully-qualified name of the class being pythonized. This can be useful e.g. if we would like to do some more complex filtering of classes in our pythonizor, for instance using regular expressions.The pythonizor above will be applied to any instantiation of std::pair
- we can see this with the print we did inside the pythonizor. Note that we could use the name
parameter to e.g. further filter which particular instantiations we would like to pythonize.
Note that, to pythonize multiple classes in different namespaces, we can stack multiple @pythonization decorators. For example, if we define these classes:
We can pythonize both of them with a single pythonizor function like so:
If we now access both classes, we should see that the pythonizor runs twice.
So far we have seen how pythonizations can be registered for classes that have not been used yet. We have discussed how, in that case, the pythonizor functions are executed lazily when their target class/es are used for the first time in the application. However, it can also happen that our target class/es have already been accessed by the time we register a pythonization. In such a scenario, the pythonizor is applied immediately (at registration time) to the target class/es
Let's see an example of what was just explained. We will define a new class and immediately create an object of that class. We can check how the object still does not have a new attribute pythonized
that we are going to inject in the next step.
After that, we will register a pythonization for MyClass2
. Since the class has already been used, the pythonization will happen right away.
Now our object does have the pythonized
attribute:
This example illustrates the pretty printing feature of PyROOT, which reveals the content of the object if a string representation is requested, e.g., by Python's print statement. The printing behaves similar to the ROOT prompt powered by the C++ interpreter cling.
Create an object with PyROOT
Print the object, which reveals the content. Note that print
calls the special method __str__
of the object internally.
The output can be retrieved as string by any function that triggers the __str__
special method of the object, e.g., str
or format
.
Note that the interactive Python prompt does not call __str__
, it calls __repr__
, which implements a formal and unique string representation of the object.
The print output behaves similar to the ROOT prompt, e.g., here for a ROOT histogram.
If cling cannot produce any nice representation for the class, we fall back to a "<ClassName at address>" format, which is what __repr__
returns
Classes | |
class | ROOT::RDataFrame |
ROOT's RDataFrame offers a modern, high-level interface for analysis of data stored in TTree , CSV and other data formats, in C++ or Python. More... | |
class | ROOT::VecOps::RVec< T > |
A "std::vector"-like collection of values implementing handy operation to analyse them. More... | |
class | TArray |
Abstract array base class. More... | |
class | TDirectory::TContext |
TDirectory::TContext keeps track and restore the current directory. More... | |
class | TDirectory |
Describe directory structure in memory. More... | |
class | TDirectoryFile |
A ROOT file is structured in Directories (like a file system). More... | |
class | TF1 |
1-Dim function class More... | |
class | TFile |
A ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-like logical structure, possibly including subdirectory hierarchies. More... | |
class | TGraph |
A TGraph is an object made of two arrays X and Y with npoints each. More... | |
class | TH1 |
TH1 is the base class of all histogram classes in ROOT. More... | |
class | THistPainter |
The histogram painter class. More... | |
class | TScatter |
A TScatter is able to draw four variables scatter plot on a single plot. More... | |
class | TTree |
A TTree represents a columnar dataset. More... | |
Functions | |
ROOT._pythonization.pythonization (class_name, ns='::', is_prefix=False) | |
Decorator that allows to pythonize C++ classes. | |
ROOT._pythonization.pythonization | ( | class_name, | |
ns = '::' , |
|||
is_prefix = False |
|||
) |
Decorator that allows to pythonize C++ classes.
To pythonize means to add some extra behaviour to a C++ class that is used from Python via PyROOT, so that such a class can be used in an easier / more "pythonic" way. When a pythonization is registered with this decorator, the injection of the new behaviour in the C++ class is done immediately, if the class has already been used from the application, or lazily, i.e. only when the class is first accessed from the application.
Args: class_name (string/iterable[string]): specifies either a single string or multiple strings, where each string can be either (i) the name of a C++ class to be pythonized, or (ii) a prefix to match all classes whose name starts with that prefix. ns (string): namespace of the classes to be pythonized. Default is the global namespace (::
). is_prefix (boolean): if True, class_name
contains one or multiple prefixes, each prefix potentially matching multiple classes. Default is False. These are examples of prefixes and namespace and what they match:
Returns: function: function that receives the user-defined function and decorates it.
Definition at line 26 of file __init__.py.