Lably
Skip to content

Metaclass

Class that describes common behavior for classes

  • 16 min read
  • 6 chapters

Related on Lably

Through Object-oriented programming, Class (programming) and Syntax (programming languages)

Python example

In Python, the builtin class type is a metaclass. Consider this simple Python class:

<span id="mwKg"></span><span class="k" id="mwKw">class</span><span class="w" id="mwLA"> </span><span class="nc" id="mwLQ">Car</span><span class="p" id="mwLg">:</span> <span class="n" id="mwLw">make</span><span class="p" id="mwMA">:</span> <span class="nb" id="mwMQ">str</span> <span class="n" id="mwMg">model</span><span class="p" id="mwMw">:</span> <span class="nb" id="mwNA">str</span> <span class="n" id="mwNQ">year</span><span class="p" id="mwNg">:</span> <span class="nb" id="mwNw">int</span> <span class="n" id="mwOA">color</span><span class="p" id="mwOQ">:</span> <span class="nb" id="mwOg">str</span> <span class="k" id="mwOw">def</span><span class="w" id="mwPA"> </span><span class="fm" id="mwPQ">__init__</span><span class="p" id="mwPg">(</span><span class="bp" id="mwPw">self</span><span class="p" id="mwQA">,</span> <span class="n" id="mwQQ">make</span><span class="p" id="mwQg">:</span> <span class="nb" id="mwQw">str</span><span class="p" id="mwRA">,</span> <span class="n" id="mwRQ">model</span><span class="p" id="mwRg">:</span> <span class="nb" id="mwRw">str</span><span class="p" id="mwSA">,</span> <span class="n" id="mwSQ">year</span><span class="p" id="mwSg">:</span> <span class="nb" id="mwSw">int</span><span class="p" id="mwTA">,</span> <span class="n" id="mwTQ">color</span><span class="p" id="mwTg">:</span> <span class="nb" id="mwTw">str</span><span class="p" id="mwUA">)</span> <span class="o" id="mwUQ">-></span> <span class="kc" id="mwUg">None</span><span class="p" id="mwUw">:</span> <span class="bp" id="mwVA">self</span><span class="o" id="mwVQ">.</span><span class="n" id="mwVg">make</span> <span class="o" id="mwVw">=</span> <span class="n" id="mwWA">make</span> <span class="bp" id="mwWQ">self</span><span class="o" id="mwWg">.</span><span class="n" id="mwWw">model</span> <span class="o" id="mwXA">=</span> <span class="n" id="mwXQ">model</span> <span class="bp" id="mwXg">self</span><span class="o" id="mwXw">.</span><span class="n" id="mwYA">year</span> <span class="o" id="mwYQ">=</span> <span class="n" id="mwYg">year</span> <span class="bp" id="mwYw">self</span><span class="o" id="mwZA">.</span><span class="n" id="mwZQ">color</span> <span class="o" id="mwZg">=</span> <span class="n" id="mwZw">color</span> <span class="nd" id="mwaA">@property</span> <span class="k" id="mwaQ">def</span><span class="w" id="mwag"> </span><span class="nf" id="mwaw">description</span><span class="p" id="mwbA">(</span><span class="bp" id="mwbQ">self</span><span class="p" id="mwbg">)</span> <span class="o" id="mwbw">-></span> <span class="nb" id="mwcA">str</span><span class="p" id="mwcQ">:</span> <span class="w" id="mwcg"> </span><span class="sd" id="mwcw">"""</span> <span class="sd" id="mwdA"> Return a description of this car.</span> <span class="sd" id="mwdQ"> """</span> <span class="k" id="mwdg">return</span> <span class="sa" id="mwdw">f</span><span class="s2" id="mweA">"</span><span class="si" id="mweQ">{</span><span class="bp" id="mweg">self</span><span class="o" id="mwew">.</span><span class="n" id="mwfA">color</span><span class="si" id="mwfQ">}</span><span class="s2" id="mwfg"> </span><span class="si" id="mwfw">{</span><span class="bp" id="mwgA">self</span><span class="o" id="mwgQ">.</span><span class="n" id="mwgg">make</span><span class="si" id="mwgw">}</span><span class="s2" id="mwhA"> </span><span class="si" id="mwhQ">{</span><span class="bp" id="mwhg">self</span><span class="o" id="mwhw">.</span><span class="n" id="mwiA">model</span><span class="si" id="mwiQ">}</span><span class="s2" id="mwig">"</span>

