From f9384ccf4ec25611f240a1b1b8e837fea8535029 Mon Sep 17 00:00:00 2001
From: cmoineau <cyril.moineau@cea.fr>
Date: Tue, 18 Feb 2025 08:39:04 +0000
Subject: [PATCH] Add attributesName function in C++ and Python API.

---
 include/aidge/operator/ArgMax.hpp             |   8 ++
 include/aidge/operator/AvgPooling.hpp         |   8 ++
 include/aidge/operator/BatchNorm.hpp          |   8 ++
 include/aidge/operator/BitShift.hpp           |  10 +-
 include/aidge/operator/Cast.hpp               |   8 ++
 include/aidge/operator/Clip.hpp               |   8 ++
 include/aidge/operator/Concat.hpp             |   8 ++
 include/aidge/operator/ConstantOfShape.hpp    |  12 +-
 include/aidge/operator/Conv.hpp               |   8 ++
 include/aidge/operator/ConvDepthWise.hpp      |   8 ++
 include/aidge/operator/DepthToSpace.hpp       |   8 ++
 include/aidge/operator/Flatten.hpp            |   8 ++
 include/aidge/operator/Fold.hpp               |   8 ++
 include/aidge/operator/Gather.hpp             |   8 ++
 include/aidge/operator/GridSample.hpp         |   8 ++
 include/aidge/operator/Heaviside.hpp          |   8 ++
 include/aidge/operator/LRN.hpp                |  10 +-
 include/aidge/operator/LeakyReLU.hpp          |   8 ++
 include/aidge/operator/MaxPooling.hpp         |   8 ++
 include/aidge/operator/Memorize.hpp           |   8 ++
 include/aidge/operator/Pad.hpp                |   8 ++
 include/aidge/operator/Pop.hpp                |   8 ++
 include/aidge/operator/ReduceMean.hpp         |   8 ++
 include/aidge/operator/ReduceSum.hpp          |   8 ++
 include/aidge/operator/Reshape.hpp            |   8 ++
 include/aidge/operator/Resize.hpp             |   8 ++
 include/aidge/operator/Scaling.hpp            |   8 ++
 include/aidge/operator/Shape.hpp              |   8 ++
 include/aidge/operator/Slice.hpp              |   8 ++
 include/aidge/operator/Softmax.hpp            |   8 ++
 include/aidge/operator/Split.hpp              |   8 ++
 include/aidge/operator/Squeeze.hpp            |   8 ++
 include/aidge/operator/Stack.hpp              |   8 ++
 include/aidge/operator/Transpose.hpp          |   8 ++
 include/aidge/operator/Unfold.hpp             |   8 ++
 include/aidge/operator/Unsqueeze.hpp          |   8 ++
 python_binding/operator/pybind_ArgMax.cpp     |   8 ++
 python_binding/operator/pybind_AvgPooling.cpp |   9 ++
 python_binding/operator/pybind_BatchNorm.cpp  |   9 ++
 python_binding/operator/pybind_BitShift.cpp   |  10 +-
 python_binding/operator/pybind_Cast.cpp       |  10 +-
 python_binding/operator/pybind_Clip.cpp       | 127 ++++++++++--------
 python_binding/operator/pybind_Concat.cpp     |   9 ++
 .../operator/pybind_ConstantOfShape.cpp       |  12 +-
 python_binding/operator/pybind_Conv.cpp       |   9 ++
 .../operator/pybind_ConvDepthWise.cpp         |   9 ++
 .../operator/pybind_DepthToSpace.cpp          |   9 ++
 python_binding/operator/pybind_Gather.cpp     |   9 ++
 python_binding/operator/pybind_GridSample.cpp |   9 ++
 python_binding/operator/pybind_Heaviside.cpp  |   9 ++
 python_binding/operator/pybind_LRN.cpp        |   9 ++
 python_binding/operator/pybind_LeakyReLU.cpp  |   9 ++
 python_binding/operator/pybind_MaxPooling.cpp |   9 ++
 python_binding/operator/pybind_Memorize.cpp   |  10 +-
 python_binding/operator/pybind_Pad.cpp        |   8 ++
 python_binding/operator/pybind_Pop.cpp        |   9 ++
 python_binding/operator/pybind_ReduceMean.cpp |   8 ++
 python_binding/operator/pybind_ReduceSum.cpp  |   9 ++
 python_binding/operator/pybind_Reshape.cpp    |   9 ++
 python_binding/operator/pybind_Resize.cpp     |  16 ++-
 python_binding/operator/pybind_Scaling.cpp    |   9 ++
 python_binding/operator/pybind_Shape.cpp      |   9 ++
 python_binding/operator/pybind_Slice.cpp      |   9 ++
 python_binding/operator/pybind_Softmax.cpp    |   9 ++
 python_binding/operator/pybind_Split.cpp      |   9 ++
 python_binding/operator/pybind_Squeeze.cpp    |   9 ++
 python_binding/operator/pybind_Stack.cpp      |   9 ++
 python_binding/operator/pybind_Transpose.cpp  |   8 ++
 python_binding/operator/pybind_Unsqueeze.cpp  |   8 ++
 69 files changed, 647 insertions(+), 72 deletions(-)

diff --git a/include/aidge/operator/ArgMax.hpp b/include/aidge/operator/ArgMax.hpp
index 7358899a9..6d24d87bd 100644
--- a/include/aidge/operator/ArgMax.hpp
+++ b/include/aidge/operator/ArgMax.hpp
@@ -177,6 +177,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ArgMaxAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/AvgPooling.hpp b/include/aidge/operator/AvgPooling.hpp
index ab9e111f2..7e02a94ab 100644
--- a/include/aidge/operator/AvgPooling.hpp
+++ b/include/aidge/operator/AvgPooling.hpp
@@ -223,6 +223,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::AvgPoolingAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/BatchNorm.hpp b/include/aidge/operator/BatchNorm.hpp
index ddffaeb02..995179d7f 100644
--- a/include/aidge/operator/BatchNorm.hpp
+++ b/include/aidge/operator/BatchNorm.hpp
@@ -152,6 +152,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::BatchNormAttr>::data; 
+	}
 };
 
 extern template class Aidge::BatchNorm_Op<2>;
diff --git a/include/aidge/operator/BitShift.hpp b/include/aidge/operator/BitShift.hpp
index 9368e3461..d066507dd 100644
--- a/include/aidge/operator/BitShift.hpp
+++ b/include/aidge/operator/BitShift.hpp
@@ -147,6 +147,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return { "OutputTensor" };
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::BitShiftAttr>::data;
+	}
 };
 
 /**
@@ -166,7 +174,7 @@ namespace {
  * @brief Specialization of `EnumStrings` for `BitShiftAttr`.
  */
 template <>
