diff --git a/doc/python/mechanisms.rst b/doc/python/mechanisms.rst
index cb19925fcc711abec7d000719d0413153d4c008c..1a690de72d03bf9d9b2e7f4f43c21986d2907214 100644
--- a/doc/python/mechanisms.rst
+++ b/doc/python/mechanisms.rst
@@ -257,6 +257,15 @@ Mechanism catalogues
     2. A further hierarchy of *derived* mechanisms, that allow specialization of
        global parameters, ion bindings, and implementations.
 
+    .. py:method:: __init__(catalogue=None)
+
+        Create an empty or copied catalogue.
+
+        :param catalogue: ``catalogue`` to copy
+        :type catalogue: :class:`catalogue`
+        :return: empty or copied catalogue
+        :rtype: :class:`catalogue`
+
     .. py:method:: __contains__(name)
 
         Test if mechanism with *name* is in the catalogue.
diff --git a/python/mechanism.cpp b/python/mechanism.cpp
index 16f9f25e8388f36135b79a2a9fccab956e1112e6..0d74a21fdb00fec184e5b624662d4c14ebbe1f4d 100644
--- a/python/mechanism.cpp
+++ b/python/mechanism.cpp
@@ -152,6 +152,7 @@ void register_mechanisms(pybind11::module& m) {
         .def("__next__", &py_mech_cat_item_iterator::next);
 
     cat
+        .def(pybind11::init())
         .def(pybind11::init<const arb::mechanism_catalogue&>())
         .def("__contains__", &arb::mechanism_catalogue::has,
              "name"_a, "Is 'name' in the catalogue?")
diff --git a/python/test/unit/test_catalogues.py b/python/test/unit/test_catalogues.py
index f81f3f41b2901841c55e8f3c90255df2c1f5fcd1..4009192ba3a38bbd0c2701b96e3f59103e5eb69d 100644
--- a/python/test/unit/test_catalogues.py
+++ b/python/test/unit/test_catalogues.py
@@ -70,6 +70,32 @@ class Catalogues(unittest.TestCase):
         sim = arb.simulation(rcp, dom, ctx)
         sim.run(tfinal=30)
 
+    def test_empty(self):
+        def len(cat):
+            return sum(1 for _ in cat)
+
+        def hash_(cat):
+            return hash(" ".join(sorted(cat)))
+
+        cat = arb.catalogue()
+        ref = arb.default_catalogue()
+        other = arb.default_catalogue()
+        # Test empty constructor
+        self.assertEqual(0, len(cat), "Expected no mechanisms in `arbor.catalogue()`.")
+        # Test empty extend
+        other.extend(cat, "")
+        self.assertEqual(hash_(ref), hash_(other), "Extending cat with empty should not change cat.")
+        self.assertEqual(0, len(cat), "Extending cat with empty should not change empty.")
+        other.extend(cat, "prefix/")
+        self.assertEqual(hash_(ref), hash_(other), "Extending cat with prefixed empty should not change cat.")
+        self.assertEqual(0, len(cat), "Extending cat with prefixed empty should not change empty.")
+        cat.extend(other, "")
+        self.assertEqual(hash_(other), hash_(cat), "Extending empty with cat should turn empty into cat.")
+        cat = arb.catalogue()
+        cat.extend(other, "prefix/")
+        self.assertNotEqual(hash_(other), hash_(cat), "Extending empty with prefixed cat should not yield cat")
+
+
 
 def suite():
     # specify class and test functions in tuple (here: all tests starting with 'test' from class Contexts