At run time, Car itself is an instance of type. The source code of the Car class, shown above, does not include such details as the size in bytes of Car objects, their binary layout in memory, how they are allocated, that the __init__ method is automatically called each time a Car is created, and so on. These details come into play not only when a new Car object is created, but also each time any attribute of a Car is accessed. In languages without metaclasses, these details are defined by the language specification and can't be overridden. In Python, the metaclass - type - controls these details of Car's behavior. They can be overridden by using a different metaclass instead of type.

The above example contains some redundant code to do with the four attributes make, model, year, and color. It is possible to eliminate some of this redundancy using a custom metaclass. In Python, a metaclass is most easily defined as a subclass of type.

<span id="mwnw"></span><span class="kn" id="mwoA">from</span><span class="w" id="mwoQ"> </span><span class="nn" id="mwog">typing</span><span class="w" id="mwow"> </span><span class="kn" id="mwpA">import</span> <span class="n" id="mwpQ">Any</span> <span class="k" id="mwpg">class</span><span class="w" id="mwpw"> </span><span class="nc" id="mwqA">AttributeInitType</span><span class="p" id="mwqQ">(</span><span class="nb" id="mwqg">type</span><span class="p" id="mwqw">):</span> <span class="k" id="mwrA">def</span><span class="w" id="mwrQ"> </span><span class="fm" id="mwrg">__call__</span><span class="p" id="mwrw">(</span><span class="bp" id="mwsA">self</span><span class="p" id="mwsQ">,</span> <span class="o" id="mwsg">*</span><span class="n" id="mwsw">args</span><span class="p" id="mwtA">:</span> <span class="nb" id="mwtQ">tuple</span><span class="p" id="mwtg">[</span><span class="n" id="mwtw">Any</span><span class="p" id="mwuA">],</span> <span class="o" id="mwuQ">**</span><span class="n" id="mwug">kwargs</span><span class="p" id="mwuw">:</span> <span class="nb" id="mwvA">dict</span><span class="p" id="mwvQ">[</span><span class="nb" id="mwvg">str</span><span class="p" id="mwvw">,</span> <span class="n" id="mwwA">Any</span><span class="p" id="mwwQ">])</span> <span class="o" id="mwwg">-></span> <span class="nb" id="mwww">object</span><span class="p" id="mwxA">:</span> <span class="w" id="mwxQ"> </span><span class="sd" id="mwxg">"""</span> <span class="sd" id="mwxw"> Create a new instance.</span> <span class="sd" id="mwyA"> """</span> <span class="c1" id="mwyQ"># First, create the object in the normal default way.</span> <span class="n" id="mwyg">obj</span><span class="p" id="mwyw">:</span> <span class="nb" id="mwzA">object</span> <span class="o" id="mwzQ">=</span> <span class="nb" id="mwzg">type</span><span class="o" id="mwzw">.</span><span class="fm" id="mw0A">__call__</span><span class="p" id="mw0Q">(</span><span class="bp" id="mw0g">self</span><span class="p" id="mw0w">,</span> <span class="o" id="mw1A">*</span><span class="n" id="mw1Q">args</span><span class="p" id="mw1g">)</span> <span class="c1" id="mw1w"># Additionally, set attributes on the new object.</span> <span class="k" id="mw2A">for</span> <span class="n" id="mw2Q">name</span><span class="p" id="mw2g">,</span> <span class="n" id="mw2w">value</span> <span class="ow" id="mw3A">in</span> <span class="n" id="mw3Q">kwargs</span><span class="o" id="mw3g">.</span><span class="n" id="mw3w">items</span><span class="p" id="mw4A">():</span> <span class="nb" id="mw4Q">setattr</span><span class="p" id="mw4g">(</span><span class="n" id="mw4w">obj</span><span class="p" id="mw5A">,</span> <span class="n" id="mw5Q">name</span><span class="p" id="mw5g">,</span> <span class="n" id="mw5w">value</span><span class="p" id="mw6A">)</span> <span class="c1" id="mw6Q"># Return the new object.</span> <span class="k" id="mw6g">return</span> <span class="n" id="mw6w">obj</span>