-const char* const EnumStrings<Aidge::BitShiftAttr>::data[] = { "bit_shift_direction" };
+const char* const EnumStrings<Aidge::BitShiftAttr>::data[] = {"bit_shift_direction"};
 }
 
 #endif /* AIDGE_CORE_OPERATOR_BITSHIFT_H_ */
diff --git a/include/aidge/operator/Cast.hpp b/include/aidge/operator/Cast.hpp
index 1f934fbc7..12c3a280a 100644
--- a/include/aidge/operator/Cast.hpp
+++ b/include/aidge/operator/Cast.hpp
@@ -137,6 +137,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::CastAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Clip.hpp b/include/aidge/operator/Clip.hpp
index 0825b85bb..93c042d86 100644
--- a/include/aidge/operator/Clip.hpp
+++ b/include/aidge/operator/Clip.hpp
@@ -148,6 +148,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return { "data_output" };
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ClipAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Concat.hpp b/include/aidge/operator/Concat.hpp
index ad31ef1a3..7a4ea74a4 100644
--- a/include/aidge/operator/Concat.hpp
+++ b/include/aidge/operator/Concat.hpp
@@ -169,6 +169,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return { "data_output" };
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ConcatAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/ConstantOfShape.hpp b/include/aidge/operator/ConstantOfShape.hpp
index 18e626544..d837d108a 100644
--- a/include/aidge/operator/ConstantOfShape.hpp
+++ b/include/aidge/operator/ConstantOfShape.hpp
@@ -63,7 +63,7 @@ private:
 public:
   /**
    * @brief constructor for ConstantOfShape_op
-   * @param[in] value : a scalar tensor which holds the value that will 
+   * @param[in] value : a scalar tensor which holds the value that will
    * fill the output tensor
    */
   ConstantOfShape_Op(const Tensor &value = Tensor(0.f))
@@ -116,6 +116,14 @@ public:
   static const std::vector<std::string> getOutputsName() {
     return {"constant_of_shape"};
   }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ConstantOfShapeAttr>::data;
+	}
 };
 
 // helper with C-style array instead of std::array for kernel_dims to allow
@@ -129,7 +137,7 @@ inline std::shared_ptr<Node> ConstantOfShape(const Tensor value = Tensor(0.f),
 
 namespace {
 template <>
-const char *const EnumStrings<Aidge::ConstantOfShapeAttr>::data[] = {"Value"};
+const char *const EnumStrings<Aidge::ConstantOfShapeAttr>::data[] = {"value"};
 }
 
 #endif // AIDGE_CORE_OPERATOR_CONSTANT_OF_SHAPE_H_
diff --git a/include/aidge/operator/Conv.hpp b/include/aidge/operator/Conv.hpp
index 8984ebd08..7beea057e 100644
--- a/include/aidge/operator/Conv.hpp
+++ b/include/aidge/operator/Conv.hpp
@@ -209,6 +209,14 @@ public:
     static const std::vector<std::string> getOutputsName(){
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ConvAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/ConvDepthWise.hpp b/include/aidge/operator/ConvDepthWise.hpp
index 03e821041..3090b9feb 100644
--- a/include/aidge/operator/ConvDepthWise.hpp
+++ b/include/aidge/operator/ConvDepthWise.hpp
@@ -189,6 +189,14 @@ public:
     static const std::vector<std::string> getOutputsName(){
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ConvDepthWiseAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/DepthToSpace.hpp b/include/aidge/operator/DepthToSpace.hpp
index 769dad767..cc51ea180 100644
--- a/include/aidge/operator/DepthToSpace.hpp
+++ b/include/aidge/operator/DepthToSpace.hpp
@@ -164,6 +164,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::DepthToSpaceAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Flatten.hpp b/include/aidge/operator/Flatten.hpp
index a7f5c6435..10ce58ad0 100644
--- a/include/aidge/operator/Flatten.hpp
+++ b/include/aidge/operator/Flatten.hpp
@@ -155,6 +155,14 @@ public:
     static const std::vector<std::string> getOutputsName(){
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::FlattenAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Fold.hpp b/include/aidge/operator/Fold.hpp
index 3b5b9449d..9d2d4e0df 100644
--- a/include/aidge/operator/Fold.hpp
+++ b/include/aidge/operator/Fold.hpp
@@ -210,6 +210,14 @@ public:
     static const std::vector<std::string> getOutputsName(){
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::FoldAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Gather.hpp b/include/aidge/operator/Gather.hpp
index dc3e1a814..3842a041e 100644
--- a/include/aidge/operator/Gather.hpp
+++ b/include/aidge/operator/Gather.hpp
@@ -184,6 +184,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::GatherAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/GridSample.hpp b/include/aidge/operator/GridSample.hpp
index 999f7bba1..28c5fb5e5 100644
--- a/include/aidge/operator/GridSample.hpp
+++ b/include/aidge/operator/GridSample.hpp
@@ -170,6 +170,14 @@ public:
 	static const std::vector<std::string> getOutputsName() {
 		return {"data_output"};
 	}
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::GridSampleAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Heaviside.hpp b/include/aidge/operator/Heaviside.hpp
index 94eaa400a..874853c4e 100644
--- a/include/aidge/operator/Heaviside.hpp
+++ b/include/aidge/operator/Heaviside.hpp
@@ -110,6 +110,14 @@ public:
         return {"output"};
     }
 
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::HeavisideAttr>::data; 
+	}
+
     /**
      * @brief Get the attributes of the operator.
      */
diff --git a/include/aidge/operator/LRN.hpp b/include/aidge/operator/LRN.hpp
index 369da5f97..9019c089b 100644
--- a/include/aidge/operator/LRN.hpp
+++ b/include/aidge/operator/LRN.hpp
@@ -158,6 +158,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::LRNAttr>::data; 
+	}
 };
 
 /**
@@ -176,7 +184,7 @@ namespace {
  * @brief EnumStrings specialization for LRNAttr.
  */
 template <>
-const char *const EnumStrings<Aidge::LRNAttr>::data[] = {"alpha", "beta", "bias", "size"};
+const char *const EnumStrings<Aidge::LRNAttr>::data[] = {"alpha", "beta", "bias", "size", nullptr};
 }
 
 #endif /* AIDGE_CORE_OPERATOR_LRN_H_ */
