diff --git a/bindings/pyroot_experimental/PyROOT/CMakeLists.txt b/bindings/pyroot_experimental/PyROOT/CMakeLists.txt
index e72ff0b49273e368dd99fe05d5df1f22d0d73895..5b6abea394885f82bdca533e602e6caf26508dc3 100644
--- a/bindings/pyroot_experimental/PyROOT/CMakeLists.txt
+++ b/bindings/pyroot_experimental/PyROOT/CMakeLists.txt
@@ -9,7 +9,9 @@
 ############################################################################
 
 if(dataframe)
-    list(APPEND PYROOT_EXTRA_PYSOURCE ROOT/pythonization/_rdataframe.py)
+    list(APPEND PYROOT_EXTRA_PYSOURCE
+        ROOT/pythonization/_rdataframe.py
+        ROOT/pythonization/_rtensor.py)
     list(APPEND PYROOT_EXTRA_SOURCE src/RDataFramePyz.cxx)
 endif()
 
diff --git a/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_rtensor.py b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_rtensor.py
new file mode 100644
index 0000000000000000000000000000000000000000..fdc7e11b15b9df9c35f53afe3d4ac1f4155e07d2
--- /dev/null
+++ b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_rtensor.py
@@ -0,0 +1,56 @@
+# Author: Stefan Wunsch CERN  02/2019
+
+################################################################################
+# Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.                      #
+# All rights reserved.                                                         #
+#                                                                              #
+# For the licensing terms see $ROOTSYS/LICENSE.                                #
+# For the list of contributors see $ROOTSYS/README/CREDITS.                    #
+################################################################################
+
+from ROOT import pythonization
+from libROOTPython import GetEndianess, GetTensorDataPointer, GetSizeOfType, AsRVec
+from ROOT.pythonization._rvec import _array_interface_dtype_map
+
+
+def get_array_interface(self):
+    cppname = type(self).__cppname__
+    for dtype in _array_interface_dtype_map:
+        if not cppname.find("RTensor<{},".format(dtype)) is -1:
+            dtype_numpy = _array_interface_dtype_map[dtype]
+            dtype_size = GetSizeOfType(dtype)
+            endianess = GetEndianess()
+            shape = self.GetShape()
+            strides = self.GetStrides()
+            # Numpy breaks for data pointer of 0 even though the array is empty.
+            # We set the pointer to 1 but the value itself is arbitrary and never accessed.
+            pointer = GetTensorDataPointer(self, cppname)
+            if pointer == 0:
+                pointer == 1
+            return {
+                "shape": tuple(s for s in shape),
+                "strides": tuple(s * dtype_size for s in strides),
+                "typestr": "{}{}{}".format(endianess, dtype_numpy, dtype_size),
+                "version": 3,
+                "data": (pointer, False)
+            }
+
+
+def add_array_interface_property(klass, name):
+    if True in [
+            not name.find("RTensor<{},".format(dtype)) is -1 for dtype in _array_interface_dtype_map
+    ]:
+        klass.__array_interface__ = property(get_array_interface)
+
+
+@pythonization()
+def pythonize_rtensor(klass, name):
+    # Parameters:
+    # klass: class to be pythonized
+    # name: string containing the name of the class
+
+    if name.startswith("TMVA::Experimental::RTensor<"):
+        # Add numpy array interface
+        add_array_interface_property(klass, name)
+
+    return True
diff --git a/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx b/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx
index 3e574cb48a3df5e83fb4d5a687144705b5541243..508843f9c15ce96e05d8e91c5c2e4c9f7647128c 100644
--- a/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx
+++ b/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx
@@ -64,6 +64,8 @@ static PyMethodDef gPyROOTMethods[] = {{(char *)"AddDirectoryWritePyz", (PyCFunc
                                         (char *)"Get endianess of the system"},
                                        {(char *)"GetVectorDataPointer", (PyCFunction)PyROOT::GetVectorDataPointer, METH_VARARGS,
                                         (char *)"Get pointer to data of vector"},
+                                       {(char *)"GetTensorDataPointer", (PyCFunction)PyROOT::GetTensorDataPointer, METH_VARARGS,
+                                        (char *)"Get pointer to data of RTensor"},
                                        {(char *)"GetSizeOfType", (PyCFunction)PyROOT::GetSizeOfType, METH_VARARGS,
                                         (char *)"Get size of data-type"},
                                        {(char *)"GetCppCallableClass", (PyCFunction)PyROOT::GetCppCallableClass, METH_VARARGS,
diff --git a/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h b/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h
index 26cb345735f117a3179073f619e8798719b60c5b..7ccc586e054c61b4a74d07dcbda4938d7d39fa86 100644
--- a/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h
+++ b/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h
@@ -42,6 +42,7 @@ PyObject *GetCppCallableClass(PyObject *self, PyObject *args);
 
 PyObject *GetEndianess(PyObject *self, PyObject *args);
 PyObject *GetVectorDataPointer(PyObject *self, PyObject *args);
+PyObject *GetTensorDataPointer(PyObject *self, PyObject *args);
 PyObject *GetSizeOfType(PyObject *self, PyObject *args);
 
 PyObject *MakeNumpyDataFrame(PyObject *self, PyObject *obj);
diff --git a/bindings/pyroot_experimental/PyROOT/src/PyzPythonHelpers.cxx b/bindings/pyroot_experimental/PyROOT/src/PyzPythonHelpers.cxx
index 11f26e82535e2f8944c8837a73b3690011ec1f9f..eabfb8514e6b2e14e0a604644609ba1731ada65c 100644
--- a/bindings/pyroot_experimental/PyROOT/src/PyzPythonHelpers.cxx
+++ b/bindings/pyroot_experimental/PyROOT/src/PyzPythonHelpers.cxx
@@ -53,14 +53,13 @@ PyObject *PyROOT::GetSizeOfType(PyObject * /*self*/, PyObject *args)
 }
 
 ////////////////////////////////////////////////////////////////////////////
-/// \brief Get pointer to the data of a vector
-/// \param[in] self Always null, since this is a module function.
-/// \param[in] args[0] Data-type of the C++ object as Python string
-/// \param[in] args[1] Python representation of the C++ object.
+/// \brief Helper to get pointer to the data of an object
+/// \param[in] args Arguments with Python object and data-type
+/// \param[in] method Name of the method returning the pointer to the data
 ///
 /// This function returns the pointer to the data of a vector as an Python
-/// integer.
-PyObject *PyROOT::GetVectorDataPointer(PyObject * /*self*/, PyObject *args)
+/// integer retrieved by the given method.
+PyObject* GetDataPointerHelper(PyObject* args, const std::string& method)
 {
    // Get pointer of C++ object
    PyObject *pyobj = PyTuple_GetItem(args, 0);
@@ -75,7 +74,7 @@ PyObject *PyROOT::GetVectorDataPointer(PyObject * /*self*/, PyObject *args)
    unsigned long long pointer = 0;
    std::stringstream code;
    code << "*((long*)" << &pointer << ") = reinterpret_cast<long>(reinterpret_cast<" << cppname << "*>(" << cppobj
-        << ")->data())";
+        << ")->" << method << "())";
    gInterpreter->Calc(code.str().c_str());
 
    // Return pointer as integer
@@ -83,6 +82,32 @@ PyObject *PyROOT::GetVectorDataPointer(PyObject * /*self*/, PyObject *args)
    return pypointer;
 }
 
+////////////////////////////////////////////////////////////////////////////
+/// \brief Get pointer to the data of a vector
+/// \param[in] self Always null, since this is a module function.
+/// \param[in] args[0] Data-type of the C++ object as Python string
+/// \param[in] args[1] Python representation of the C++ object.
+///
+/// This function returns the pointer to the data of a vector as an Python
+/// integer.
+PyObject *PyROOT::GetVectorDataPointer(PyObject * /*self*/, PyObject *args)
+{
+   return GetDataPointerHelper(args, "data");
+}
+
+////////////////////////////////////////////////////////////////////////////
+/// \brief Get pointer to the data of a RTensor
+/// \param[in] self Always null, since this is a module function.
+/// \param[in] args[0] Data-type of the C++ object as Python string
+/// \param[in] args[1] Python representation of the C++ object.
+///
+/// This function returns the pointer to the data of a RTensor as an Python
+/// integer.
+PyObject *PyROOT::GetTensorDataPointer(PyObject * /*self*/, PyObject *args)
+{
+   return GetDataPointerHelper(args, "GetData");
+}
+
 ////////////////////////////////////////////////////////////////////////////
 /// \brief Get endianess of the system
 /// \param[in] self Always null, since this is a module function.
diff --git a/bindings/pyroot_experimental/PyROOT/test/CMakeLists.txt b/bindings/pyroot_experimental/PyROOT/test/CMakeLists.txt
index e886c5bbb5cd3f3b9e52d4a7648c0c751a66657a..7727e056e5e7ebf03e6043707af5cecd51d7d049 100644
--- a/bindings/pyroot_experimental/PyROOT/test/CMakeLists.txt
+++ b/bindings/pyroot_experimental/PyROOT/test/CMakeLists.txt
@@ -89,6 +89,9 @@ if (dataframe)
     ROOT_ADD_PYUNITTEST(pyroot_pyz_rdataframe_makenumpy rdataframe_makenumpy.py)
 endif()
 
+# RTensor pythonizations
+ROOT_ADD_PYUNITTEST(pyroot_pyz_rtensor rtensor.py)
+
 # Passing Python callables to ROOT.TF
 ROOT_ADD_PYUNITTEST(pyroot_pyz_tf_pycallables tf_pycallables.py)
 
diff --git a/bindings/pyroot_experimental/PyROOT/test/rtensor.py b/bindings/pyroot_experimental/PyROOT/test/rtensor.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f96b33e5c665258060fa10102172280c9e72554
--- /dev/null
+++ b/bindings/pyroot_experimental/PyROOT/test/rtensor.py
@@ -0,0 +1,84 @@
+import unittest
+import ROOT
+RTensor = ROOT.TMVA.Experimental.RTensor
+import numpy as np
+
+
+class ArrayInterface(unittest.TestCase):
+    """
+    Test memory adoption of RTensor array interface.
+    """
+
+    # Helpers
+    dtypes = [
+        "int", "unsigned int", "long", "long long", "Long64_t", "unsigned long",
+        "unsigned long long", "ULong64_t", "float", "double"
+    ]
+
+    def get_maximum_for_dtype(self, dtype):
+        if np.issubdtype(dtype, np.integer):
+            return np.iinfo(dtype).max
+        if np.issubdtype(dtype, np.floating):
+            return np.finfo(dtype).max
+
+    def get_minimum_for_dtype(self, dtype):
+        if np.issubdtype(dtype, np.integer):
+            return np.iinfo(dtype).min
+        if np.issubdtype(dtype, np.floating):
+            return np.finfo(dtype).min
+
+    def check_memory_adoption(self, root_obj, np_obj):
+        # TODO
+        pass
+        """
+        np_obj[0,0] = self.get_maximum_for_dtype(np_obj.dtype)
+        np_obj[0,1] = self.get_minimum_for_dtype(np_obj.dtype)
+        self.assertEqual(root_obj[0], np_obj[0])
+        self.assertEqual(root_obj[1], np_obj[1])
+        """
+
+    def check_shape(self, expected_shape, np_obj):
+        self.assertEqual(len(expected_shape), len(np_obj.shape))
+        for a, b in zip(expected_shape, np_obj.shape):
+            self.assertEqual(a, b)
+
+
+    # Tests
+    def test_memoryAdoption(self):
+        """
+        Test correct adoption of different datatypes
+        """
+        shape = ROOT.std.vector("size_t")((2, 2))
+        for dtype in self.dtypes:
+            root_obj = RTensor(dtype)(shape)
+            np_obj = np.asarray(root_obj)
+            self.check_memory_adoption(root_obj, np_obj)
+            self.check_shape((2, 2), np_obj)
+
+    def test_memoryLayout(self):
+        """
+        Test adoption of the memory layout
+        """
+        shape = ROOT.std.vector("size_t")((2, 2))
+        x = RTensor("float")(shape)
+        y = np.asarray(x)
+        self.assertTrue(y.flags.c_contiguous)
+
+        x = x.Transpose()
+        y = np.asarray(x)
+        self.assertTrue(y.flags.f_contiguous)
+
+    def test_ownData(self):
+        """
+        Test ownership of adopted numpy array
+        """
+        shape = ROOT.std.vector("size_t")((2, 2))
+        x = RTensor("float")(shape)
+        y = np.asarray(x)
+        self.assertFalse(y.flags.owndata)
+
+        y = np.transpose(y)
+        self.assertFalse(y.flags.owndata)
+
+        y = np.copy(y)
+        self.assertTrue(y.flags.owndata)