This metaclass only overrides object creation. All other aspects of class and object behavior are still handled by type.

Now the class Car can be rewritten to use this metaclass. In Python 3 this is done by providing a "keyword argument" metaclass to the class definition:

<span id="mw8w"></span><span class="k" id="mw9A">class</span><span class="w" id="mw9Q"> </span><span class="nc" id="mw9g">Car</span><span class="p" id="mw9w">(</span><span class="nb" id="mw-A">object</span><span class="p" id="mw-Q">,</span> <span class="n" id="mw-g">metaclass</span><span class="o" id="mw-w">=</span><span class="n" id="mw_A">AttributeInitType</span><span class="p" id="mw_Q">):</span> <span class="nd" id="mw_g">@property</span> <span class="k" id="mw_w">def</span><span class="w" id="mwAQA"> </span><span class="nf" id="mwAQE">description</span><span class="p" id="mwAQI">(</span><span class="bp" id="mwAQM">self</span><span class="p" id="mwAQQ">)</span> <span class="o" id="mwAQU">-></span> <span class="nb" id="mwAQY">str</span><span class="p" id="mwAQc">:</span> <span class="w" id="mwAQg"> </span><span class="sd" id="mwAQk">"""</span> <span class="sd" id="mwAQo"> Return a description of this car.</span> <span class="sd" id="mwAQs"> """</span> <span class="k" id="mwAQw">return</span> <span class="s2" id="mwAQ0">" "</span><span class="o" id="mwAQ4">.</span><span class="n" id="mwAQ8">join</span><span class="p" id="mwARA">(</span><span class="nb" id="mwARE">str</span><span class="p" id="mwARI">(</span><span class="n" id="mwARM">value</span><span class="p" id="mwARQ">)</span> <span class="k" id="mwARU">for</span> <span class="n" id="mwARY">value</span> <span class="ow" id="mwARc">in</span> <span class="bp" id="mwARg">self</span><span class="o" id="mwARk">.</span><span class="vm" id="mwARo">__dict__</span><span class="o" id="mwARs">.</span><span class="n" id="mwARw">values</span><span class="p" id="mwAR0">())</span>

The resulting object Car can be instantiated as usual, but can contain any number of keyword arguments:

<span id="mwASI"></span><span class="n" id="mwASM">new_car</span><span class="p" id="mwASQ">:</span> <span class="n" id="mwASU">Car</span> <span class="o" id="mwASY">=</span> <span class="n" id="mwASc">Car</span><span class="p" id="mwASg">(</span><span class="n" id="mwASk">make</span><span class="o" id="mwASo">=</span><span class="s1" id="mwASs">'Toyota'</span><span class="p" id="mwASw">,</span> <span class="n" id="mwAS0">model</span><span class="o" id="mwAS4">=</span><span class="s1" id="mwAS8">'Prius'</span><span class="p" id="mwATA">,</span> <span class="n" id="mwATE">year</span><span class="o" id="mwATI">=</span><span class="mi" id="mwATM">2005</span><span class="p" id="mwATQ">,</span> <span class="n" id="mwATU">color</span><span class="o" id="mwATY">=</span><span class="s1" id="mwATc">'Green'</span><span class="p" id="mwATg">,</span> <span class="n" id="mwATk">engine</span><span class="o" id="mwATo">=</span><span class="s1" id="mwATs">'Hybrid'</span><span class="p" id="mwATw">)</span>

Sign in to keep reading

5 more chapters are free with a Lably account.

More in Computing and communication