diff --git a/include/aidge/operator/LeakyReLU.hpp b/include/aidge/operator/LeakyReLU.hpp
index 46730d026..5381b3cb1 100644
--- a/include/aidge/operator/LeakyReLU.hpp
+++ b/include/aidge/operator/LeakyReLU.hpp
@@ -115,6 +115,14 @@ public:
     static const std::vector<std::string> getOutputsName(){
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::LeakyReLUAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/MaxPooling.hpp b/include/aidge/operator/MaxPooling.hpp
index 9063fb88b..f4f38de4a 100644
--- a/include/aidge/operator/MaxPooling.hpp
+++ b/include/aidge/operator/MaxPooling.hpp
@@ -198,6 +198,14 @@ public:
      * @return A vector of output tensors names.
      */
     static const std::vector<std::string> getOutputsName(){ return {"data_output"}; }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::MaxPoolingAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Memorize.hpp b/include/aidge/operator/Memorize.hpp
index deefc0077..10bbfce85 100644
--- a/include/aidge/operator/Memorize.hpp
+++ b/include/aidge/operator/Memorize.hpp
@@ -240,6 +240,14 @@ public:
     static const std::vector<std::string> getOutputsName(){
         return {"data_output", "data_output_rec"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::MemorizeAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Pad.hpp b/include/aidge/operator/Pad.hpp
index c1ed3500c..417e9664c 100644
--- a/include/aidge/operator/Pad.hpp
+++ b/include/aidge/operator/Pad.hpp
@@ -216,6 +216,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::PadAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Pop.hpp b/include/aidge/operator/Pop.hpp
index 2cf567329..630c58c0d 100644
--- a/include/aidge/operator/Pop.hpp
+++ b/include/aidge/operator/Pop.hpp
@@ -211,6 +211,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::PopAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/ReduceMean.hpp b/include/aidge/operator/ReduceMean.hpp
index 6aded3638..c6d875719 100644
--- a/include/aidge/operator/ReduceMean.hpp
+++ b/include/aidge/operator/ReduceMean.hpp
@@ -165,6 +165,14 @@ public:
         return {"data_output"};
     }
 
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ReduceMeanAttr>::data; 
+	}
+
     virtual ~ReduceMean_Op() noexcept;
 };
 
diff --git a/include/aidge/operator/ReduceSum.hpp b/include/aidge/operator/ReduceSum.hpp
index 5a3674b21..72f6bf9b2 100644
--- a/include/aidge/operator/ReduceSum.hpp
+++ b/include/aidge/operator/ReduceSum.hpp
@@ -170,6 +170,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ReduceSumAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Reshape.hpp b/include/aidge/operator/Reshape.hpp
index c170ad79e..51623737e 100644
--- a/include/aidge/operator/Reshape.hpp
+++ b/include/aidge/operator/Reshape.hpp
@@ -176,6 +176,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ReshapeAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Resize.hpp b/include/aidge/operator/Resize.hpp
index 89224f927..3a4ef3771 100644
--- a/include/aidge/operator/Resize.hpp
+++ b/include/aidge/operator/Resize.hpp
@@ -191,6 +191,14 @@ class Resize_Op
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ResizeAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Scaling.hpp b/include/aidge/operator/Scaling.hpp
index b33fb5841..c1f4514c9 100644
--- a/include/aidge/operator/Scaling.hpp
+++ b/include/aidge/operator/Scaling.hpp
@@ -134,6 +134,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ScalingAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Shape.hpp b/include/aidge/operator/Shape.hpp
index 609e354d5..84d497abf 100644
--- a/include/aidge/operator/Shape.hpp
+++ b/include/aidge/operator/Shape.hpp
@@ -163,6 +163,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::ShapeAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Slice.hpp b/include/aidge/operator/Slice.hpp
index d32bc4fe2..ea4d21e9a 100644
--- a/include/aidge/operator/Slice.hpp
+++ b/include/aidge/operator/Slice.hpp
@@ -203,6 +203,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::SliceAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Softmax.hpp b/include/aidge/operator/Softmax.hpp
index 290132690..a7d8283a0 100644
--- a/include/aidge/operator/Softmax.hpp
+++ b/include/aidge/operator/Softmax.hpp
@@ -130,6 +130,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::SoftmaxAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Split.hpp b/include/aidge/operator/Split.hpp
index 3c6b52d3c..9f2beb3aa 100644
--- a/include/aidge/operator/Split.hpp
+++ b/include/aidge/operator/Split.hpp
@@ -173,6 +173,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output_0", "data_output_n"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::SplitAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Squeeze.hpp b/include/aidge/operator/Squeeze.hpp
index e3c1f4de1..9a2cc8f54 100644
--- a/include/aidge/operator/Squeeze.hpp
+++ b/include/aidge/operator/Squeeze.hpp
@@ -142,6 +142,14 @@ public:
   static const std::vector<std::string> getOutputsName() {
     return {"squeezed"};
   }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::SqueezeAttr>::data; 
+	}
 };
 
 // helper with C-style array instead of std::array for kernel_dims to allow
diff --git a/include/aidge/operator/Stack.hpp b/include/aidge/operator/Stack.hpp
index 71e4e780a..0e420789d 100644
--- a/include/aidge/operator/Stack.hpp
+++ b/include/aidge/operator/Stack.hpp
@@ -212,6 +212,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::StackAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Transpose.hpp b/include/aidge/operator/Transpose.hpp
index ab3b18e51..d760ccd0d 100644
--- a/include/aidge/operator/Transpose.hpp
+++ b/include/aidge/operator/Transpose.hpp
@@ -166,6 +166,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::TransposeAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Unfold.hpp b/include/aidge/operator/Unfold.hpp
index 333413b1d..bea32c6cc 100644
--- a/include/aidge/operator/Unfold.hpp
+++ b/include/aidge/operator/Unfold.hpp
@@ -199,6 +199,14 @@ public:
     static const std::vector<std::string> getOutputsName() {
         return {"data_output"};
     }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::UnfoldAttr>::data; 
+	}
 };
 
 /**
diff --git a/include/aidge/operator/Unsqueeze.hpp b/include/aidge/operator/Unsqueeze.hpp
index c25800acb..8c5909182 100644
--- a/include/aidge/operator/Unsqueeze.hpp
+++ b/include/aidge/operator/Unsqueeze.hpp
@@ -140,6 +140,14 @@ public:
   static const std::vector<std::string> getOutputsName() {
     return {"unsqueezed"};
   }
+
+	/**
+	 * @brief Retrieves the names of the attributes for the operator.
+	 * @return A vector containing the attributes name.
+	 */
+	static const char* const* attributesName(){
+		return EnumStrings<Aidge::UnsqueezeAttr>::data; 
+	}
 };
 
 // helper with C-style array instead of std::array for kernel_dims to allow
