{"id":2983,"date":"2021-11-19T03:56:01","date_gmt":"2021-11-19T03:56:01","guid":{"rendered":"https:\/\/www.pythontutorial.net\/?page_id=2983"},"modified":"2025-03-28T03:37:35","modified_gmt":"2025-03-28T03:37:35","slug":"python-metaclass-example","status":"publish","type":"page","link":"https:\/\/www.pythontutorial.net\/python-oop\/python-metaclass-example\/","title":{"rendered":"Python Metaclass Example"},"content":{"rendered":"\n<p><strong>Summary<\/strong>: in this tutorial, you&#8217;ll learn about a Python metaclass example that creates classes with many features.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id='introduction-to-the-python-metaclass-example'>Introduction to the Python metaclass example <a href=\"#introduction-to-the-python-metaclass-example\" class=\"anchor\" id=\"introduction-to-the-python-metaclass-example\" title=\"Anchor for Introduction to the Python metaclass example\">#<\/a><\/h2>\n\n\n\n<p>The following defines a <code>Person<\/code> class with two attributes <code>name<\/code> and <code>age<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Person<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, name, age)<\/span>:<\/span>\n        self.name = name\n        self.age = age\n\n<span class=\"hljs-meta\">    @property<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">name<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> self._name\n\n<span class=\"hljs-meta\">    @name.setter<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">name<\/span><span class=\"hljs-params\">(self, value)<\/span>:<\/span>\n        self._name = value\n\n<span class=\"hljs-meta\">    @property<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">age<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> self._age\n\n<span class=\"hljs-meta\">    @age.setter<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">age<\/span><span class=\"hljs-params\">(self, value)<\/span>:<\/span>\n        self._age = value\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__eq__<\/span><span class=\"hljs-params\">(self, other)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> self.name == other.name <span class=\"hljs-keyword\">and<\/span> self.age == other.age\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__hash__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> hash(<span class=\"hljs-string\">f'<span class=\"hljs-subst\">{self.name, self.age}<\/span>'<\/span>)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__str__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">f'Person(name=<span class=\"hljs-subst\">{self.name}<\/span>,age=<span class=\"hljs-subst\">{self.age}<\/span>)'<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__repr__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">f'Person(name=<span class=\"hljs-subst\">{self.name}<\/span>,age=<span class=\"hljs-subst\">{self.age}<\/span>)'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Typically, when defining a new class, you need to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Define a list of object&#8217;s properties.<\/li>\n\n\n\n<li>Define an <code><a href=\"https:\/\/www.pythontutorial.net\/python-oop\/python-__init__\/\">__init__<\/a><\/code> method to initialize object&#8217;s attributes.<\/li>\n\n\n\n<li>Implement the <code><a href=\"https:\/\/www.pythontutorial.net\/python-oop\/python-__str__\/\">__str__<\/a><\/code> and <code><a href=\"https:\/\/www.pythontutorial.net\/python-oop\/python-__repr__\/\">__repr__<\/a><\/code> methods to represent the objects in human-readable and machine-readable formats.<\/li>\n\n\n\n<li>Implement the <code><a href=\"https:\/\/www.pythontutorial.net\/python-oop\/python-__eq__\/\">__eq__<\/a><\/code> method to compare objects by values of all properties.<\/li>\n\n\n\n<li>Implement the <code><a href=\"https:\/\/www.pythontutorial.net\/python-oop\/python-__hash__\/\">__hash__<\/a><\/code> method to use the objects of the class as keys of a <a href=\"https:\/\/www.pythontutorial.net\/python-basics\/python-dictionary\/\">dictionary<\/a> or elements of a <a href=\"https:\/\/www.pythontutorial.net\/python-basics\/python-set\/\">set<\/a>.<\/li>\n<\/ul>\n\n\n\n<p>As you can see, it requires a lot of code.<\/p>\n\n\n\n<p>Imagine you want to define a Person class like this and automagically has all the functions above:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Person<\/span>:<\/span>\n    props = &#91;<span class=\"hljs-string\">'first_name'<\/span>, <span class=\"hljs-string\">'last_name'<\/span>, <span class=\"hljs-string\">'age'<\/span>]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>To do that, you can use a metaclass.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id='define-a-metaclass'>Define a metaclass <a href=\"#define-a-metaclass\" class=\"anchor\" id=\"define-a-metaclass\" title=\"Anchor for Define a metaclass\">#<\/a><\/h2>\n\n\n\n<p>First, define the <code>Data<\/code> metaclass that inherits from the <code><a href=\"https:\/\/www.pythontutorial.net\/python-oop\/python-type-class\/\">type<\/a><\/code> class:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Data<\/span><span class=\"hljs-params\">(type)<\/span>:<\/span>\n    <span class=\"hljs-keyword\">pass<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Second, override the <code><a href=\"https:\/\/www.pythontutorial.net\/python-oop\/python-__new__\/\">__new__<\/a><\/code> method to return a new class object:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Data<\/span><span class=\"hljs-params\">(type)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__new__<\/span><span class=\"hljs-params\">(mcs, name, bases, class_dict)<\/span>:<\/span>\n        class_obj = super().__new__(mcs, name, bases, class_dict)\n        <span class=\"hljs-keyword\">return<\/span> class_obj<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that the <code>__new__<\/code> method is a <a href=\"https:\/\/www.pythontutorial.net\/python-oop\/python-static-methods\/\">static method<\/a> of the <code>Data<\/code> metaclass. And you don&#8217;t need to use the <code>@staticmethod<\/code> decorator because Python treats it special. <\/p>\n\n\n\n<p>Also, the <code>__new__<\/code> method creates a new class like the <code>Person<\/code> class, not the instance of the <code>Person<\/code> class.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id='create-property-objects'>Create property objects <a href=\"#create-property-objects\" class=\"anchor\" id=\"create-property-objects\" title=\"Anchor for Create property objects\">#<\/a><\/h2>\n\n\n\n<p>First, define a <code>Prop<\/code> class that accepts an attribute name and contains three methods for creating a property object(<code>set<\/code>, <code>get<\/code>, and <code>delete<\/code>). The <code>Data<\/code> metaclass will use this <code>Prop<\/code> class for adding property objects to the class.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Prop<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, attr)<\/span>:<\/span>\n        self._attr = attr\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get<\/span><span class=\"hljs-params\">(self, obj)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> getattr(obj, self._attr)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">set<\/span><span class=\"hljs-params\">(self, obj, value)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> setattr(obj, self._attr, value)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete<\/span><span class=\"hljs-params\">(self, obj)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> delattr(obj, self._attr)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Second, create a new static method <code>define_property()<\/code> that creates a property object for each attribute from the <code>props<\/code> list:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Data<\/span><span class=\"hljs-params\">(type)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__new__<\/span><span class=\"hljs-params\">(mcs, name, bases, class_dict)<\/span>:<\/span>\n        class_obj = super().__new__(mcs, name, bases, class_dict)\n        Data.define_property(class_obj)\n\n        <span class=\"hljs-keyword\">return<\/span> class_obj\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">define_property<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props:\n            attr = <span class=\"hljs-string\">f'_<span class=\"hljs-subst\">{prop}<\/span>'<\/span>\n            prop_obj = property(\n                fget=Prop(attr).get,\n                fset=Prop(attr).set,\n                fdel=Prop(attr).delete\n            )\n            setattr(class_obj, prop, prop_obj)\n\n        <span class=\"hljs-keyword\">return<\/span> class_obj<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The following defines the <code>Person<\/code> class that uses the <code>Data<\/code> metaclass:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Person<\/span><span class=\"hljs-params\">(metaclass=Data)<\/span>:<\/span>\n    props = &#91;<span class=\"hljs-string\">'name'<\/span>, <span class=\"hljs-string\">'age'<\/span>]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>Person<\/code> class has two properties <code>name<\/code> and <code>age<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">pprint(Person.__dict__)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Output:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">mappingproxy({<span class=\"hljs-string\">'__dict__'<\/span>: &lt;attribute <span class=\"hljs-string\">'__dict__'<\/span> of <span class=\"hljs-string\">'Person'<\/span> objects&gt;,\n              <span class=\"hljs-string\">'__doc__'<\/span>: <span class=\"hljs-literal\">None<\/span>,\n              <span class=\"hljs-string\">'__module__'<\/span>: <span class=\"hljs-string\">'__main__'<\/span>,\n              <span class=\"hljs-string\">'__weakref__'<\/span>: &lt;attribute <span class=\"hljs-string\">'__weakref__'<\/span> of <span class=\"hljs-string\">'Person'<\/span> objects&gt;,\n              <span class=\"hljs-string\">'age'<\/span>: &lt;property object at <span class=\"hljs-number\">0x000002213CA92090<\/span>&gt;,\n              <span class=\"hljs-string\">'name'<\/span>: &lt;property object at <span class=\"hljs-number\">0x000002213C772A90<\/span>&gt;,\n              <span class=\"hljs-string\">'props'<\/span>: &#91;<span class=\"hljs-string\">'name'<\/span>, <span class=\"hljs-string\">'age'<\/span>]})<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id='define-__init__-method'>Define __init__ method <a href=\"#define-__init__-method\" class=\"anchor\" id=\"define-__init__-method\" title=\"Anchor for Define __init__ method\">#<\/a><\/h2>\n\n\n\n<p>The following defines an <code>init<\/code> static method and assign it to the <code>__init__<\/code> attribute of the class object:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Data<\/span><span class=\"hljs-params\">(type)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__new__<\/span><span class=\"hljs-params\">(mcs, name, bases, class_dict)<\/span>:<\/span>\n        class_obj = super().__new__(mcs, name, bases, class_dict)\n\n        <span class=\"hljs-comment\"># create property<\/span>\n        Data.define_property(class_obj)\n\n        <span class=\"hljs-comment\"># define __init__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__init__'<\/span>, Data.init(class_obj))\n\n        <span class=\"hljs-keyword\">return<\/span> class_obj\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">init<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_init<\/span><span class=\"hljs-params\">(self, *obj_args, **obj_kwargs)<\/span>:<\/span>\n            <span class=\"hljs-keyword\">if<\/span> obj_kwargs:\n                <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props:\n                    <span class=\"hljs-keyword\">if<\/span> prop <span class=\"hljs-keyword\">in<\/span> obj_kwargs.keys():\n                        setattr(self, prop, obj_kwargs&#91;prop])\n\n            <span class=\"hljs-keyword\">if<\/span> obj_args:\n                <span class=\"hljs-keyword\">for<\/span> kv <span class=\"hljs-keyword\">in<\/span> zip(class_obj.props, obj_args):\n                    setattr(self, kv&#91;<span class=\"hljs-number\">0<\/span>], kv&#91;<span class=\"hljs-number\">1<\/span>])\n\n        <span class=\"hljs-keyword\">return<\/span> _init\n\n    <span class=\"hljs-comment\"># more methods<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The following creates a new instance of the <code>Person<\/code> class and initialize its attributes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">p = Person(<span class=\"hljs-string\">'John Doe'<\/span>, age=<span class=\"hljs-number\">25<\/span>)\nprint(p.__dict__)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Output:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">{<span class=\"hljs-string\">'_age'<\/span>: <span class=\"hljs-number\">25<\/span>, <span class=\"hljs-string\">'_name'<\/span>: <span class=\"hljs-string\">'John Doe'<\/span>}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>p.__dict__<\/code> contains two attributes <code>_name<\/code> and <code>_age<\/code> based on the predefined names in the <code>props<\/code> list.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id='define-__repr__-method'>Define __repr__ method <a href=\"#define-__repr__-method\" class=\"anchor\" id=\"define-__repr__-method\" title=\"Anchor for Define __repr__ method\">#<\/a><\/h2>\n\n\n\n<p>The following defines the <code>repr<\/code> static method that returns a function and uses it for the <code>__repr__<\/code> attribute of the class object:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Data<\/span><span class=\"hljs-params\">(type)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__new__<\/span><span class=\"hljs-params\">(mcs, name, bases, class_dict)<\/span>:<\/span>\n        class_obj = super().__new__(mcs, name, bases, class_dict)\n\n        <span class=\"hljs-comment\"># create property<\/span>\n        Data.define_property(class_obj)\n\n        <span class=\"hljs-comment\"># define __init__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__init__'<\/span>, Data.init(class_obj))\n\n        <span class=\"hljs-comment\"># define __repr__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__repr__'<\/span>, Data.repr(class_obj))\n\n        <span class=\"hljs-keyword\">return<\/span> class_obj\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">repr<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_repr<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n            prop_values = (getattr(self, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props)\n            prop_key_values = (<span class=\"hljs-string\">f'<span class=\"hljs-subst\">{key}<\/span>=<span class=\"hljs-subst\">{value}<\/span>'<\/span> <span class=\"hljs-keyword\">for<\/span> key, value <span class=\"hljs-keyword\">in<\/span> zip(class_obj.props, prop_values))\n            prop_key_values_str = <span class=\"hljs-string\">', '<\/span>.join(prop_key_values)\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">f'<span class=\"hljs-subst\">{class_obj.__name__}<\/span>(<span class=\"hljs-subst\">{prop_key_values_str}<\/span>)'<\/span>\n\n        <span class=\"hljs-keyword\">return<\/span> _repr<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The following creates a new instance of the <code>Person<\/code> class and displays it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">p = Person(<span class=\"hljs-string\">'John Doe'<\/span>, age=<span class=\"hljs-number\">25<\/span>)\nprint(p)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Output:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">Person(name=John Doe, age=<span class=\"hljs-number\">25<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id='define-__eq__-and-__hash__-methods'>Define __eq__ and __hash__ methods <a href=\"#define-__eq__-and-__hash__-methods\" class=\"anchor\" id=\"define-__eq__-and-__hash__-methods\" title=\"Anchor for Define __eq__ and __hash__ methods\">#<\/a><\/h2>\n\n\n\n<p>The following defines the <code>eq<\/code> and <code>hash<\/code> methods and assigns them to the <code>__eq__<\/code> and <code>__hash__<\/code> of the class object of the metaclass:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Data<\/span><span class=\"hljs-params\">(type)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__new__<\/span><span class=\"hljs-params\">(mcs, name, bases, class_dict)<\/span>:<\/span>\n        class_obj = super().__new__(mcs, name, bases, class_dict)\n\n        <span class=\"hljs-comment\"># create property<\/span>\n        Data.define_property(class_obj)\n\n        <span class=\"hljs-comment\"># define __init__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__init__'<\/span>, Data.init(class_obj))\n\n        <span class=\"hljs-comment\"># define __repr__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__repr__'<\/span>, Data.repr(class_obj))\n\n        <span class=\"hljs-comment\"># define __eq__ &amp; __hash__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__eq__'<\/span>, Data.eq(class_obj))\n        setattr(class_obj, <span class=\"hljs-string\">'__hash__'<\/span>, Data.hash(class_obj))\n\n        <span class=\"hljs-keyword\">return<\/span> class_obj\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">eq<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_eq<\/span><span class=\"hljs-params\">(self, other)<\/span>:<\/span>\n            <span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-keyword\">not<\/span> isinstance(other, class_obj):\n                <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">False<\/span>\n\n            self_values = &#91;getattr(self, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props]\n            other_values = &#91;getattr(other, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> other.props]\n\n            <span class=\"hljs-keyword\">return<\/span> self_values == other_values\n\n        <span class=\"hljs-keyword\">return<\/span> _eq\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">hash<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_hash<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n            values = (getattr(self, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props)\n            <span class=\"hljs-keyword\">return<\/span> hash(tuple(values))\n\n        <span class=\"hljs-keyword\">return<\/span> _hash<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The following creates two instances of the Person and compares them. If the values of all properties are the same, they will be equal. Otherwise, they will not be equal:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">p1 = Person(<span class=\"hljs-string\">'John Doe'<\/span>, age=<span class=\"hljs-number\">25<\/span>)\np2 = Person(<span class=\"hljs-string\">'Jane Doe'<\/span>, age=<span class=\"hljs-number\">25<\/span>)\n\nprint(p1 == p2)  <span class=\"hljs-comment\"># False<\/span>\n\np2.name = <span class=\"hljs-string\">'John Doe'<\/span>\nprint(p1 == p2)  <span class=\"hljs-comment\"># True<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id='put-it-all-together'>Put it all together <a href=\"#put-it-all-together\" class=\"anchor\" id=\"put-it-all-together\" title=\"Anchor for Put it all together\">#<\/a><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from<\/span> pprint <span class=\"hljs-keyword\">import<\/span> pprint\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Prop<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, attr)<\/span>:<\/span>\n        self._attr = attr\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get<\/span><span class=\"hljs-params\">(self, obj)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> getattr(obj, self._attr)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">set<\/span><span class=\"hljs-params\">(self, obj, value)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> setattr(obj, self._attr, value)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete<\/span><span class=\"hljs-params\">(self, obj)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> delattr(obj, self._attr)\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Data<\/span><span class=\"hljs-params\">(type)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__new__<\/span><span class=\"hljs-params\">(mcs, name, bases, class_dict)<\/span>:<\/span>\n        class_obj = super().__new__(mcs, name, bases, class_dict)\n\n        <span class=\"hljs-comment\"># create property<\/span>\n        Data.define_property(class_obj)\n\n        <span class=\"hljs-comment\"># define __init__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__init__'<\/span>, Data.init(class_obj))\n\n        <span class=\"hljs-comment\"># define __repr__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__repr__'<\/span>, Data.repr(class_obj))\n\n        <span class=\"hljs-comment\"># define __eq__ &amp; __hash__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__eq__'<\/span>, Data.eq(class_obj))\n        setattr(class_obj, <span class=\"hljs-string\">'__hash__'<\/span>, Data.hash(class_obj))\n\n        <span class=\"hljs-keyword\">return<\/span> class_obj\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">eq<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_eq<\/span><span class=\"hljs-params\">(self, other)<\/span>:<\/span>\n            <span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-keyword\">not<\/span> isinstance(other, class_obj):\n                <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">False<\/span>\n\n            self_values = &#91;getattr(self, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props]\n            other_values = &#91;getattr(other, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> other.props]\n\n            <span class=\"hljs-keyword\">return<\/span> self_values == other_values\n\n        <span class=\"hljs-keyword\">return<\/span> _eq\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">hash<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_hash<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n            values = (getattr(self, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props)\n            <span class=\"hljs-keyword\">return<\/span> hash(tuple(values))\n\n        <span class=\"hljs-keyword\">return<\/span> _hash\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">repr<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_repr<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n            prop_values = (getattr(self, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props)\n            prop_key_values = (<span class=\"hljs-string\">f'<span class=\"hljs-subst\">{key}<\/span>=<span class=\"hljs-subst\">{value}<\/span>'<\/span> <span class=\"hljs-keyword\">for<\/span> key, value <span class=\"hljs-keyword\">in<\/span> zip(class_obj.props, prop_values))\n            prop_key_values_str = <span class=\"hljs-string\">', '<\/span>.join(prop_key_values)\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">f'<span class=\"hljs-subst\">{class_obj.__name__}<\/span>(<span class=\"hljs-subst\">{prop_key_values_str}<\/span>)'<\/span>\n\n        <span class=\"hljs-keyword\">return<\/span> _repr\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">init<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_init<\/span><span class=\"hljs-params\">(self, *obj_args, **obj_kwargs)<\/span>:<\/span>\n            <span class=\"hljs-keyword\">if<\/span> obj_kwargs:\n                <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props:\n                    <span class=\"hljs-keyword\">if<\/span> prop <span class=\"hljs-keyword\">in<\/span> obj_kwargs.keys():\n                        setattr(self, prop, obj_kwargs&#91;prop])\n\n            <span class=\"hljs-keyword\">if<\/span> obj_args:\n                <span class=\"hljs-keyword\">for<\/span> kv <span class=\"hljs-keyword\">in<\/span> zip(class_obj.props, obj_args):\n                    setattr(self, kv&#91;<span class=\"hljs-number\">0<\/span>], kv&#91;<span class=\"hljs-number\">1<\/span>])\n\n        <span class=\"hljs-keyword\">return<\/span> _init\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">define_property<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props:\n            attr = <span class=\"hljs-string\">f'_<span class=\"hljs-subst\">{prop}<\/span>'<\/span>\n            prop_obj = property(\n                fget=Prop(attr).get,\n                fset=Prop(attr).set,\n                fdel=Prop(attr).delete\n            )\n            setattr(class_obj, prop, prop_obj)\n\n        <span class=\"hljs-keyword\">return<\/span> class_obj\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Person<\/span><span class=\"hljs-params\">(metaclass=Data)<\/span>:<\/span>\n    props = &#91;<span class=\"hljs-string\">'name'<\/span>, <span class=\"hljs-string\">'age'<\/span>]\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">'__main__'<\/span>:\n    pprint(Person.__dict__)\n\n    p1 = Person(<span class=\"hljs-string\">'John Doe'<\/span>, age=<span class=\"hljs-number\">25<\/span>)\n    p2 = Person(<span class=\"hljs-string\">'Jane Doe'<\/span>, age=<span class=\"hljs-number\">25<\/span>)\n\n    print(p1 == p2)  <span class=\"hljs-comment\"># False<\/span>\n\n    p2.name = <span class=\"hljs-string\">'John Doe'<\/span>\n    print(p1 == p2)  <span class=\"hljs-comment\"># True<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><a href=\"https:\/\/www.pythontutorial.net\/playground\/?q=ZnJvbSBwcHJpbnQgaW1wb3J0IHBwcmludAoKCmNsYXNzIFByb3A6CiAgICBkZWYgX19pbml0X18oc2VsZiwgYXR0cik6CiAgICAgICAgc2VsZi5fYXR0ciA9IGF0dHIKCiAgICBkZWYgZ2V0KHNlbGYsIG9iaik6CiAgICAgICAgcmV0dXJuIGdldGF0dHIob2JqLCBzZWxmLl9hdHRyKQoKICAgIGRlZiBzZXQoc2VsZiwgb2JqLCB2YWx1ZSk6CiAgICAgICAgcmV0dXJuIHNldGF0dHIob2JqLCBzZWxmLl9hdHRyLCB2YWx1ZSkKCiAgICBkZWYgZGVsZXRlKHNlbGYsIG9iaik6CiAgICAgICAgcmV0dXJuIGRlbGF0dHIob2JqLCBzZWxmLl9hdHRyKQoKCmNsYXNzIERhdGEodHlwZSk6CiAgICBkZWYgX19uZXdfXyhtY3MsIG5hbWUsIGJhc2VzLCBjbGFzc19kaWN0KToKICAgICAgICBjbGFzc19vYmogPSBzdXBlcigpLl9fbmV3X18obWNzLCBuYW1lLCBiYXNlcywgY2xhc3NfZGljdCkKCiAgICAgICAgIyBjcmVhdGUgcHJvcGVydHkKICAgICAgICBEYXRhLmRlZmluZV9wcm9wZXJ0eShjbGFzc19vYmopCgogICAgICAgICMgZGVmaW5lIF9faW5pdF9fCiAgICAgICAgc2V0YXR0cihjbGFzc19vYmosICdfX2luaXRfXycsIERhdGEuaW5pdChjbGFzc19vYmopKQoKICAgICAgICAjIGRlZmluZSBfX3JlcHJfXwogICAgICAgIHNldGF0dHIoY2xhc3Nfb2JqLCAnX19yZXByX18nLCBEYXRhLnJlcHIoY2xhc3Nfb2JqKSkKCiAgICAgICAgIyBkZWZpbmUgX19lcV9fICYgX19oYXNoX18KICAgICAgICBzZXRhdHRyKGNsYXNzX29iaiwgJ19fZXFfXycsIERhdGEuZXEoY2xhc3Nfb2JqKSkKICAgICAgICBzZXRhdHRyKGNsYXNzX29iaiwgJ19faGFzaF9fJywgRGF0YS5oYXNoKGNsYXNzX29iaikpCgogICAgICAgIHJldHVybiBjbGFzc19vYmoKCiAgICBAc3RhdGljbWV0aG9kCiAgICBkZWYgZXEoY2xhc3Nfb2JqKToKICAgICAgICBkZWYgX2VxKHNlbGYsIG90aGVyKToKICAgICAgICAgICAgaWYgbm90IGlzaW5zdGFuY2Uob3RoZXIsIGNsYXNzX29iaik6CiAgICAgICAgICAgICAgICByZXR1cm4gRmFsc2UKCiAgICAgICAgICAgIHNlbGZfdmFsdWVzID0gW2dldGF0dHIoc2VsZiwgcHJvcCkgZm9yIHByb3AgaW4gY2xhc3Nfb2JqLnByb3BzXQogICAgICAgICAgICBvdGhlcl92YWx1ZXMgPSBbZ2V0YXR0cihvdGhlciwgcHJvcCkgZm9yIHByb3AgaW4gb3RoZXIucHJvcHNdCgogICAgICAgICAgICByZXR1cm4gc2VsZl92YWx1ZXMgPT0gb3RoZXJfdmFsdWVzCgogICAgICAgIHJldHVybiBfZXEKCiAgICBAc3RhdGljbWV0aG9kCiAgICBkZWYgaGFzaChjbGFzc19vYmopOgogICAgICAgIGRlZiBfaGFzaChzZWxmKToKICAgICAgICAgICAgdmFsdWVzID0gKGdldGF0dHIoc2VsZiwgcHJvcCkgZm9yIHByb3AgaW4gY2xhc3Nfb2JqLnByb3BzKQogICAgICAgICAgICByZXR1cm4gaGFzaCh0dXBsZSh2YWx1ZXMpKQoKICAgICAgICByZXR1cm4gX2hhc2gKCiAgICBAc3RhdGljbWV0aG9kCiAgICBkZWYgcmVwcihjbGFzc19vYmopOgogICAgICAgIGRlZiBfcmVwcihzZWxmKToKICAgICAgICAgICAgcHJvcF92YWx1ZXMgPSAoZ2V0YXR0cihzZWxmLCBwcm9wKSBmb3IgcHJvcCBpbiBjbGFzc19vYmoucHJvcHMpCiAgICAgICAgICAgIHByb3Bfa2V5X3ZhbHVlcyA9IChmJ3trZXl9PXt2YWx1ZX0nIGZvciBrZXksIHZhbHVlIGluIHppcChjbGFzc19vYmoucHJvcHMsIHByb3BfdmFsdWVzKSkKICAgICAgICAgICAgcHJvcF9rZXlfdmFsdWVzX3N0ciA9ICcsICcuam9pbihwcm9wX2tleV92YWx1ZXMpCiAgICAgICAgICAgIHJldHVybiBmJ3tjbGFzc19vYmouX19uYW1lX199KHtwcm9wX2tleV92YWx1ZXNfc3RyfSknCgogICAgICAgIHJldHVybiBfcmVwcgoKICAgIEBzdGF0aWNtZXRob2QKICAgIGRlZiBpbml0KGNsYXNzX29iaik6CiAgICAgICAgZGVmIF9pbml0KHNlbGYsICpvYmpfYXJncywgKipvYmpfa3dhcmdzKToKICAgICAgICAgICAgaWYgb2JqX2t3YXJnczoKICAgICAgICAgICAgICAgIGZvciBwcm9wIGluIGNsYXNzX29iai5wcm9wczoKICAgICAgICAgICAgICAgICAgICBpZiBwcm9wIGluIG9ial9rd2FyZ3Mua2V5cygpOgogICAgICAgICAgICAgICAgICAgICAgICBzZXRhdHRyKHNlbGYsIHByb3AsIG9ial9rd2FyZ3NbcHJvcF0pCgogICAgICAgICAgICBpZiBvYmpfYXJnczoKICAgICAgICAgICAgICAgIGZvciBrdiBpbiB6aXAoY2xhc3Nfb2JqLnByb3BzLCBvYmpfYXJncyk6CiAgICAgICAgICAgICAgICAgICAgc2V0YXR0cihzZWxmLCBrdlswXSwga3ZbMV0pCgogICAgICAgIHJldHVybiBfaW5pdAoKICAgIEBzdGF0aWNtZXRob2QKICAgIGRlZiBkZWZpbmVfcHJvcGVydHkoY2xhc3Nfb2JqKToKICAgICAgICBmb3IgcHJvcCBpbiBjbGFzc19vYmoucHJvcHM6CiAgICAgICAgICAgIGF0dHIgPSBmJ197cHJvcH0nCiAgICAgICAgICAgIHByb3Bfb2JqID0gcHJvcGVydHkoCiAgICAgICAgICAgICAgICBmZ2V0PVByb3AoYXR0cikuZ2V0LAogICAgICAgICAgICAgICAgZnNldD1Qcm9wKGF0dHIpLnNldCwKICAgICAgICAgICAgICAgIGZkZWw9UHJvcChhdHRyKS5kZWxldGUKICAgICAgICAgICAgKQogICAgICAgICAgICBzZXRhdHRyKGNsYXNzX29iaiwgcHJvcCwgcHJvcF9vYmopCgogICAgICAgIHJldHVybiBjbGFzc19vYmoKCgpjbGFzcyBQZXJzb24obWV0YWNsYXNzPURhdGEpOgogICAgcHJvcHMgPSBbJ25hbWUnLCAnYWdlJ10KCgppZiBfX25hbWVfXyA9PSAnX19tYWluX18nOgogICAgcHByaW50KFBlcnNvbi5fX2RpY3RfXykKCiAgICBwMSA9IFBlcnNvbignSm9obiBEb2UnLCBhZ2U9MjUpCiAgICBwMiA9IFBlcnNvbignSmFuZSBEb2UnLCBhZ2U9MjUpCgogICAgcHJpbnQocDEgPT0gcDIpICAjIEZhbHNlCgogICAgcDIubmFtZSA9ICdKb2huIERvZScKICAgIHByaW50KHAxID09IHAyKSAgIyBUcnVl\" target=\"_blank\" rel=\"noreferrer noopener\">Try it<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id='decorator'>Decorator <a href=\"#decorator\" class=\"anchor\" id=\"decorator\" title=\"Anchor for Decorator\">#<\/a><\/h2>\n\n\n\n<p>The following defines a class called <code>Employee<\/code> that uses the <code>Data<\/code> metaclass:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Employee<\/span><span class=\"hljs-params\">(metaclass=Data)<\/span>:<\/span>\n    props = &#91;<span class=\"hljs-string\">'name'<\/span>, <span class=\"hljs-string\">'job_title'<\/span>]\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">'__main__'<\/span>:\n    e = Employee(name=<span class=\"hljs-string\">'John Doe'<\/span>, job_title=<span class=\"hljs-string\">'Python Developer'<\/span>)\n    print(e)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Output:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">Employee(name=John Doe, job_title=Python Developer)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>It works as expected. However, specifying the metaclass is quite verbose. To improve this, you can use a function <a href=\"https:\/\/www.pythontutorial.net\/advanced-python\/python-decorators\/\">decorator<\/a>. <\/p>\n\n\n\n<p>First, define a function decorator that returns a new class which is an instance of the <code>Data<\/code> metaclass:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">data<\/span><span class=\"hljs-params\">(cls)<\/span>:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> Data(cls.__name__, cls.__bases__, dict(cls.__dict__))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Second, use the <code>@data<\/code> decorator for any class that uses the <code>Data<\/code> as the metaclass:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-meta\">@data<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Employee<\/span>:<\/span>\n    props = &#91;<span class=\"hljs-string\">'name'<\/span>, <span class=\"hljs-string\">'job_title'<\/span>]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The following shows the complete code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Prop<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, attr)<\/span>:<\/span>\n        self._attr = attr\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get<\/span><span class=\"hljs-params\">(self, obj)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> getattr(obj, self._attr)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">set<\/span><span class=\"hljs-params\">(self, obj, value)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> setattr(obj, self._attr, value)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete<\/span><span class=\"hljs-params\">(self, obj)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> delattr(obj, self._attr)\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Data<\/span><span class=\"hljs-params\">(type)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__new__<\/span><span class=\"hljs-params\">(mcs, name, bases, class_dict)<\/span>:<\/span>\n        class_obj = super().__new__(mcs, name, bases, class_dict)\n\n        <span class=\"hljs-comment\"># create property<\/span>\n        Data.define_property(class_obj)\n\n        <span class=\"hljs-comment\"># define __init__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__init__'<\/span>, Data.init(class_obj))\n\n        <span class=\"hljs-comment\"># define __repr__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__repr__'<\/span>, Data.repr(class_obj))\n\n        <span class=\"hljs-comment\"># define __eq__ &amp; __hash__<\/span>\n        setattr(class_obj, <span class=\"hljs-string\">'__eq__'<\/span>, Data.eq(class_obj))\n        setattr(class_obj, <span class=\"hljs-string\">'__hash__'<\/span>, Data.hash(class_obj))\n\n        <span class=\"hljs-keyword\">return<\/span> class_obj\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">eq<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_eq<\/span><span class=\"hljs-params\">(self, other)<\/span>:<\/span>\n            <span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-keyword\">not<\/span> isinstance(other, class_obj):\n                <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">False<\/span>\n\n            self_values = &#91;getattr(self, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props]\n            other_values = &#91;getattr(other, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> other.props]\n\n            <span class=\"hljs-keyword\">return<\/span> self_values == other_values\n\n        <span class=\"hljs-keyword\">return<\/span> _eq\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">hash<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_hash<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n            values = (getattr(self, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props)\n            <span class=\"hljs-keyword\">return<\/span> hash(tuple(values))\n\n        <span class=\"hljs-keyword\">return<\/span> _hash\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">repr<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_repr<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n            prop_values = (getattr(self, prop) <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props)\n            prop_key_values = (<span class=\"hljs-string\">f'<span class=\"hljs-subst\">{key}<\/span>=<span class=\"hljs-subst\">{value}<\/span>'<\/span> <span class=\"hljs-keyword\">for<\/span> key, value <span class=\"hljs-keyword\">in<\/span> zip(class_obj.props, prop_values))\n            prop_key_values_str = <span class=\"hljs-string\">', '<\/span>.join(prop_key_values)\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">f'<span class=\"hljs-subst\">{class_obj.__name__}<\/span>(<span class=\"hljs-subst\">{prop_key_values_str}<\/span>)'<\/span>\n\n        <span class=\"hljs-keyword\">return<\/span> _repr\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">init<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">_init<\/span><span class=\"hljs-params\">(self, *obj_args, **obj_kwargs)<\/span>:<\/span>\n            <span class=\"hljs-keyword\">if<\/span> obj_kwargs:\n                <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props:\n                    <span class=\"hljs-keyword\">if<\/span> prop <span class=\"hljs-keyword\">in<\/span> obj_kwargs.keys():\n                        setattr(self, prop, obj_kwargs&#91;prop])\n\n            <span class=\"hljs-keyword\">if<\/span> obj_args:\n                <span class=\"hljs-keyword\">for<\/span> kv <span class=\"hljs-keyword\">in<\/span> zip(class_obj.props, obj_args):\n                    setattr(self, kv&#91;<span class=\"hljs-number\">0<\/span>], kv&#91;<span class=\"hljs-number\">1<\/span>])\n\n        <span class=\"hljs-keyword\">return<\/span> _init\n\n<span class=\"hljs-meta\">    @staticmethod<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">define_property<\/span><span class=\"hljs-params\">(class_obj)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">for<\/span> prop <span class=\"hljs-keyword\">in<\/span> class_obj.props:\n            attr = <span class=\"hljs-string\">f'_<span class=\"hljs-subst\">{prop}<\/span>'<\/span>\n            prop_obj = property(\n                fget=Prop(attr).get,\n                fset=Prop(attr).set,\n                fdel=Prop(attr).delete\n            )\n            setattr(class_obj, prop, prop_obj)\n\n        <span class=\"hljs-keyword\">return<\/span> class_obj\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Person<\/span><span class=\"hljs-params\">(metaclass=Data)<\/span>:<\/span>\n    props = &#91;<span class=\"hljs-string\">'name'<\/span>, <span class=\"hljs-string\">'age'<\/span>]\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">data<\/span><span class=\"hljs-params\">(cls)<\/span>:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> Data(cls.__name__, cls.__bases__, dict(cls.__dict__))\n\n\n<span class=\"hljs-meta\">@data<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Employee<\/span>:<\/span>\n    props = &#91;<span class=\"hljs-string\">'name'<\/span>, <span class=\"hljs-string\">'job_title'<\/span>]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Python 3.7 provided a <code>@dataclass<\/code> decorator specified in the <a href=\"https:\/\/www.python.org\/dev\/peps\/pep-0557\/\" target=\"_blank\" rel=\"noreferrer noopener\">PEP 557<\/a> that has some features like the <code>Data<\/code> metaclass. Also, the <a href=\"https:\/\/www.pythontutorial.net\/python-oop\/python-dataclass\/\">dataclass<\/a> offers more features that help you save time when working with classes.<\/p>\n<div class=\"helpful-block-content\" data-title=\"\">\n\t<header>\n\t\t<div class=\"wth-question\">Was this tutorial helpful ?<\/div>\n\t\t<div class=\"wth-thumbs\">\n\t\t\t<button\n\t\t\t\tdata-post=\"2983\"\n\t\t\t\tdata-post-url=\"https:\/\/www.pythontutorial.net\/python-oop\/python-metaclass-example\/\"\n\t\t\t\tdata-post-title=\"Python Metaclass Example\"\n\t\t\t\tdata-response=\"1\"\n\t\t\t\tclass=\"wth-btn-rounded wth-yes-btn\"\n\t\t\t>\n\t\t\t\t<svg\n\t\t\t\t\txmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\tclass=\"feather feather-thumbs-up block w-full h-full\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3\"\n\t\t\t\t\t><\/path>\n\t\t\t\t<\/svg>\n\t\t\t\t<span class=\"sr-only\"> Yes <\/span>\n\t\t\t<\/button>\n\n\t\t\t<button\n\t\t\t\tdata-response=\"0\"\n\t\t\t\tdata-post=\"2983\"\n\t\t\t\tdata-post-url=\"https:\/\/www.pythontutorial.net\/python-oop\/python-metaclass-example\/\"\n\t\t\t\tdata-post-title=\"Python Metaclass Example\"\n\t\t\t\tclass=\"wth-btn-rounded wth-no-btn\"\n\t\t\t>\n\t\t\t\t<svg\n\t\t\t\t\txmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17\"\n\t\t\t\t\t><\/path>\n\t\t\t\t<\/svg>\n\t\t\t\t<span class=\"sr-only\"> No <\/span>\n\t\t\t<\/button>\n\t\t<\/div>\n\t<\/header>\n\n\t<div class=\"wth-form hidden\">\n\t\t<div class=\"wth-form-wrapper\">\n\t\t\t<div class=\"wth-title\"><\/div>\n\t\t\t<textarea class=\"wth-message\"><\/textarea>\n\t\t\t<input type=\"button\" name=\"wth-submit\" class=\"wth-btn wth-btn-submit\" id=\"wth-submit\" \/>\n\t\t\t<input type=\"button\" class=\"wth-btn wth-btn-cancel\" value=\"Cancel\" \/>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial, you&#8217;ll learn how to use a metaclass to create classes with many features.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":417,"menu_order":43,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2983","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages\/2983","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/comments?post=2983"}],"version-history":[{"count":1,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages\/2983\/revisions"}],"predecessor-version":[{"id":7183,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages\/2983\/revisions\/7183"}],"up":[{"embeddable":true,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages\/417"}],"wp:attachment":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/media?parent=2983"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}