diff --git a/python_binding/operator/pybind_ArgMax.cpp b/python_binding/operator/pybind_ArgMax.cpp
index 3de54afd7..75f325749 100644
--- a/python_binding/operator/pybind_ArgMax.cpp
+++ b/python_binding/operator/pybind_ArgMax.cpp
@@ -43,6 +43,14 @@ void init_ArgMax(py::module &m) {
     .def(py::init<std::int32_t, bool, bool>(), py::arg("axis"), py::arg("keep_dims"), py::arg("select_last_index"))
     .def_static("get_inputs_name", &ArgMax_Op::getInputsName)
     .def_static("get_outputs_name", &ArgMax_Op::getOutputsName)
+	.def_static("attributes_name", []() {
+		std::vector<std::string> result;
+		auto attributes = ArgMax_Op::attributesName();
+		for (size_t i = 0; i < size(EnumStrings<ArgMaxAttr>::data); ++i) {
+			result.emplace_back(attributes[i]);
+		}
+		return result;
+	})
     ;
   declare_registrable<ArgMax_Op>(m, pyClassName);
 
diff --git a/python_binding/operator/pybind_AvgPooling.cpp b/python_binding/operator/pybind_AvgPooling.cpp
index f93df9e2c..6130fc271 100644
--- a/python_binding/operator/pybind_AvgPooling.cpp
+++ b/python_binding/operator/pybind_AvgPooling.cpp
@@ -62,6 +62,15 @@ template <DimIdx_t DIM> void declare_AvgPoolingOp(py::module &m) {
             py::arg("ceil_mode") = false)
     .def_static("get_inputs_name", &AvgPooling_Op<DIM>::getInputsName)
     .def_static("get_outputs_name", &AvgPooling_Op<DIM>::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = AvgPooling_Op<DIM>::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<AvgPoolingAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
     .def_readonly_static("Type", &AvgPooling_Op<DIM>::Type);
 
   declare_registrable<AvgPooling_Op<DIM>>(m, pyClassName);
diff --git a/python_binding/operator/pybind_BatchNorm.cpp b/python_binding/operator/pybind_BatchNorm.cpp
index 3339db0f2..199ef8134 100644
--- a/python_binding/operator/pybind_BatchNorm.cpp
+++ b/python_binding/operator/pybind_BatchNorm.cpp
@@ -42,6 +42,15 @@ void declare_BatchNormOp(py::module& m) {
             py::arg("training_mode"))
         .def_static("get_inputs_name", &BatchNorm_Op<DIM>::getInputsName)
         .def_static("get_outputs_name", &BatchNorm_Op<DIM>::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = BatchNorm_Op<DIM>::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<BatchNormAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def_readonly_static("Type", &BatchNorm_Op<DIM>::Type);
 
     declare_registrable<BatchNorm_Op<DIM>>(m, pyClassName);
diff --git a/python_binding/operator/pybind_BitShift.cpp b/python_binding/operator/pybind_BitShift.cpp
index b4f6c90e5..f2f4b223d 100644
--- a/python_binding/operator/pybind_BitShift.cpp
+++ b/python_binding/operator/pybind_BitShift.cpp
@@ -35,7 +35,15 @@ void init_BitShift(py::module &m) {
         .def(py::init<BitShift_Op::BitShiftDirection>(), py::arg("direction"))
         .def("direction", &BitShift_Op::direction, "Get the direction of the bit shift (left or right).")
         .def_static("get_inputs_name", &BitShift_Op::getInputsName, "Get the names of the input tensors.")
-        .def_static("get_outputs_name", &BitShift_Op::getOutputsName, "Get the names of the output tensors.");
+        .def_static("get_outputs_name", &BitShift_Op::getOutputsName, "Get the names of the output tensors.")
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = BitShift_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<BitShiftAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		});
 
     // Enum binding under BitShiftOp class
     py::enum_<BitShift_Op::BitShiftDirection>(pyBitShiftOp, "BitShiftDirection")
diff --git a/python_binding/operator/pybind_Cast.cpp b/python_binding/operator/pybind_Cast.cpp
index 960a084ff..1e0ad7f9b 100644
--- a/python_binding/operator/pybind_Cast.cpp
+++ b/python_binding/operator/pybind_Cast.cpp
@@ -32,7 +32,15 @@ void init_Cast(py::module &m) {
         .def(py::init<DataType>(), py::arg("target_type"))
         .def("target_type", &Cast_Op::targetType, "Get the targeted type, output tensor data type")
         .def_static("get_inputs_name", &Cast_Op::getInputsName, "Get the names of the input tensors.")
-        .def_static("get_outputs_name", &Cast_Op::getOutputsName, "Get the names of the output tensors.");
+        .def_static("get_outputs_name", &Cast_Op::getOutputsName, "Get the names of the output tensors.")
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Cast_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<CastAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		});
 
     // Binding for the Cast function
     m.def("Cast", &Cast, py::arg("target_type"), py::arg("name") = "",
diff --git a/python_binding/operator/pybind_Clip.cpp b/python_binding/operator/pybind_Clip.cpp
index 7c4563a98..a22a002d4 100644
--- a/python_binding/operator/pybind_Clip.cpp
+++ b/python_binding/operator/pybind_Clip.cpp
@@ -1,59 +1,68 @@
-/********************************************************************************
- * Copyright (c) 2023 CEA-List
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- ********************************************************************************/
-
-#include <pybind11/pybind11.h>
-
-#include "aidge/data/Tensor.hpp"
-#include "aidge/operator/Clip.hpp"
-#include "aidge/operator/OperatorTensor.hpp"
-#include "aidge/backend/OperatorImpl.hpp"
-#include "aidge/utils/Types.h"
-
-namespace py = pybind11;
-namespace Aidge {
-
-void init_Clip(py::module& m) {
-    py::class_<Clip_Op, std::shared_ptr<Clip_Op>, OperatorTensor>(m, "ClipOp", py::multiple_inheritance(),
-        R"mydelimiter(
-        Initialize a Clip operator.
-
-        :param min : Minimum clipping value. Default is the lowest possible float value.
-        :type min : :py:class:`float`
-        :param max : Maximum clipping value. Default is the highest possible float value.
-        :type max : :py:class:`float`
-        )mydelimiter")
-    .def(py::init<float, float>(), py::arg("min") = std::numeric_limits<float>::lowest(), py::arg("max") = std::numeric_limits<float>::max())
-    .def_static("get_inputs_name", &Clip_Op::getInputsName)
-    .def_static("get_outputs_name", &Clip_Op::getOutputsName)
-    .def("min", &Clip_Op::min, py::return_value_policy::reference_internal)
-    .def("max", &Clip_Op::max, py::return_value_policy::reference_internal);
-
-    declare_registrable<Clip_Op>(m, "ClipOp");
-
-    m.def("Clip", &Clip, py::arg("name") = "",
-        py::arg("min") = std::numeric_limits<float>::lowest(),
-        py::arg("max") = std::numeric_limits<float>::max(),
-        R"mydelimiter(
-        ClipOp is a tensor operator that performs a clipping operation on tensor elements.
-        This class allows limiting tensor values to a specified range, defined by the `min` 
-        and `max` parameters. Values outside this range are replaced by the corresponding 
-        limit values. When `min` is greater than `max`, the clip operator sets all the 'input' values to the value of `max`.
-
-        :param min: Minimum clipping value.
-        :type min: :py:class:`float`
-        :param max: Maximum clipping value.
-        :type max: :py:class:`float`
-        :param name: Name of the node.
-        :type name: :py:class:`str`
-        )mydelimiter");
-}
-
-}  // namespace Aidge
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#include <pybind11/pybind11.h>
+
+#include "aidge/data/Tensor.hpp"
+#include "aidge/operator/Clip.hpp"
+#include "aidge/operator/OperatorTensor.hpp"
+#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/utils/Types.h"
+
+namespace py = pybind11;
+namespace Aidge {
+
+void init_Clip(py::module& m) {
+    py::class_<Clip_Op, std::shared_ptr<Clip_Op>, OperatorTensor>(m, "ClipOp", py::multiple_inheritance(),
+        R"mydelimiter(
+        Initialize a Clip operator.
+
+        :param min : Minimum clipping value. Default is the lowest possible float value.
+        :type min : :py:class:`float`
+        :param max : Maximum clipping value. Default is the highest possible float value.
+        :type max : :py:class:`float`
+        )mydelimiter")
+    .def(py::init<float, float>(), py::arg("min") = std::numeric_limits<float>::lowest(), py::arg("max") = std::numeric_limits<float>::max())
+    .def_static("get_inputs_name", &Clip_Op::getInputsName)
+    .def_static("get_outputs_name", &Clip_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Clip_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<ClipAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
+    .def("min", &Clip_Op::min, py::return_value_policy::reference_internal)
+    .def("max", &Clip_Op::max, py::return_value_policy::reference_internal);
+
+    declare_registrable<Clip_Op>(m, "ClipOp");
+
+    m.def("Clip", &Clip, py::arg("name") = "",
+        py::arg("min") = std::numeric_limits<float>::lowest(),
+        py::arg("max") = std::numeric_limits<float>::max(),
+        R"mydelimiter(
+        ClipOp is a tensor operator that performs a clipping operation on tensor elements.
+        This class allows limiting tensor values to a specified range, defined by the `min` 
+        and `max` parameters. Values outside this range are replaced by the corresponding 
+        limit values. When `min` is greater than `max`, the clip operator sets all the 'input' values to the value of `max`.
+
+        :param min: Minimum clipping value.
+        :type min: :py:class:`float`
+        :param max: Maximum clipping value.
+        :type max: :py:class:`float`
+        :param name: Name of the node.
+        :type name: :py:class:`str`
+        )mydelimiter");
+}
+
+}  // namespace Aidge
diff --git a/python_binding/operator/pybind_Concat.cpp b/python_binding/operator/pybind_Concat.cpp
index d2410b03a..236f16922 100644
--- a/python_binding/operator/pybind_Concat.cpp
+++ b/python_binding/operator/pybind_Concat.cpp
@@ -34,6 +34,15 @@ void init_Concat(py::module& m) {
              py::arg("axis") = 0)
         .def_static("get_inputs_name", &Concat_Op::getInputsName)
         .def_static("get_outputs_name", &Concat_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Concat_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<ConcatAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def_readonly_static("Type", &Concat_Op::Type);
 
     declare_registrable<Concat_Op>(m, "ConcatOp");
diff --git a/python_binding/operator/pybind_ConstantOfShape.cpp b/python_binding/operator/pybind_ConstantOfShape.cpp
index 5a0e858f1..b185f2f80 100644
--- a/python_binding/operator/pybind_ConstantOfShape.cpp
+++ b/python_binding/operator/pybind_ConstantOfShape.cpp
@@ -31,9 +31,17 @@ void init_ConstantOfShape(py::module &m) {
                      that will fill the output tensor.
       :type value : :py:class:`Tensor`
       )mydelimiter")
-      .def("get_inputs_name", &ConstantOfShape_Op::getInputsName)
+      .def_static("get_inputs_name", &ConstantOfShape_Op::getInputsName)
       .def_static("get_outputs_name", &ConstantOfShape_Op::getOutputsName)
-      .def_static("value", &ConstantOfShape_Op::value);
+      .def_static("attributes_name", []() {
+        std::vector<std::string> result;
+        auto attributes = ConstantOfShape_Op::attributesName();
+        for (size_t i = 0; i < size(EnumStrings<ConstantOfShapeAttr>::data); ++i) {
+          result.emplace_back(attributes[i]);
+        }
+        return result;
+      })
+      .def("value", &ConstantOfShape_Op::value);
 
   m.def("ConstantOfShape", &ConstantOfShape, py::arg("value") = Tensor(0.f),
         py::arg("name") = "",
diff --git a/python_binding/operator/pybind_Conv.cpp b/python_binding/operator/pybind_Conv.cpp
index 6ab073be6..e65a74c0c 100644
--- a/python_binding/operator/pybind_Conv.cpp
+++ b/python_binding/operator/pybind_Conv.cpp
@@ -43,6 +43,15 @@ void declare_ConvOp(py::module &m) {
             py::arg("dilation_dims") = std::vector<DimSize_t>(DIM,1))
         .def_static("get_inputs_name", &Conv_Op<DIM>::getInputsName)
         .def_static("get_outputs_name", &Conv_Op<DIM>::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Conv_Op<DIM>::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<ConvAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def("in_channels", &Conv_Op<DIM>::inChannels)
         .def("out_channels", &Conv_Op<DIM>::outChannels)
         .def_readonly_static("Type", &Conv_Op<DIM>::Type)
diff --git a/python_binding/operator/pybind_ConvDepthWise.cpp b/python_binding/operator/pybind_ConvDepthWise.cpp
index 5e24431d7..7ddbefd3d 100644
--- a/python_binding/operator/pybind_ConvDepthWise.cpp
+++ b/python_binding/operator/pybind_ConvDepthWise.cpp
@@ -56,6 +56,15 @@ void declare_ConvDepthWiseOp(py::module &m) {
         py::arg("dilation_dims"))
   .def_static("get_inputs_name", &ConvDepthWise_Op<DIM>::getInputsName)
   .def_static("get_outputs_name", &ConvDepthWise_Op<DIM>::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = ConvDepthWise_Op<DIM>::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<ConvDepthWiseAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+				return result;
+		})
   .def("nb_channels", &ConvDepthWise_Op<DIM>::nbChannels)
   .def_readonly_static("Type", &ConvDepthWise_Op<DIM>::Type);
 
diff --git a/python_binding/operator/pybind_DepthToSpace.cpp b/python_binding/operator/pybind_DepthToSpace.cpp
index efb8a7406..d33386711 100644
--- a/python_binding/operator/pybind_DepthToSpace.cpp
+++ b/python_binding/operator/pybind_DepthToSpace.cpp
@@ -37,6 +37,15 @@ void declare_DepthToSpace(py::module &m) {
         }), py::arg("block_size"), py::arg("mode") = "CRD")
     .def_static("get_inputs_name", &DepthToSpace_Op::getInputsName)
     .def_static("get_outputs_name", &DepthToSpace_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = DepthToSpace_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<DepthToSpaceAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
     .def_readonly_static("Type", &DepthToSpace_Op::Type)
     .def("__repr__", [](DepthToSpace_Op& b) {
         return fmt::format("Operator(type='{}')", b.Type);
diff --git a/python_binding/operator/pybind_Gather.cpp b/python_binding/operator/pybind_Gather.cpp
index fed44a1e2..6afeb42a7 100644
--- a/python_binding/operator/pybind_Gather.cpp
+++ b/python_binding/operator/pybind_Gather.cpp
@@ -44,6 +44,15 @@ void init_Gather(py::module& m) {
                 py::arg("gathered_shape"))
         .def_static("get_inputs_name", &Gather_Op::getInputsName)
         .def_static("get_outputs_name", &Gather_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Gather_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<GatherAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def_readonly_static("Type", &Gather_Op::Type);
 
     declare_registrable<Gather_Op>(m, "GatherOp");
diff --git a/python_binding/operator/pybind_GridSample.cpp b/python_binding/operator/pybind_GridSample.cpp
index 3464941dd..f4f0335fd 100644
--- a/python_binding/operator/pybind_GridSample.cpp
+++ b/python_binding/operator/pybind_GridSample.cpp
@@ -65,6 +65,15 @@ void declare_GridSampleOp(py::module &m) {
             py::arg("align_corners") = false)
         .def_static("get_inputs_name", &GridSample_Op::getInputsName)
         .def_static("get_outputs_name", &GridSample_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = GridSample_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<GridSampleAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def_readonly_static("Type", &GridSample_Op::Type)
         ;
 
diff --git a/python_binding/operator/pybind_Heaviside.cpp b/python_binding/operator/pybind_Heaviside.cpp
index cbc2502aa..b8d7f1d80 100644
--- a/python_binding/operator/pybind_Heaviside.cpp
+++ b/python_binding/operator/pybind_Heaviside.cpp
@@ -37,6 +37,15 @@ void init_Heaviside(py::module &m) {
         .def(py::init<float>(), py::arg("value"))
         .def_static("get_inputs_name", &Heaviside_Op::getInputsName)
         .def_static("get_outputs_name", &Heaviside_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Heaviside_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<HeavisideAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def_readonly_static("Type", &Heaviside_Op::Type);
 
     declare_registrable<Heaviside_Op>(m, "HeavisideOp");
diff --git a/python_binding/operator/pybind_LRN.cpp b/python_binding/operator/pybind_LRN.cpp
index bb04ed1c5..f802152ba 100644
--- a/python_binding/operator/pybind_LRN.cpp
+++ b/python_binding/operator/pybind_LRN.cpp
@@ -30,6 +30,15 @@ void init_LRN(py::module& m) {
         .def(py::init<std::int32_t>(), py::arg("size"))
         .def_static("get_inputs_name", &LRN_Op::getInputsName)
         .def_static("get_outputs_name", &LRN_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = LRN_Op::attributesName();
+			for (size_t i = 0; attributes[i] != nullptr; ++i) {
+				result.emplace_back(attributes[i]);
+			}
+				return result;
+		})
         .def_readonly_static("Type", &LRN_Op::Type);
 
     m.def("LRN", &LRN, py::arg("size"), py::arg("name") = "",
diff --git a/python_binding/operator/pybind_LeakyReLU.cpp b/python_binding/operator/pybind_LeakyReLU.cpp
index 564fd90be..ab81052d2 100644
--- a/python_binding/operator/pybind_LeakyReLU.cpp
+++ b/python_binding/operator/pybind_LeakyReLU.cpp
@@ -30,6 +30,15 @@ void init_LeakyReLU(py::module& m) {
         .def(py::init<float>(), py::arg("negative_slope"))
         .def_static("get_inputs_name", &LeakyReLU_Op::getInputsName)
         .def_static("get_outputs_name", &LeakyReLU_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = LeakyReLU_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<LeakyReLUAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def_readonly_static("Type", &LeakyReLU_Op::Type);
 
     declare_registrable<LeakyReLU_Op>(m, "LeakyReLUOp");
diff --git a/python_binding/operator/pybind_MaxPooling.cpp b/python_binding/operator/pybind_MaxPooling.cpp
index bdbc1edd3..953e56ebe 100644
--- a/python_binding/operator/pybind_MaxPooling.cpp
+++ b/python_binding/operator/pybind_MaxPooling.cpp
@@ -52,6 +52,15 @@ template <DimIdx_t DIM> void declare_MaxPoolingOp(py::module &m) {
         py::arg("ceil_mode"))
   .def_static("get_inputs_name", &MaxPooling_Op<DIM>::getInputsName)
   .def_static("get_outputs_name", &MaxPooling_Op<DIM>::getOutputsName)
+
+  .def_static("attributes_name", []() {
+    std::vector<std::string> result;
+    auto attributes = MaxPooling_Op<DIM>::attributesName();
+    for (size_t i = 0; i < size(EnumStrings<MaxPoolingAttr>::data); ++i) {
+      result.emplace_back(attributes[i]);
+    }
+    return result;
+  })
   .def_readonly_static("Type", &MaxPooling_Op<DIM>::Type);
   
   declare_registrable<MaxPooling_Op<DIM>>(m, pyClassName);
diff --git a/python_binding/operator/pybind_Memorize.cpp b/python_binding/operator/pybind_Memorize.cpp
index 3ac112211..f583602c9 100644
--- a/python_binding/operator/pybind_Memorize.cpp
+++ b/python_binding/operator/pybind_Memorize.cpp
@@ -23,7 +23,15 @@ void init_Memorize(py::module& m) {
     py::class_<Memorize_Op, std::shared_ptr<Memorize_Op>, OperatorTensor>(m, "MemorizeOp", py::multiple_inheritance())
         .def(py::init<const std::uint32_t>(), py::arg("end_step"))
         .def_static("get_inputs_name", &Memorize_Op::getInputsName)
-        .def_static("get_outputs_name", &Memorize_Op::getOutputsName);
+        .def_static("get_outputs_name", &Memorize_Op::getOutputsName)
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Memorize_Op::attributesName();
+			for (size_t i = 0;i < size(EnumStrings<MemorizeAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		});
 
     declare_registrable<Memorize_Op>(m, "MemorizeOp");
 
diff --git a/python_binding/operator/pybind_Pad.cpp b/python_binding/operator/pybind_Pad.cpp
index fe899a75a..7b37bb206 100644
--- a/python_binding/operator/pybind_Pad.cpp
+++ b/python_binding/operator/pybind_Pad.cpp
@@ -50,6 +50,14 @@ template <DimIdx_t DIM> void declare_PadOp(py::module &m) {
         py::arg("borderValue") = 0.0)
     .def_static("get_inputs_name", &Pad_Op<DIM>::getInputsName)
     .def_static("get_outputs_name", &Pad_Op<DIM>::getOutputsName)
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Pad_Op<DIM>::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<PadAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
     .def_readonly_static("Type", &Pad_Op<DIM>::Type);
 
   declare_registrable<Pad_Op<DIM>>(m, pyClassName);
diff --git a/python_binding/operator/pybind_Pop.cpp b/python_binding/operator/pybind_Pop.cpp
index 2040f642b..20606d24d 100644
--- a/python_binding/operator/pybind_Pop.cpp
+++ b/python_binding/operator/pybind_Pop.cpp
@@ -23,6 +23,15 @@ void init_Pop(py::module& m) {
     .def(py::init<>())
     .def_static("get_inputs_name", &Pop_Op::getInputsName)
     .def_static("get_outputs_name", &Pop_Op::getOutputsName)
+
+	.def_static("attributes_name", []() {
+		std::vector<std::string> result;
+		auto attributes = Pop_Op::attributesName();
+		for (size_t i = 0; i < size(EnumStrings<PopAttr>::data); ++i) {
+			result.emplace_back(attributes[i]);
+		}
+		return result;
+	})
     .def_readonly_static("Type", &Pop_Op::Type);
 
     m.def("Pop", &Pop, py::arg("name") = "");
diff --git a/python_binding/operator/pybind_ReduceMean.cpp b/python_binding/operator/pybind_ReduceMean.cpp
index 028e45755..d29f6bfe7 100644
--- a/python_binding/operator/pybind_ReduceMean.cpp
+++ b/python_binding/operator/pybind_ReduceMean.cpp
@@ -43,6 +43,14 @@ void declare_ReduceMeanOp(py::module &m) {
     .def(py::init<std::vector<std::int32_t>, bool, bool>(), py::arg("axes") = std::vector<std::int32_t>(), py::arg("keep_dims") = true, py::arg("noop_with_empty_axes") = false)
     .def_static("get_inputs_name", &ReduceMean_Op::getInputsName)
     .def_static("get_outputs_name", &ReduceMean_Op::getOutputsName)
+	.def_static("attributes_name", []() {
+		std::vector<std::string> result;
+		auto attributes = ReduceMean_Op::attributesName();
+		for (size_t i = 0; i < size(EnumStrings<ReduceMeanAttr>::data); ++i) {
+			result.emplace_back(attributes[i]);
+		}
+		return result;
+	})
     .def_readonly_static("Type", &ReduceMean_Op::Type)
     ;
   declare_registrable<ReduceMean_Op>(m, pyClassName);
diff --git a/python_binding/operator/pybind_ReduceSum.cpp b/python_binding/operator/pybind_ReduceSum.cpp
index eaa57ef1c..f139f2e7b 100644
--- a/python_binding/operator/pybind_ReduceSum.cpp
+++ b/python_binding/operator/pybind_ReduceSum.cpp
@@ -43,6 +43,15 @@ void init_ReduceSum(py::module &m) {
     .def(py::init<std::vector<std::int32_t>, bool, bool>(), py::arg("axes"), py::arg("keep_dims"), py::arg("noop_with_empty_axes"))
     .def_static("get_inputs_name", &ReduceSum_Op::getInputsName)
     .def_static("get_outputs_name", &ReduceSum_Op::getOutputsName)
+
+	.def_static("attributes_name", []() {
+		std::vector<std::string> result;
+		auto attributes = ReduceSum_Op::attributesName();
+		for (size_t i = 0; i < size(EnumStrings<ReduceSumAttr>::data); ++i) {
+			result.emplace_back(attributes[i]);
+		}
+		return result;
+	})
     ;
   declare_registrable<ReduceSum_Op>(m, pyClassName);
 
diff --git a/python_binding/operator/pybind_Reshape.cpp b/python_binding/operator/pybind_Reshape.cpp
index e3244f5dd..d263796ce 100644
--- a/python_binding/operator/pybind_Reshape.cpp
+++ b/python_binding/operator/pybind_Reshape.cpp
@@ -35,6 +35,15 @@ void init_Reshape(py::module& m) {
     .def(py::init<const std::vector<std::int64_t>&, bool>(), py::arg("shape"), py::arg("allowzero"))
     .def_static("get_inputs_name", &Reshape_Op::getInputsName)
     .def_static("get_outputs_name", &Reshape_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Reshape_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<ReshapeAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
     .def_readonly_static("Type", &Reshape_Op::Type);
 
     declare_registrable<Reshape_Op>(m, "ReshapeOp");
diff --git a/python_binding/operator/pybind_Resize.cpp b/python_binding/operator/pybind_Resize.cpp
index 2aa626098..10a60e1f9 100644
--- a/python_binding/operator/pybind_Resize.cpp
+++ b/python_binding/operator/pybind_Resize.cpp
@@ -25,10 +25,18 @@ namespace Aidge {
 void init_Resize(py::module &m) {
   py::class_<Resize_Op, std::shared_ptr<Resize_Op>, OperatorTensor>(
           m, "ResizeOp", py::multiple_inheritance())
-          .def(py::init<Interpolation::CoordinateTransformation, Interpolation::Mode, float, PadBorderType>(), py::arg("coordinate_transformation_mode"), py::arg("interpolation_mode"), py::arg("cubic_coeff_a") = -0.75f, py::arg("padding_mode") = PadBorderType::Edge)
-          .def_static("get_inputs_name", &Resize_Op::getInputsName)
-          .def_static("get_outputs_name", &Resize_Op::getOutputsName)
-          .def_readonly_static("Type", &Resize_Op::Type);
+        .def(py::init<Interpolation::CoordinateTransformation, Interpolation::Mode, float, PadBorderType>(), py::arg("coordinate_transformation_mode"), py::arg("interpolation_mode"), py::arg("cubic_coeff_a") = -0.75f, py::arg("padding_mode") = PadBorderType::Edge)
+        .def_static("get_inputs_name", &Resize_Op::getInputsName)
+        .def_static("get_outputs_name", &Resize_Op::getOutputsName)
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Resize_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<ResizeAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+		    return result;
+		})
+        .def_readonly_static("Type", &Resize_Op::Type);
 
   declare_registrable<Resize_Op>(m, "ResizeOp");
 
diff --git a/python_binding/operator/pybind_Scaling.cpp b/python_binding/operator/pybind_Scaling.cpp
index c555bca89..ba975bb06 100644
--- a/python_binding/operator/pybind_Scaling.cpp
+++ b/python_binding/operator/pybind_Scaling.cpp
@@ -41,6 +41,15 @@ void init_Scaling(py::module& m) {
              py::arg("is_output_unsigned"))
         .def_static("get_inputs_name", &Scaling_Op::getInputsName)
         .def_static("get_outputs_name", &Scaling_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Scaling_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<ScalingAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def_readonly_static("Type", &Scaling_Op::Type);
 
     declare_registrable<Scaling_Op>(m, "ScalingOp");
diff --git a/python_binding/operator/pybind_Shape.cpp b/python_binding/operator/pybind_Shape.cpp
index cc7669a24..3c8974bf0 100644
--- a/python_binding/operator/pybind_Shape.cpp
+++ b/python_binding/operator/pybind_Shape.cpp
@@ -34,6 +34,15 @@ void init_Shape(py::module& m) {
         .def(py::init<const std::int64_t, const std::int64_t>(), py::arg("start"), py::arg("end"))
         .def_static("get_inputs_name", &Shape_Op::getInputsName)
         .def_static("get_outputs_name", &Shape_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Shape_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<ShapeAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def_readonly_static("Type", &Shape_Op::Type);
 
     declare_registrable<Shape_Op>(m, "ShapeOp");
diff --git a/python_binding/operator/pybind_Slice.cpp b/python_binding/operator/pybind_Slice.cpp
index f01751b86..1cfd63f65 100644
--- a/python_binding/operator/pybind_Slice.cpp
+++ b/python_binding/operator/pybind_Slice.cpp
@@ -45,6 +45,15 @@ void init_Slice(py::module& m) {
                   py::arg("steps") = std::vector<std::int64_t>())
     .def_static("get_inputs_name", &Slice_Op::getInputsName)
     .def_static("get_outputs_name", &Slice_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Slice_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<SliceAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
     .def_readonly_static("Type", &Slice_Op::Type);
 
     declare_registrable<Slice_Op>(m, "SliceOp");
diff --git a/python_binding/operator/pybind_Softmax.cpp b/python_binding/operator/pybind_Softmax.cpp
index 093f448e4..7a4a687fd 100644
--- a/python_binding/operator/pybind_Softmax.cpp
+++ b/python_binding/operator/pybind_Softmax.cpp
@@ -30,6 +30,15 @@ void init_Softmax(py::module& m) {
         .def(py::init<std::int32_t>(), py::arg("axis"))
         .def_static("get_inputs_name", &Softmax_Op::getInputsName)
         .def_static("get_outputs_name", &Softmax_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Softmax_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<SoftmaxAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def_readonly_static("Type", &Softmax_Op::Type);
     declare_registrable<Softmax_Op>(m, "SoftmaxOp");
     m.def("Softmax", &Softmax, py::arg("axis"), py::arg("name") = "",
diff --git a/python_binding/operator/pybind_Split.cpp b/python_binding/operator/pybind_Split.cpp
index f02a699e4..052fa277e 100644
--- a/python_binding/operator/pybind_Split.cpp
+++ b/python_binding/operator/pybind_Split.cpp
@@ -36,6 +36,15 @@ void init_Split(py::module& m) {
             py::arg("split"))
     .def_static("get_inputs_name", &Split_Op::getInputsName)
     .def_static("get_outputs_name", &Split_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Split_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<SplitAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
     .def_readonly_static("Type", &Split_Op::Type);
 
     declare_registrable<Split_Op>(m, "SplitOp");
diff --git a/python_binding/operator/pybind_Squeeze.cpp b/python_binding/operator/pybind_Squeeze.cpp
index f7ee4d722..7808c78da 100644
--- a/python_binding/operator/pybind_Squeeze.cpp
+++ b/python_binding/operator/pybind_Squeeze.cpp
@@ -34,6 +34,15 @@ void init_Squeeze(py::module &m) {
     )mydelimiter")
     .def_static("get_inputs_name", &Squeeze_Op::getInputsName)
     .def_static("get_outputs_name", &Squeeze_Op::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = Squeeze_Op::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<SqueezeAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
     .def("axes", &Squeeze_Op::axes);
 
     declare_registrable<Squeeze_Op>(m, "SqueezeOp");
diff --git a/python_binding/operator/pybind_Stack.cpp b/python_binding/operator/pybind_Stack.cpp
index c9bd969fa..026167446 100644
--- a/python_binding/operator/pybind_Stack.cpp
+++ b/python_binding/operator/pybind_Stack.cpp
@@ -26,6 +26,15 @@ void init_Stack(py::module &m) {
         .def(py::init<const std::uint32_t>(), py::arg("max_elements"))
         .def_static("get_inputs_name", &StackOp::getInputsName)
         .def_static("get_outputs_name", &StackOp::getOutputsName)
+
+		.def_static("attributes_name", []() {
+			std::vector<std::string> result;
+			auto attributes = StackOp::attributesName();
+			for (size_t i = 0; i < size(EnumStrings<StackAttr>::data); ++i) {
+				result.emplace_back(attributes[i]);
+			}
+			return result;
+		})
         .def_readonly_static("Type", &StackOp::s_type);
 
     m.def("Stack",
diff --git a/python_binding/operator/pybind_Transpose.cpp b/python_binding/operator/pybind_Transpose.cpp
index 20794a155..1882aa4c4 100644
--- a/python_binding/operator/pybind_Transpose.cpp
+++ b/python_binding/operator/pybind_Transpose.cpp
@@ -38,6 +38,14 @@ void declare_Transpose(py::module &m) {
     .def(py::init<const std::vector<DimSize_t>&>(), py::arg("output_dims_order")=std::vector<std::size_t>())
     .def_static("get_inputs_name", &Transpose_Op::getInputsName)
     .def_static("get_outputs_name", &Transpose_Op::getOutputsName)
+	.def_static("attributes_name", []() {
+		std::vector<std::string> result;
+		auto attributes = Transpose_Op::attributesName();
+		for (size_t i = 0; i < size(EnumStrings<TransposeAttr>::data); ++i) {
+			result.emplace_back(attributes[i]);
+		}
+		return result;
+	})
     .def_readonly_static("Type", &Transpose_Op::Type);
   declare_registrable<Transpose_Op>(m, pyClassName);
   m.def("Transpose", &Transpose, py::arg("output_dims_order")=std::vector<std::size_t>(), py::arg("name") = "",
diff --git a/python_binding/operator/pybind_Unsqueeze.cpp b/python_binding/operator/pybind_Unsqueeze.cpp
index c21a7bcfa..1ef94202c 100644
--- a/python_binding/operator/pybind_Unsqueeze.cpp
+++ b/python_binding/operator/pybind_Unsqueeze.cpp
@@ -30,6 +30,14 @@ void init_Unsqueeze(py::module &m) {
       // Here we bind the methods of the Unsqueeze_Op that will want to access
       .def_static("get_inputs_name", &Unsqueeze_Op::getInputsName)
       .def_static("get_outputs_name", &Unsqueeze_Op::getOutputsName)
+        .def_static("attributes_name", []() {
+            std::vector<std::string> result;
+            auto attributes = Unsqueeze_Op::attributesName();
+            for (size_t i = 0; i < size(EnumStrings<UnsqueezeAttr>::data); ++i) {
+                result.emplace_back(attributes[i]);
+            }
+            return result;
+        })
       .def_readonly_static("Type", &Unsqueeze_Op::Type)
       ;
 
-- 
GitLab