1090 lines
115 KiB
HTML
1090 lines
115 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>PEP 557 – Data Classes | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0557/">
|
||
<link rel="stylesheet" href="../_static/style.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
|
||
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
|
||
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
|
||
<meta property="og:title" content='PEP 557 – Data Classes | peps.python.org'>
|
||
<meta property="og:description" content="This PEP describes an addition to the standard library called Data Classes. Although they use a very different mechanism, Data Classes can be thought of as “mutable namedtuples with defaults”. Because Data Classes use normal class definition syntax, y...">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0557/">
|
||
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
||
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
||
<meta property="og:image:alt" content="Python PEPs">
|
||
<meta property="og:image:width" content="200">
|
||
<meta property="og:image:height" content="200">
|
||
<meta name="description" content="This PEP describes an addition to the standard library called Data Classes. Although they use a very different mechanism, Data Classes can be thought of as “mutable namedtuples with defaults”. Because Data Classes use normal class definition syntax, y...">
|
||
<meta name="theme-color" content="#3776ab">
|
||
</head>
|
||
<body>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Following system colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="9"></circle>
|
||
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected dark colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected light colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="5"></circle>
|
||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||
</svg>
|
||
</symbol>
|
||
</svg>
|
||
<script>
|
||
|
||
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
|
||
</script>
|
||
<section id="pep-page-section">
|
||
<header>
|
||
<h1>Python Enhancement Proposals</h1>
|
||
<ul class="breadcrumbs">
|
||
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 557</li>
|
||
</ul>
|
||
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
|
||
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
|
||
</button>
|
||
</header>
|
||
<article>
|
||
<section id="pep-content">
|
||
<h1 class="page-title">PEP 557 – Data Classes</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Eric V. Smith <eric at trueblade.com></dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
|
||
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-even">Created<span class="colon">:</span></dt>
|
||
<dd class="field-even">02-Jun-2017</dd>
|
||
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-odd">3.7</dd>
|
||
<dt class="field-even">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-even">08-Sep-2017, 25-Nov-2017, 30-Nov-2017, 01-Dec-2017, 02-Dec-2017, 06-Jan-2018, 04-Mar-2018</dd>
|
||
<dt class="field-odd">Resolution<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-December/151034.html">Python-Dev message</a></dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#notice-for-reviewers">Notice for Reviewers</a></li>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a></li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#field-objects"><code class="docutils literal notranslate"><span class="pre">Field</span></code> objects</a></li>
|
||
<li><a class="reference internal" href="#post-init-processing">post-init processing</a></li>
|
||
<li><a class="reference internal" href="#class-variables">Class variables</a></li>
|
||
<li><a class="reference internal" href="#init-only-variables">Init-only variables</a></li>
|
||
<li><a class="reference internal" href="#frozen-instances">Frozen instances</a></li>
|
||
<li><a class="reference internal" href="#inheritance">Inheritance</a></li>
|
||
<li><a class="reference internal" href="#default-factory-functions">Default factory functions</a></li>
|
||
<li><a class="reference internal" href="#mutable-default-values">Mutable default values</a></li>
|
||
<li><a class="reference internal" href="#module-level-helper-functions">Module level helper functions</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#discussion">Discussion</a><ul>
|
||
<li><a class="reference internal" href="#python-ideas-discussion">python-ideas discussion</a></li>
|
||
<li><a class="reference internal" href="#support-for-automatically-setting-slots">Support for automatically setting <code class="docutils literal notranslate"><span class="pre">__slots__</span></code>?</a></li>
|
||
<li><a class="reference internal" href="#why-not-just-use-namedtuple">Why not just use namedtuple?</a></li>
|
||
<li><a class="reference internal" href="#why-not-just-use-typing-namedtuple">Why not just use typing.NamedTuple?</a></li>
|
||
<li><a class="reference internal" href="#why-not-just-use-attrs">Why not just use attrs?</a></li>
|
||
<li><a class="reference internal" href="#post-init-parameters">post-init parameters</a></li>
|
||
<li><a class="reference internal" href="#asdict-and-astuple-function-names">asdict and astuple function names</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected ideas</a><ul>
|
||
<li><a class="reference internal" href="#copying-init-false-fields-after-new-object-creation-in-replace">Copying <code class="docutils literal notranslate"><span class="pre">init=False</span></code> fields after new object creation in replace()</a></li>
|
||
<li><a class="reference internal" href="#automatically-support-mutable-default-values">Automatically support mutable default values</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#examples">Examples</a><ul>
|
||
<li><a class="reference internal" href="#custom-init-method">Custom __init__ method</a></li>
|
||
<li><a class="reference internal" href="#a-complicated-example">A complicated example</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="notice-for-reviewers">
|
||
<h2><a class="toc-backref" href="#notice-for-reviewers" role="doc-backlink">Notice for Reviewers</a></h2>
|
||
<p>This PEP and the initial implementation were drafted in a separate
|
||
repo: <a class="reference external" href="https://github.com/ericvsmith/dataclasses">https://github.com/ericvsmith/dataclasses</a>. Before commenting in
|
||
a public forum please at least read the <a class="reference internal" href="#discussion">discussion</a> listed at the
|
||
end of this PEP.</p>
|
||
</section>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>This PEP describes an addition to the standard library called Data
|
||
Classes. Although they use a very different mechanism, Data Classes
|
||
can be thought of as “mutable namedtuples with defaults”. Because
|
||
Data Classes use normal class definition syntax, you are free to use
|
||
inheritance, metaclasses, docstrings, user-defined methods, class
|
||
factories, and other Python class features.</p>
|
||
<p>A class decorator is provided which inspects a class definition for
|
||
variables with type annotations as defined in <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a>, “Syntax for
|
||
Variable Annotations”. In this document, such variables are called
|
||
fields. Using these fields, the decorator adds generated method
|
||
definitions to the class to support instance initialization, a repr,
|
||
comparison methods, and optionally other methods as described in the
|
||
<a class="reference internal" href="#specification">Specification</a> section. Such a class is called a Data Class, but
|
||
there’s really nothing special about the class: the decorator adds
|
||
generated methods to the class and returns the same class it was
|
||
given.</p>
|
||
<p>As an example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">InventoryItem</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">'''Class for keeping track of an item in inventory.'''</span>
|
||
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">unit_price</span><span class="p">:</span> <span class="nb">float</span>
|
||
<span class="n">quantity_on_hand</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">total_cost</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">unit_price</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">quantity_on_hand</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">@dataclass</span></code> decorator will add the equivalent of these methods
|
||
to the InventoryItem class:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">unit_price</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="n">quantity_on_hand</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">unit_price</span> <span class="o">=</span> <span class="n">unit_price</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">quantity_on_hand</span> <span class="o">=</span> <span class="n">quantity_on_hand</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="sa">f</span><span class="s1">'InventoryItem(name=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">!r}</span><span class="s1">, unit_price=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">unit_price</span><span class="si">!r}</span><span class="s1">, quantity_on_hand=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="si">!r}</span><span class="s1">)'</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">other</span><span class="o">.</span><span class="vm">__class__</span> <span class="ow">is</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">other</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="bp">NotImplemented</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__ne__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">other</span><span class="o">.</span><span class="vm">__class__</span> <span class="ow">is</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span> <span class="o">!=</span> <span class="p">(</span><span class="n">other</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="bp">NotImplemented</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__lt__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">other</span><span class="o">.</span><span class="vm">__class__</span> <span class="ow">is</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span> <span class="o"><</span> <span class="p">(</span><span class="n">other</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="bp">NotImplemented</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__le__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">other</span><span class="o">.</span><span class="vm">__class__</span> <span class="ow">is</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span> <span class="o"><=</span> <span class="p">(</span><span class="n">other</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="bp">NotImplemented</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__gt__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">other</span><span class="o">.</span><span class="vm">__class__</span> <span class="ow">is</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span> <span class="o">></span> <span class="p">(</span><span class="n">other</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="bp">NotImplemented</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__ge__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">other</span><span class="o">.</span><span class="vm">__class__</span> <span class="ow">is</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span> <span class="o">>=</span> <span class="p">(</span><span class="n">other</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">unit_price</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">quantity_on_hand</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="bp">NotImplemented</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Data Classes save you from writing and maintaining these methods.</p>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<p>There have been numerous attempts to define classes which exist
|
||
primarily to store values which are accessible by attribute lookup.
|
||
Some examples include:</p>
|
||
<ul class="simple">
|
||
<li>collections.namedtuple in the standard library.</li>
|
||
<li>typing.NamedTuple in the standard library.</li>
|
||
<li>The popular attrs <a class="footnote-reference brackets" href="#id15" id="id1">[1]</a> project.</li>
|
||
<li>George Sakkis’ recordType recipe <a class="footnote-reference brackets" href="#id16" id="id2">[2]</a>, a mutable data type inspired
|
||
by collections.namedtuple.</li>
|
||
<li>Many example online recipes <a class="footnote-reference brackets" href="#id17" id="id3">[3]</a>, packages <a class="footnote-reference brackets" href="#id18" id="id4">[4]</a>, and questions <a class="footnote-reference brackets" href="#id19" id="id5">[5]</a>.
|
||
David Beazley used a form of data classes as the motivating example
|
||
in a PyCon 2013 metaclass talk <a class="footnote-reference brackets" href="#id20" id="id6">[6]</a>.</li>
|
||
</ul>
|
||
<p>So, why is this PEP needed?</p>
|
||
<p>With the addition of <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a>, Python has a concise way to specify the
|
||
type of class members. This PEP leverages that syntax to provide a
|
||
simple, unobtrusive way to describe Data Classes. With two exceptions,
|
||
the specified attribute type annotation is completely ignored by Data
|
||
Classes.</p>
|
||
<p>No base classes or metaclasses are used by Data Classes. Users of
|
||
these classes are free to use inheritance and metaclasses without any
|
||
interference from Data Classes. The decorated classes are truly
|
||
“normal” Python classes. The Data Class decorator should not
|
||
interfere with any usage of the class.</p>
|
||
<p>One main design goal of Data Classes is to support static type
|
||
checkers. The use of <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a> syntax is one example of this, but so is
|
||
the design of the <code class="docutils literal notranslate"><span class="pre">fields()</span></code> function and the <code class="docutils literal notranslate"><span class="pre">@dataclass</span></code>
|
||
decorator. Due to their very dynamic nature, some of the libraries
|
||
mentioned above are difficult to use with static type checkers.</p>
|
||
<p>Data Classes are not, and are not intended to be, a replacement
|
||
mechanism for all of the above libraries. But being in the standard
|
||
library will allow many of the simpler use cases to instead leverage
|
||
Data Classes. Many of the libraries listed have different feature
|
||
sets, and will of course continue to exist and prosper.</p>
|
||
<p>Where is it not appropriate to use Data Classes?</p>
|
||
<ul class="simple">
|
||
<li>API compatibility with tuples or dicts is required.</li>
|
||
<li>Type validation beyond that provided by PEPs 484 and 526 is
|
||
required, or value validation or conversion is required.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="specification">
|
||
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
|
||
<p>All of the functions described in this PEP will live in a module named
|
||
<code class="docutils literal notranslate"><span class="pre">dataclasses</span></code>.</p>
|
||
<p>A function <code class="docutils literal notranslate"><span class="pre">dataclass</span></code> which is typically used as a class decorator
|
||
is provided to post-process classes and add generated methods,
|
||
described below.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">dataclass</span></code> decorator examines the class to find <code class="docutils literal notranslate"><span class="pre">field</span></code>s. A
|
||
<code class="docutils literal notranslate"><span class="pre">field</span></code> is defined as any variable identified in
|
||
<code class="docutils literal notranslate"><span class="pre">__annotations__</span></code>. That is, a variable that has a type annotation.
|
||
With two exceptions described below, none of the Data Class machinery
|
||
examines the type specified in the annotation.</p>
|
||
<p>Note that <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> is guaranteed to be an ordered mapping,
|
||
in class declaration order. The order of the fields in all of the
|
||
generated methods is the order in which they appear in the class.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">dataclass</span></code> decorator will add various “dunder” methods to the
|
||
class, described below. If any of the added methods already exist on the
|
||
class, a <code class="docutils literal notranslate"><span class="pre">TypeError</span></code> will be raised. The decorator returns the same
|
||
class that is called on: no new class is created.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">dataclass</span></code> decorator is typically used with no parameters and
|
||
no parentheses. However, it also supports the following logical
|
||
signature:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">dataclass</span><span class="p">(</span><span class="o">*</span><span class="p">,</span> <span class="n">init</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">eq</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">order</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">unsafe_hash</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">frozen</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If <code class="docutils literal notranslate"><span class="pre">dataclass</span></code> is used just as a simple decorator with no
|
||
parameters, it acts as if it has the default values documented in this
|
||
signature. That is, these three uses of <code class="docutils literal notranslate"><span class="pre">@dataclass</span></code> are equivalent:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="nd">@dataclass</span><span class="p">()</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="nd">@dataclass</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">eq</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">order</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">unsafe_hash</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">frozen</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The parameters to <code class="docutils literal notranslate"><span class="pre">dataclass</span></code> are:</p>
|
||
<ul>
|
||
<li><code class="docutils literal notranslate"><span class="pre">init</span></code>: If true (the default), a <code class="docutils literal notranslate"><span class="pre">__init__</span></code> method will be
|
||
generated.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">repr</span></code>: If true (the default), a <code class="docutils literal notranslate"><span class="pre">__repr__</span></code> method will be
|
||
generated. The generated repr string will have the class name and
|
||
the name and repr of each field, in the order they are defined in
|
||
the class. Fields that are marked as being excluded from the repr
|
||
are not included. For example:
|
||
<code class="docutils literal notranslate"><span class="pre">InventoryItem(name='widget',</span> <span class="pre">unit_price=3.0,</span> <span class="pre">quantity_on_hand=10)</span></code>.<p>If the class already defines <code class="docutils literal notranslate"><span class="pre">__repr__</span></code>, this parameter is
|
||
ignored.</p>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">eq</span></code>: If true (the default), an <code class="docutils literal notranslate"><span class="pre">__eq__</span></code> method will be
|
||
generated. This method compares the class as if it were a tuple of its
|
||
fields, in order. Both instances in the comparison must be of the
|
||
identical type.<p>If the class already defines <code class="docutils literal notranslate"><span class="pre">__eq__</span></code>, this parameter is ignored.</p>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">order</span></code>: If true (the default is False), <code class="docutils literal notranslate"><span class="pre">__lt__</span></code>, <code class="docutils literal notranslate"><span class="pre">__le__</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">__gt__</span></code>, and <code class="docutils literal notranslate"><span class="pre">__ge__</span></code> methods will be generated. These compare
|
||
the class as if it were a tuple of its fields, in order. Both
|
||
instances in the comparison must be of the identical type. If
|
||
<code class="docutils literal notranslate"><span class="pre">order</span></code> is true and <code class="docutils literal notranslate"><span class="pre">eq</span></code> is false, a <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> is raised.<p>If the class already defines any of <code class="docutils literal notranslate"><span class="pre">__lt__</span></code>, <code class="docutils literal notranslate"><span class="pre">__le__</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">__gt__</span></code>, or <code class="docutils literal notranslate"><span class="pre">__ge__</span></code>, then <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> is raised.</p>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">unsafe_hash</span></code>: If <code class="docutils literal notranslate"><span class="pre">False</span></code> (the default), the <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> method
|
||
is generated according to how <code class="docutils literal notranslate"><span class="pre">eq</span></code> and <code class="docutils literal notranslate"><span class="pre">frozen</span></code> are set.<p>If <code class="docutils literal notranslate"><span class="pre">eq</span></code> and <code class="docutils literal notranslate"><span class="pre">frozen</span></code> are both true, Data Classes will generate a
|
||
<code class="docutils literal notranslate"><span class="pre">__hash__</span></code> method for you. If <code class="docutils literal notranslate"><span class="pre">eq</span></code> is true and <code class="docutils literal notranslate"><span class="pre">frozen</span></code> is
|
||
false, <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> will be set to <code class="docutils literal notranslate"><span class="pre">None</span></code>, marking it unhashable
|
||
(which it is). If <code class="docutils literal notranslate"><span class="pre">eq</span></code> is false, <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> will be left
|
||
untouched meaning the <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> method of the superclass will be
|
||
used (if the superclass is <code class="docutils literal notranslate"><span class="pre">object</span></code>, this means it will fall back
|
||
to id-based hashing).</p>
|
||
<p>Although not recommended, you can force Data Classes to create a
|
||
<code class="docutils literal notranslate"><span class="pre">__hash__</span></code> method with <code class="docutils literal notranslate"><span class="pre">unsafe_hash=True</span></code>. This might be the
|
||
case if your class is logically immutable but can nonetheless be
|
||
mutated. This is a specialized use case and should be considered
|
||
carefully.</p>
|
||
<p>If a class already has an explicitly defined <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> the
|
||
behavior when adding <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> is modified. An explicitly
|
||
defined <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> is defined when:</p>
|
||
<blockquote>
|
||
<div><ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">__eq__</span></code> is defined in the class and <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> is defined
|
||
with any value other than <code class="docutils literal notranslate"><span class="pre">None</span></code>.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">__eq__</span></code> is defined in the class and any non-<code class="docutils literal notranslate"><span class="pre">None</span></code>
|
||
<code class="docutils literal notranslate"><span class="pre">__hash__</span></code> is defined.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">__eq__</span></code> is not defined on the class, and any <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> is
|
||
defined.</li>
|
||
</ul>
|
||
</div></blockquote>
|
||
<p>If <code class="docutils literal notranslate"><span class="pre">unsafe_hash</span></code> is true and an explicitly defined <code class="docutils literal notranslate"><span class="pre">__hash__</span></code>
|
||
is present, then <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> is raised.</p>
|
||
<p>If <code class="docutils literal notranslate"><span class="pre">unsafe_hash</span></code> is false and an explicitly defined <code class="docutils literal notranslate"><span class="pre">__hash__</span></code>
|
||
is present, then no <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> is added.</p>
|
||
<p>See the Python documentation <a class="footnote-reference brackets" href="#id21" id="id7">[7]</a> for more information.</p>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">frozen</span></code>: If true (the default is False), assigning to fields will
|
||
generate an exception. This emulates read-only frozen instances.
|
||
If either <code class="docutils literal notranslate"><span class="pre">__getattr__</span></code> or <code class="docutils literal notranslate"><span class="pre">__setattr__</span></code> is defined in the
|
||
class, then <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> is raised. See the discussion below.</li>
|
||
</ul>
|
||
<p><code class="docutils literal notranslate"><span class="pre">field</span></code>s may optionally specify a default value, using normal
|
||
Python syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">a</span><span class="p">:</span> <span class="nb">int</span> <span class="c1"># 'a' has no default value</span>
|
||
<span class="n">b</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># assign a default value for 'b'</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In this example, both <code class="docutils literal notranslate"><span class="pre">a</span></code> and <code class="docutils literal notranslate"><span class="pre">b</span></code> will be included in the added
|
||
<code class="docutils literal notranslate"><span class="pre">__init__</span></code> method, which will be defined as:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">):</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">TypeError</span></code> will be raised if a field without a default value
|
||
follows a field with a default value. This is true either when this
|
||
occurs in a single class, or as a result of class inheritance.</p>
|
||
<p>For common and simple use cases, no other functionality is required.
|
||
There are, however, some Data Class features that require additional
|
||
per-field information. To satisfy this need for additional
|
||
information, you can replace the default field value with a call to
|
||
the provided <code class="docutils literal notranslate"><span class="pre">field()</span></code> function. The signature of <code class="docutils literal notranslate"><span class="pre">field()</span></code> is:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">field</span><span class="p">(</span><span class="o">*</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">MISSING</span><span class="p">,</span> <span class="n">default_factory</span><span class="o">=</span><span class="n">MISSING</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||
<span class="nb">hash</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">init</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">compare</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">metadata</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">MISSING</span></code> value is a sentinel object used to detect if the
|
||
<code class="docutils literal notranslate"><span class="pre">default</span></code> and <code class="docutils literal notranslate"><span class="pre">default_factory</span></code> parameters are provided. This
|
||
sentinel is used because <code class="docutils literal notranslate"><span class="pre">None</span></code> is a valid value for <code class="docutils literal notranslate"><span class="pre">default</span></code>.</p>
|
||
<p>The parameters to <code class="docutils literal notranslate"><span class="pre">field()</span></code> are:</p>
|
||
<ul>
|
||
<li><code class="docutils literal notranslate"><span class="pre">default</span></code>: If provided, this will be the default value for this
|
||
field. This is needed because the <code class="docutils literal notranslate"><span class="pre">field</span></code> call itself replaces
|
||
the normal position of the default value.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">default_factory</span></code>: If provided, it must be a zero-argument
|
||
callable that will be called when a default value is needed for this
|
||
field. Among other purposes, this can be used to specify fields
|
||
with mutable default values, as discussed below. It is an error to
|
||
specify both <code class="docutils literal notranslate"><span class="pre">default</span></code> and <code class="docutils literal notranslate"><span class="pre">default_factory</span></code>.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">init</span></code>: If true (the default), this field is included as a
|
||
parameter to the generated <code class="docutils literal notranslate"><span class="pre">__init__</span></code> method.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">repr</span></code>: If true (the default), this field is included in the
|
||
string returned by the generated <code class="docutils literal notranslate"><span class="pre">__repr__</span></code> method.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">compare</span></code>: If True (the default), this field is included in the
|
||
generated equality and comparison methods (<code class="docutils literal notranslate"><span class="pre">__eq__</span></code>, <code class="docutils literal notranslate"><span class="pre">__gt__</span></code>,
|
||
et al.).</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">hash</span></code>: This can be a bool or <code class="docutils literal notranslate"><span class="pre">None</span></code>. If True, this field is
|
||
included in the generated <code class="docutils literal notranslate"><span class="pre">__hash__</span></code> method. If <code class="docutils literal notranslate"><span class="pre">None</span></code> (the
|
||
default), use the value of <code class="docutils literal notranslate"><span class="pre">compare</span></code>: this would normally be the
|
||
expected behavior. A field should be considered in the hash if
|
||
it’s used for comparisons. Setting this value to anything other
|
||
than <code class="docutils literal notranslate"><span class="pre">None</span></code> is discouraged.<p>One possible reason to set <code class="docutils literal notranslate"><span class="pre">hash=False</span></code> but <code class="docutils literal notranslate"><span class="pre">compare=True</span></code> would
|
||
be if a field is expensive to compute a hash value for, that field
|
||
is needed for equality testing, and there are other fields that
|
||
contribute to the type’s hash value. Even if a field is excluded
|
||
from the hash, it will still be used for comparisons.</p>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">metadata</span></code>: This can be a mapping or None. None is treated as an
|
||
empty dict. This value is wrapped in <code class="docutils literal notranslate"><span class="pre">types.MappingProxyType</span></code> to
|
||
make it read-only, and exposed on the Field object. It is not used
|
||
at all by Data Classes, and is provided as a third-party extension
|
||
mechanism. Multiple third-parties can each have their own key, to
|
||
use as a namespace in the metadata.</li>
|
||
</ul>
|
||
<p>If the default value of a field is specified by a call to <code class="docutils literal notranslate"><span class="pre">field()</span></code>,
|
||
then the class attribute for this field will be replaced by the
|
||
specified <code class="docutils literal notranslate"><span class="pre">default</span></code> value. If no <code class="docutils literal notranslate"><span class="pre">default</span></code> is provided, then the
|
||
class attribute will be deleted. The intent is that after the
|
||
<code class="docutils literal notranslate"><span class="pre">dataclass</span></code> decorator runs, the class attributes will all contain
|
||
the default values for the fields, just as if the default value itself
|
||
were specified. For example, after:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">y</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="n">z</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
|
||
<span class="n">t</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">20</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The class attribute <code class="docutils literal notranslate"><span class="pre">C.z</span></code> will be <code class="docutils literal notranslate"><span class="pre">10</span></code>, the class attribute
|
||
<code class="docutils literal notranslate"><span class="pre">C.t</span></code> will be <code class="docutils literal notranslate"><span class="pre">20</span></code>, and the class attributes <code class="docutils literal notranslate"><span class="pre">C.x</span></code> and <code class="docutils literal notranslate"><span class="pre">C.y</span></code>
|
||
will not be set.</p>
|
||
<section id="field-objects">
|
||
<h3><a class="toc-backref" href="#field-objects" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">Field</span></code> objects</a></h3>
|
||
<p><code class="docutils literal notranslate"><span class="pre">Field</span></code> objects describe each defined field. These objects are
|
||
created internally, and are returned by the <code class="docutils literal notranslate"><span class="pre">fields()</span></code> module-level
|
||
method (see below). Users should never instantiate a <code class="docutils literal notranslate"><span class="pre">Field</span></code>
|
||
object directly. Its documented attributes are:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">name</span></code>: The name of the field.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">type</span></code>: The type of the field.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">default</span></code>, <code class="docutils literal notranslate"><span class="pre">default_factory</span></code>, <code class="docutils literal notranslate"><span class="pre">init</span></code>, <code class="docutils literal notranslate"><span class="pre">repr</span></code>, <code class="docutils literal notranslate"><span class="pre">hash</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">compare</span></code>, and <code class="docutils literal notranslate"><span class="pre">metadata</span></code> have the identical meaning and values
|
||
as they do in the <code class="docutils literal notranslate"><span class="pre">field()</span></code> declaration.</li>
|
||
</ul>
|
||
<p>Other attributes may exist, but they are private and must not be
|
||
inspected or relied on.</p>
|
||
</section>
|
||
<section id="post-init-processing">
|
||
<h3><a class="toc-backref" href="#post-init-processing" role="doc-backlink">post-init processing</a></h3>
|
||
<p>The generated <code class="docutils literal notranslate"><span class="pre">__init__</span></code> code will call a method named
|
||
<code class="docutils literal notranslate"><span class="pre">__post_init__</span></code>, if it is defined on the class. It will be called
|
||
as <code class="docutils literal notranslate"><span class="pre">self.__post_init__()</span></code>. If no <code class="docutils literal notranslate"><span class="pre">__init__</span></code> method is generated,
|
||
then <code class="docutils literal notranslate"><span class="pre">__post_init__</span></code> will not automatically be called.</p>
|
||
<p>Among other uses, this allows for initializing field values that
|
||
depend on one or more other fields. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">a</span><span class="p">:</span> <span class="nb">float</span>
|
||
<span class="n">b</span><span class="p">:</span> <span class="nb">float</span>
|
||
<span class="n">c</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">__post_init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>See the section below on init-only variables for ways to pass
|
||
parameters to <code class="docutils literal notranslate"><span class="pre">__post_init__()</span></code>. Also see the warning about how
|
||
<code class="docutils literal notranslate"><span class="pre">replace()</span></code> handles <code class="docutils literal notranslate"><span class="pre">init=False</span></code> fields.</p>
|
||
</section>
|
||
<section id="class-variables">
|
||
<h3><a class="toc-backref" href="#class-variables" role="doc-backlink">Class variables</a></h3>
|
||
<p>One place where <code class="docutils literal notranslate"><span class="pre">dataclass</span></code> actually inspects the type of a field is
|
||
to determine if a field is a class variable as defined in <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a>. It
|
||
does this by checking if the type of the field is <code class="docutils literal notranslate"><span class="pre">typing.ClassVar</span></code>.
|
||
If a field is a <code class="docutils literal notranslate"><span class="pre">ClassVar</span></code>, it is excluded from consideration as a
|
||
field and is ignored by the Data Class mechanisms. For more
|
||
discussion, see <a class="footnote-reference brackets" href="#id22" id="id8">[8]</a>. Such <code class="docutils literal notranslate"><span class="pre">ClassVar</span></code> pseudo-fields are not
|
||
returned by the module-level <code class="docutils literal notranslate"><span class="pre">fields()</span></code> function.</p>
|
||
</section>
|
||
<section id="init-only-variables">
|
||
<h3><a class="toc-backref" href="#init-only-variables" role="doc-backlink">Init-only variables</a></h3>
|
||
<p>The other place where <code class="docutils literal notranslate"><span class="pre">dataclass</span></code> inspects a type annotation is to
|
||
determine if a field is an init-only variable. It does this by seeing
|
||
if the type of a field is of type <code class="docutils literal notranslate"><span class="pre">dataclasses.InitVar</span></code>. If a field
|
||
is an <code class="docutils literal notranslate"><span class="pre">InitVar</span></code>, it is considered a pseudo-field called an init-only
|
||
field. As it is not a true field, it is not returned by the
|
||
module-level <code class="docutils literal notranslate"><span class="pre">fields()</span></code> function. Init-only fields are added as
|
||
parameters to the generated <code class="docutils literal notranslate"><span class="pre">__init__</span></code> method, and are passed to
|
||
the optional <code class="docutils literal notranslate"><span class="pre">__post_init__</span></code> method. They are not otherwise used
|
||
by Data Classes.</p>
|
||
<p>For example, suppose a field will be initialized from a database, if a
|
||
value is not provided when creating the class:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">i</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">j</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">database</span><span class="p">:</span> <span class="n">InitVar</span><span class="p">[</span><span class="n">DatabaseType</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">__post_init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">database</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">database</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">j</span> <span class="o">=</span> <span class="n">database</span><span class="o">.</span><span class="n">lookup</span><span class="p">(</span><span class="s1">'j'</span><span class="p">)</span>
|
||
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">C</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">database</span><span class="o">=</span><span class="n">my_database</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In this case, <code class="docutils literal notranslate"><span class="pre">fields()</span></code> will return <code class="docutils literal notranslate"><span class="pre">Field</span></code> objects for <code class="docutils literal notranslate"><span class="pre">i</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">j</span></code>, but not for <code class="docutils literal notranslate"><span class="pre">database</span></code>.</p>
|
||
</section>
|
||
<section id="frozen-instances">
|
||
<h3><a class="toc-backref" href="#frozen-instances" role="doc-backlink">Frozen instances</a></h3>
|
||
<p>It is not possible to create truly immutable Python objects. However,
|
||
by passing <code class="docutils literal notranslate"><span class="pre">frozen=True</span></code> to the <code class="docutils literal notranslate"><span class="pre">@dataclass</span></code> decorator you can
|
||
emulate immutability. In that case, Data Classes will add
|
||
<code class="docutils literal notranslate"><span class="pre">__setattr__</span></code> and <code class="docutils literal notranslate"><span class="pre">__delattr__</span></code> methods to the class. These
|
||
methods will raise a <code class="docutils literal notranslate"><span class="pre">FrozenInstanceError</span></code> when invoked.</p>
|
||
<p>There is a tiny performance penalty when using <code class="docutils literal notranslate"><span class="pre">frozen=True</span></code>:
|
||
<code class="docutils literal notranslate"><span class="pre">__init__</span></code> cannot use simple assignment to initialize fields, and
|
||
must use <code class="docutils literal notranslate"><span class="pre">object.__setattr__</span></code>.</p>
|
||
</section>
|
||
<section id="inheritance">
|
||
<h3><a class="toc-backref" href="#inheritance" role="doc-backlink">Inheritance</a></h3>
|
||
<p>When the Data Class is being created by the <code class="docutils literal notranslate"><span class="pre">@dataclass</span></code> decorator,
|
||
it looks through all of the class’s base classes in reverse MRO (that
|
||
is, starting at <code class="docutils literal notranslate"><span class="pre">object</span></code>) and, for each Data Class that it finds,
|
||
adds the fields from that base class to an ordered mapping of fields.
|
||
After all of the base class fields are added, it adds its own fields
|
||
to the ordered mapping. All of the generated methods will use this
|
||
combined, calculated ordered mapping of fields. Because the fields
|
||
are in insertion order, derived classes override base classes. An
|
||
example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">Base</span><span class="p">:</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="n">Any</span> <span class="o">=</span> <span class="mf">15.0</span>
|
||
<span class="n">y</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
||
|
||
<span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="n">z</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">15</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The final list of fields is, in order, <code class="docutils literal notranslate"><span class="pre">x</span></code>, <code class="docutils literal notranslate"><span class="pre">y</span></code>, <code class="docutils literal notranslate"><span class="pre">z</span></code>. The final
|
||
type of <code class="docutils literal notranslate"><span class="pre">x</span></code> is <code class="docutils literal notranslate"><span class="pre">int</span></code>, as specified in class <code class="docutils literal notranslate"><span class="pre">C</span></code>.</p>
|
||
<p>The generated <code class="docutils literal notranslate"><span class="pre">__init__</span></code> method for <code class="docutils literal notranslate"><span class="pre">C</span></code> will look like:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">15</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">z</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">):</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="default-factory-functions">
|
||
<h3><a class="toc-backref" href="#default-factory-functions" role="doc-backlink">Default factory functions</a></h3>
|
||
<p>If a field specifies a <code class="docutils literal notranslate"><span class="pre">default_factory</span></code>, it is called with zero
|
||
arguments when a default value for the field is needed. For example,
|
||
to create a new instance of a list, use:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">l</span><span class="p">:</span> <span class="nb">list</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">default_factory</span><span class="o">=</span><span class="nb">list</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If a field is excluded from <code class="docutils literal notranslate"><span class="pre">__init__</span></code> (using <code class="docutils literal notranslate"><span class="pre">init=False</span></code>) and
|
||
the field also specifies <code class="docutils literal notranslate"><span class="pre">default_factory</span></code>, then the default factory
|
||
function will always be called from the generated <code class="docutils literal notranslate"><span class="pre">__init__</span></code>
|
||
function. This happens because there is no other way to give the
|
||
field an initial value.</p>
|
||
</section>
|
||
<section id="mutable-default-values">
|
||
<h3><a class="toc-backref" href="#mutable-default-values" role="doc-backlink">Mutable default values</a></h3>
|
||
<p>Python stores default member variable values in class attributes.
|
||
Consider this example, not using Data Classes:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">element</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">+=</span> <span class="n">element</span>
|
||
|
||
<span class="n">o1</span> <span class="o">=</span> <span class="n">C</span><span class="p">()</span>
|
||
<span class="n">o2</span> <span class="o">=</span> <span class="n">C</span><span class="p">()</span>
|
||
<span class="n">o1</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||
<span class="n">o2</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
|
||
<span class="k">assert</span> <span class="n">o1</span><span class="o">.</span><span class="n">x</span> <span class="o">==</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span>
|
||
<span class="k">assert</span> <span class="n">o1</span><span class="o">.</span><span class="n">x</span> <span class="ow">is</span> <span class="n">o2</span><span class="o">.</span><span class="n">x</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that the two instances of class <code class="docutils literal notranslate"><span class="pre">C</span></code> share the same class
|
||
variable <code class="docutils literal notranslate"><span class="pre">x</span></code>, as expected.</p>
|
||
<p>Using Data Classes, <em>if</em> this code was valid:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">D</span><span class="p">:</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="n">List</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">element</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">+=</span> <span class="n">element</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>it would generate code similar to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">D</span><span class="p">:</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="n">x</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">element</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">+=</span> <span class="n">element</span>
|
||
|
||
<span class="k">assert</span> <span class="n">D</span><span class="p">()</span><span class="o">.</span><span class="n">x</span> <span class="ow">is</span> <span class="n">D</span><span class="p">()</span><span class="o">.</span><span class="n">x</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This has the same issue as the original example using class <code class="docutils literal notranslate"><span class="pre">C</span></code>.
|
||
That is, two instances of class <code class="docutils literal notranslate"><span class="pre">D</span></code> that do not specify a value for
|
||
<code class="docutils literal notranslate"><span class="pre">x</span></code> when creating a class instance will share the same copy of
|
||
<code class="docutils literal notranslate"><span class="pre">x</span></code>. Because Data Classes just use normal Python class creation
|
||
they also share this problem. There is no general way for Data
|
||
Classes to detect this condition. Instead, Data Classes will raise a
|
||
<code class="docutils literal notranslate"><span class="pre">TypeError</span></code> if it detects a default parameter of type <code class="docutils literal notranslate"><span class="pre">list</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">dict</span></code>, or <code class="docutils literal notranslate"><span class="pre">set</span></code>. This is a partial solution, but it does protect
|
||
against many common errors. See <a class="reference internal" href="#automatically-support-mutable-default-values">Automatically support mutable
|
||
default values</a> in the Rejected Ideas section for more details.</p>
|
||
<p>Using default factory functions is a way to create new instances of
|
||
mutable types as default values for fields:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">D</span><span class="p">:</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="nb">list</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">default_factory</span><span class="o">=</span><span class="nb">list</span><span class="p">)</span>
|
||
|
||
<span class="k">assert</span> <span class="n">D</span><span class="p">()</span><span class="o">.</span><span class="n">x</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">D</span><span class="p">()</span><span class="o">.</span><span class="n">x</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="module-level-helper-functions">
|
||
<h3><a class="toc-backref" href="#module-level-helper-functions" role="doc-backlink">Module level helper functions</a></h3>
|
||
<ul>
|
||
<li><code class="docutils literal notranslate"><span class="pre">fields(class_or_instance)</span></code>: Returns a tuple of <code class="docutils literal notranslate"><span class="pre">Field</span></code> objects
|
||
that define the fields for this Data Class. Accepts either a Data
|
||
Class, or an instance of a Data Class. Raises <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> if not
|
||
passed a Data Class or instance of one. Does not return
|
||
pseudo-fields which are <code class="docutils literal notranslate"><span class="pre">ClassVar</span></code> or <code class="docutils literal notranslate"><span class="pre">InitVar</span></code>.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">asdict(instance,</span> <span class="pre">*,</span> <span class="pre">dict_factory=dict)</span></code>: Converts the Data Class
|
||
<code class="docutils literal notranslate"><span class="pre">instance</span></code> to a dict (by using the factory function
|
||
<code class="docutils literal notranslate"><span class="pre">dict_factory</span></code>). Each Data Class is converted to a dict of its
|
||
fields, as name:value pairs. Data Classes, dicts, lists, and tuples
|
||
are recursed into. For example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">Point</span><span class="p">:</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">y</span><span class="p">:</span> <span class="nb">int</span>
|
||
|
||
<span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">l</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Point</span><span class="p">]</span>
|
||
|
||
<span class="n">p</span> <span class="o">=</span> <span class="n">Point</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
|
||
<span class="k">assert</span> <span class="n">asdict</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="o">==</span> <span class="p">{</span><span class="s1">'x'</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="s1">'y'</span><span class="p">:</span> <span class="mi">20</span><span class="p">}</span>
|
||
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">C</span><span class="p">([</span><span class="n">Point</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">Point</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">4</span><span class="p">)])</span>
|
||
<span class="k">assert</span> <span class="n">asdict</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="o">==</span> <span class="p">{</span><span class="s1">'l'</span><span class="p">:</span> <span class="p">[{</span><span class="s1">'x'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">'y'</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span> <span class="p">{</span><span class="s1">'x'</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="s1">'y'</span><span class="p">:</span> <span class="mi">4</span><span class="p">}]}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Raises <code class="docutils literal notranslate"><span class="pre">TypeError</span></code> if <code class="docutils literal notranslate"><span class="pre">instance</span></code> is not a Data Class instance.</p>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">astuple(*,</span> <span class="pre">tuple_factory=tuple)</span></code>: Converts the Data Class
|
||
<code class="docutils literal notranslate"><span class="pre">instance</span></code> to a tuple (by using the factory function
|
||
<code class="docutils literal notranslate"><span class="pre">tuple_factory</span></code>). Each Data Class is converted to a tuple of its
|
||
field values. Data Classes, dicts, lists, and tuples are recursed
|
||
into.<p>Continuing from the previous example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">assert</span> <span class="n">astuple</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
|
||
<span class="k">assert</span> <span class="n">astuple</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="o">==</span> <span class="p">([(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">4</span><span class="p">)],)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Raises <code class="docutils literal notranslate"><span class="pre">TypeError</span></code> if <code class="docutils literal notranslate"><span class="pre">instance</span></code> is not a Data Class instance.</p>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">make_dataclass(cls_name,</span> <span class="pre">fields,</span> <span class="pre">*,</span> <span class="pre">bases=(),</span> <span class="pre">namespace=None)</span></code>:
|
||
Creates a new Data Class with name <code class="docutils literal notranslate"><span class="pre">cls_name</span></code>, fields as defined
|
||
in <code class="docutils literal notranslate"><span class="pre">fields</span></code>, base classes as given in <code class="docutils literal notranslate"><span class="pre">bases</span></code>, and initialized
|
||
with a namespace as given in <code class="docutils literal notranslate"><span class="pre">namespace</span></code>. <code class="docutils literal notranslate"><span class="pre">fields</span></code> is an
|
||
iterable whose elements are either <code class="docutils literal notranslate"><span class="pre">name</span></code>, <code class="docutils literal notranslate"><span class="pre">(name,</span> <span class="pre">type)</span></code>, or
|
||
<code class="docutils literal notranslate"><span class="pre">(name,</span> <span class="pre">type,</span> <span class="pre">Field)</span></code>. If just <code class="docutils literal notranslate"><span class="pre">name</span></code> is supplied,
|
||
<code class="docutils literal notranslate"><span class="pre">typing.Any</span></code> is used for <code class="docutils literal notranslate"><span class="pre">type</span></code>. This function is not strictly
|
||
required, because any Python mechanism for creating a new class with
|
||
<code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> can then apply the <code class="docutils literal notranslate"><span class="pre">dataclass</span></code> function to
|
||
convert that class to a Data Class. This function is provided as a
|
||
convenience. For example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">C</span> <span class="o">=</span> <span class="n">make_dataclass</span><span class="p">(</span><span class="s1">'C'</span><span class="p">,</span>
|
||
<span class="p">[(</span><span class="s1">'x'</span><span class="p">,</span> <span class="nb">int</span><span class="p">),</span>
|
||
<span class="s1">'y'</span><span class="p">,</span>
|
||
<span class="p">(</span><span class="s1">'z'</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="n">field</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">5</span><span class="p">))],</span>
|
||
<span class="n">namespace</span><span class="o">=</span><span class="p">{</span><span class="s1">'add_one'</span><span class="p">:</span> <span class="k">lambda</span> <span class="bp">self</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">})</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Is equivalent to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">y</span><span class="p">:</span> <span class="s1">'typing.Any'</span>
|
||
<span class="n">z</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">5</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">add_one</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">replace(instance,</span> <span class="pre">**changes)</span></code>: Creates a new object of the same
|
||
type of <code class="docutils literal notranslate"><span class="pre">instance</span></code>, replacing fields with values from <code class="docutils literal notranslate"><span class="pre">changes</span></code>.
|
||
If <code class="docutils literal notranslate"><span class="pre">instance</span></code> is not a Data Class, raises <code class="docutils literal notranslate"><span class="pre">TypeError</span></code>. If
|
||
values in <code class="docutils literal notranslate"><span class="pre">changes</span></code> do not specify fields, raises <code class="docutils literal notranslate"><span class="pre">TypeError</span></code>.<p>The newly returned object is created by calling the <code class="docutils literal notranslate"><span class="pre">__init__</span></code>
|
||
method of the Data Class. This ensures that
|
||
<code class="docutils literal notranslate"><span class="pre">__post_init__</span></code>, if present, is also called.</p>
|
||
<p>Init-only variables without default values, if any exist, must be
|
||
specified on the call to <code class="docutils literal notranslate"><span class="pre">replace</span></code> so that they can be passed to
|
||
<code class="docutils literal notranslate"><span class="pre">__init__</span></code> and <code class="docutils literal notranslate"><span class="pre">__post_init__</span></code>.</p>
|
||
<p>It is an error for <code class="docutils literal notranslate"><span class="pre">changes</span></code> to contain any fields that are
|
||
defined as having <code class="docutils literal notranslate"><span class="pre">init=False</span></code>. A <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> will be raised
|
||
in this case.</p>
|
||
<p>Be forewarned about how <code class="docutils literal notranslate"><span class="pre">init=False</span></code> fields work during a call to
|
||
<code class="docutils literal notranslate"><span class="pre">replace()</span></code>. They are not copied from the source object, but
|
||
rather are initialized in <code class="docutils literal notranslate"><span class="pre">__post_init__()</span></code>, if they’re
|
||
initialized at all. It is expected that <code class="docutils literal notranslate"><span class="pre">init=False</span></code> fields will
|
||
be rarely and judiciously used. If they are used, it might be wise
|
||
to have alternate class constructors, or perhaps a custom
|
||
<code class="docutils literal notranslate"><span class="pre">replace()</span></code> (or similarly named) method which handles instance
|
||
copying.</p>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">is_dataclass(class_or_instance)</span></code>: Returns True if its parameter
|
||
is a dataclass or an instance of one, otherwise returns False.<p>If you need to know if a class is an instance of a dataclass (and
|
||
not a dataclass itself), then add a further check for <code class="docutils literal notranslate"><span class="pre">not</span>
|
||
<span class="pre">isinstance(obj,</span> <span class="pre">type)</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">is_dataclass_instance</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">is_dataclass</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="nb">type</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
</section>
|
||
<section id="discussion">
|
||
<h2><a class="toc-backref" href="#discussion" role="doc-backlink">Discussion</a></h2>
|
||
<section id="python-ideas-discussion">
|
||
<h3><a class="toc-backref" href="#python-ideas-discussion" role="doc-backlink">python-ideas discussion</a></h3>
|
||
<p>This discussion started on python-ideas <a class="footnote-reference brackets" href="#id23" id="id9">[9]</a> and was moved to a GitHub
|
||
repo <a class="footnote-reference brackets" href="#id24" id="id10">[10]</a> for further discussion. As part of this discussion, we made
|
||
the decision to use <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a> syntax to drive the discovery of fields.</p>
|
||
</section>
|
||
<section id="support-for-automatically-setting-slots">
|
||
<h3><a class="toc-backref" href="#support-for-automatically-setting-slots" role="doc-backlink">Support for automatically setting <code class="docutils literal notranslate"><span class="pre">__slots__</span></code>?</a></h3>
|
||
<p>At least for the initial release, <code class="docutils literal notranslate"><span class="pre">__slots__</span></code> will not be supported.
|
||
<code class="docutils literal notranslate"><span class="pre">__slots__</span></code> needs to be added at class creation time. The Data
|
||
Class decorator is called after the class is created, so in order to
|
||
add <code class="docutils literal notranslate"><span class="pre">__slots__</span></code> the decorator would have to create a new class, set
|
||
<code class="docutils literal notranslate"><span class="pre">__slots__</span></code>, and return it. Because this behavior is somewhat
|
||
surprising, the initial version of Data Classes will not support
|
||
automatically setting <code class="docutils literal notranslate"><span class="pre">__slots__</span></code>. There are a number of
|
||
workarounds:</p>
|
||
<ul class="simple">
|
||
<li>Manually add <code class="docutils literal notranslate"><span class="pre">__slots__</span></code> in the class definition.</li>
|
||
<li>Write a function (which could be used as a decorator) that inspects
|
||
the class using <code class="docutils literal notranslate"><span class="pre">fields()</span></code> and creates a new class with
|
||
<code class="docutils literal notranslate"><span class="pre">__slots__</span></code> set.</li>
|
||
</ul>
|
||
<p>For more discussion, see <a class="footnote-reference brackets" href="#id25" id="id11">[11]</a>.</p>
|
||
</section>
|
||
<section id="why-not-just-use-namedtuple">
|
||
<h3><a class="toc-backref" href="#why-not-just-use-namedtuple" role="doc-backlink">Why not just use namedtuple?</a></h3>
|
||
<ul>
|
||
<li>Any namedtuple can be accidentally compared to any other with the
|
||
same number of fields. For example: <code class="docutils literal notranslate"><span class="pre">Point3D(2017,</span> <span class="pre">6,</span> <span class="pre">2)</span> <span class="pre">==</span>
|
||
<span class="pre">Date(2017,</span> <span class="pre">6,</span> <span class="pre">2)</span></code>. With Data Classes, this would return False.</li>
|
||
<li>A namedtuple can be accidentally compared to a tuple. For example,
|
||
<code class="docutils literal notranslate"><span class="pre">Point2D(1,</span> <span class="pre">10)</span> <span class="pre">==</span> <span class="pre">(1,</span> <span class="pre">10)</span></code>. With Data Classes, this would return
|
||
False.</li>
|
||
<li>Instances are always iterable, which can make it difficult to add
|
||
fields. If a library defines:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Time</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span><span class="s1">'Time'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'hour'</span><span class="p">,</span> <span class="s1">'minute'</span><span class="p">])</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">get_time</span><span class="p">():</span>
|
||
<span class="k">return</span> <span class="n">Time</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Then if a user uses this code as:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">hour</span><span class="p">,</span> <span class="n">minute</span> <span class="o">=</span> <span class="n">get_time</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>then it would not be possible to add a <code class="docutils literal notranslate"><span class="pre">second</span></code> field to <code class="docutils literal notranslate"><span class="pre">Time</span></code>
|
||
without breaking the user’s code.</p>
|
||
</li>
|
||
<li>No option for mutable instances.</li>
|
||
<li>Cannot specify default values.</li>
|
||
<li>Cannot control which fields are used for <code class="docutils literal notranslate"><span class="pre">__init__</span></code>, <code class="docutils literal notranslate"><span class="pre">__repr__</span></code>,
|
||
etc.</li>
|
||
<li>Cannot support combining fields by inheritance.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="why-not-just-use-typing-namedtuple">
|
||
<h3><a class="toc-backref" href="#why-not-just-use-typing-namedtuple" role="doc-backlink">Why not just use typing.NamedTuple?</a></h3>
|
||
<p>For classes with statically defined fields, it does support similar
|
||
syntax to Data Classes, using type annotations. This produces a
|
||
namedtuple, so it shares <code class="docutils literal notranslate"><span class="pre">namedtuple</span></code>s benefits and some of its
|
||
downsides. Data Classes, unlike <code class="docutils literal notranslate"><span class="pre">typing.NamedTuple</span></code>, support
|
||
combining fields via inheritance.</p>
|
||
</section>
|
||
<section id="why-not-just-use-attrs">
|
||
<h3><a class="toc-backref" href="#why-not-just-use-attrs" role="doc-backlink">Why not just use attrs?</a></h3>
|
||
<ul class="simple">
|
||
<li>attrs moves faster than could be accommodated if it were moved in to
|
||
the standard library.</li>
|
||
<li>attrs supports additional features not being proposed here:
|
||
validators, converters, metadata, etc. Data Classes makes a
|
||
tradeoff to achieve simplicity by not implementing these
|
||
features.</li>
|
||
</ul>
|
||
<p>For more discussion, see <a class="footnote-reference brackets" href="#id26" id="id12">[12]</a>.</p>
|
||
</section>
|
||
<section id="post-init-parameters">
|
||
<h3><a class="toc-backref" href="#post-init-parameters" role="doc-backlink">post-init parameters</a></h3>
|
||
<p>In an earlier version of this PEP before <code class="docutils literal notranslate"><span class="pre">InitVar</span></code> was added, the
|
||
post-init function <code class="docutils literal notranslate"><span class="pre">__post_init__</span></code> never took any parameters.</p>
|
||
<p>The normal way of doing parameterized initialization (and not just
|
||
with Data Classes) is to provide an alternate classmethod constructor.
|
||
For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="nb">int</span>
|
||
|
||
<span class="nd">@classmethod</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">from_file</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">filename</span><span class="p">):</span>
|
||
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">as</span> <span class="n">fl</span><span class="p">:</span>
|
||
<span class="n">file_value</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">fl</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
|
||
<span class="k">return</span> <span class="n">C</span><span class="p">(</span><span class="n">file_value</span><span class="p">)</span>
|
||
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">C</span><span class="o">.</span><span class="n">from_file</span><span class="p">(</span><span class="s1">'file.txt'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Because the <code class="docutils literal notranslate"><span class="pre">__post_init__</span></code> function is the last thing called in the
|
||
generated <code class="docutils literal notranslate"><span class="pre">__init__</span></code>, having a classmethod constructor (which can
|
||
also execute code immediately after constructing the object) is
|
||
functionally equivalent to being able to pass parameters to a
|
||
<code class="docutils literal notranslate"><span class="pre">__post_init__</span></code> function.</p>
|
||
<p>With <code class="docutils literal notranslate"><span class="pre">InitVar</span></code>s, <code class="docutils literal notranslate"><span class="pre">__post_init__</span></code> functions can now take
|
||
parameters. They are passed first to <code class="docutils literal notranslate"><span class="pre">__init__</span></code> which passes them
|
||
to <code class="docutils literal notranslate"><span class="pre">__post_init__</span></code> where user code can use them as needed.</p>
|
||
<p>The only real difference between alternate classmethod constructors
|
||
and <code class="docutils literal notranslate"><span class="pre">InitVar</span></code> pseudo-fields is in regards to required non-field
|
||
parameters during object creation. With <code class="docutils literal notranslate"><span class="pre">InitVar</span></code>s, using
|
||
<code class="docutils literal notranslate"><span class="pre">__init__</span></code> and the module-level <code class="docutils literal notranslate"><span class="pre">replace()</span></code> function <code class="docutils literal notranslate"><span class="pre">InitVar</span></code>s
|
||
must always be specified. Consider the case where a <code class="docutils literal notranslate"><span class="pre">context</span></code>
|
||
object is needed to create an instance, but isn’t stored as a field.
|
||
With alternate classmethod constructors the <code class="docutils literal notranslate"><span class="pre">context</span></code> parameter is
|
||
always optional, because you could still create the object by going
|
||
through <code class="docutils literal notranslate"><span class="pre">__init__</span></code> (unless you suppress its creation). Which
|
||
approach is more appropriate will be application-specific, but both
|
||
approaches are supported.</p>
|
||
<p>Another reason for using <code class="docutils literal notranslate"><span class="pre">InitVar</span></code> fields is that the class author
|
||
can control the order of <code class="docutils literal notranslate"><span class="pre">__init__</span></code> parameters. This is especially
|
||
important with regular fields and <code class="docutils literal notranslate"><span class="pre">InitVar</span></code> fields that have default
|
||
values, as all fields with defaults must come after all fields without
|
||
defaults. A previous design had all init-only fields coming after
|
||
regular fields. This meant that if any field had a default value,
|
||
then all init-only fields would have to have defaults values, too.</p>
|
||
</section>
|
||
<section id="asdict-and-astuple-function-names">
|
||
<h3><a class="toc-backref" href="#asdict-and-astuple-function-names" role="doc-backlink">asdict and astuple function names</a></h3>
|
||
<p>The names of the module-level helper functions <code class="docutils literal notranslate"><span class="pre">asdict()</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">astuple()</span></code> are arguably not <a class="pep reference internal" href="../pep-0008/" title="PEP 8 – Style Guide for Python Code">PEP 8</a> compliant, and should be
|
||
<code class="docutils literal notranslate"><span class="pre">as_dict()</span></code> and <code class="docutils literal notranslate"><span class="pre">as_tuple()</span></code>, respectively. However, after
|
||
discussion <a class="footnote-reference brackets" href="#id27" id="id13">[13]</a> it was decided to keep consistency with
|
||
<code class="docutils literal notranslate"><span class="pre">namedtuple._asdict()</span></code> and <code class="docutils literal notranslate"><span class="pre">attr.asdict()</span></code>.</p>
|
||
</section>
|
||
</section>
|
||
<section id="rejected-ideas">
|
||
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected ideas</a></h2>
|
||
<section id="copying-init-false-fields-after-new-object-creation-in-replace">
|
||
<h3><a class="toc-backref" href="#copying-init-false-fields-after-new-object-creation-in-replace" role="doc-backlink">Copying <code class="docutils literal notranslate"><span class="pre">init=False</span></code> fields after new object creation in replace()</a></h3>
|
||
<p>Fields that are <code class="docutils literal notranslate"><span class="pre">init=False</span></code> are by definition not passed to
|
||
<code class="docutils literal notranslate"><span class="pre">__init__</span></code>, but instead are initialized with a default value, or by
|
||
calling a default factory function in <code class="docutils literal notranslate"><span class="pre">__init__</span></code>, or by code in
|
||
<code class="docutils literal notranslate"><span class="pre">__post_init__</span></code>.</p>
|
||
<p>A previous version of this PEP specified that <code class="docutils literal notranslate"><span class="pre">init=False</span></code> fields
|
||
would be copied from the source object to the newly created object
|
||
after <code class="docutils literal notranslate"><span class="pre">__init__</span></code> returned, but that was deemed to be inconsistent
|
||
with using <code class="docutils literal notranslate"><span class="pre">__init__</span></code> and <code class="docutils literal notranslate"><span class="pre">__post_init__</span></code> to initialize the new
|
||
object. For example, consider this case:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">Square</span><span class="p">:</span>
|
||
<span class="n">length</span><span class="p">:</span> <span class="nb">float</span>
|
||
<span class="n">area</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mf">0.0</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">__post_init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">area</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">length</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">length</span>
|
||
|
||
<span class="n">s1</span> <span class="o">=</span> <span class="n">Square</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
|
||
<span class="n">s2</span> <span class="o">=</span> <span class="n">replace</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">length</span><span class="o">=</span><span class="mf">2.0</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If <code class="docutils literal notranslate"><span class="pre">init=False</span></code> fields were copied from the source to the
|
||
destination object after <code class="docutils literal notranslate"><span class="pre">__post_init__</span></code> is run, then s2 would end
|
||
up begin <code class="docutils literal notranslate"><span class="pre">Square(length=2.0,</span> <span class="pre">area=1.0)</span></code>, instead of the correct
|
||
<code class="docutils literal notranslate"><span class="pre">Square(length=2.0,</span> <span class="pre">area=4.0)</span></code>.</p>
|
||
</section>
|
||
<section id="automatically-support-mutable-default-values">
|
||
<h3><a class="toc-backref" href="#automatically-support-mutable-default-values" role="doc-backlink">Automatically support mutable default values</a></h3>
|
||
<p>One proposal was to automatically copy defaults, so that if a literal
|
||
list <code class="docutils literal notranslate"><span class="pre">[]</span></code> was a default value, each instance would get a new list.
|
||
There were undesirable side effects of this decision, so the final
|
||
decision is to disallow the 3 known built-in mutable types: list,
|
||
dict, and set. For a complete discussion of this and other options,
|
||
see <a class="footnote-reference brackets" href="#id28" id="id14">[14]</a>.</p>
|
||
</section>
|
||
</section>
|
||
<section id="examples">
|
||
<h2><a class="toc-backref" href="#examples" role="doc-backlink">Examples</a></h2>
|
||
<section id="custom-init-method">
|
||
<h3><a class="toc-backref" href="#custom-init-method" role="doc-backlink">Custom __init__ method</a></h3>
|
||
<p>Sometimes the generated <code class="docutils literal notranslate"><span class="pre">__init__</span></code> method does not suffice. For
|
||
example, suppose you wanted to have an object to store <code class="docutils literal notranslate"><span class="pre">*args</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">ArgHolder</span><span class="p">:</span>
|
||
<span class="n">args</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Any</span><span class="p">]</span>
|
||
<span class="n">kwargs</span><span class="p">:</span> <span class="n">Mapping</span><span class="p">[</span><span class="n">Any</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">args</span> <span class="o">=</span> <span class="n">args</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">kwargs</span> <span class="o">=</span> <span class="n">kwargs</span>
|
||
|
||
<span class="n">a</span> <span class="o">=</span> <span class="n">ArgHolder</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">three</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="a-complicated-example">
|
||
<h3><a class="toc-backref" href="#a-complicated-example" role="doc-backlink">A complicated example</a></h3>
|
||
<p>This code exists in a closed source project:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Application</span><span class="p">:</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">requirements</span><span class="p">,</span> <span class="n">constraints</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s1">''</span><span class="p">,</span> <span class="n">executable_links</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">executables_dir</span><span class="o">=</span><span class="p">()):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">requirements</span> <span class="o">=</span> <span class="n">requirements</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">constraints</span> <span class="o">=</span> <span class="p">{}</span> <span class="k">if</span> <span class="n">constraints</span> <span class="ow">is</span> <span class="kc">None</span> <span class="k">else</span> <span class="n">constraints</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">path</span> <span class="o">=</span> <span class="n">path</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">executable_links</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">if</span> <span class="n">executable_links</span> <span class="ow">is</span> <span class="kc">None</span> <span class="k">else</span> <span class="n">executable_links</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">executables_dir</span> <span class="o">=</span> <span class="n">executables_dir</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">additional_items</span> <span class="o">=</span> <span class="p">[]</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="sa">f</span><span class="s1">'Application(</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">!r}</span><span class="s1">,</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">requirements</span><span class="si">!r}</span><span class="s1">,</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">constraints</span><span class="si">!r}</span><span class="s1">,</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">path</span><span class="si">!r}</span><span class="s1">,</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">executable_links</span><span class="si">!r}</span><span class="s1">,</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">executables_dir</span><span class="si">!r}</span><span class="s1">,</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">additional_items</span><span class="si">!r}</span><span class="s1">)'</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This can be replaced by:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">Application</span><span class="p">:</span>
|
||
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">requirements</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Requirement</span><span class="p">]</span>
|
||
<span class="n">constraints</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">default_factory</span><span class="o">=</span><span class="nb">dict</span><span class="p">)</span>
|
||
<span class="n">path</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s1">''</span>
|
||
<span class="n">executable_links</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">default_factory</span><span class="o">=</span><span class="nb">list</span><span class="p">)</span>
|
||
<span class="n">executable_dir</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="p">()</span>
|
||
<span class="n">additional_items</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">default_factory</span><span class="o">=</span><span class="nb">list</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The Data Class version is more declarative, has less code, supports
|
||
<code class="docutils literal notranslate"><span class="pre">typing</span></code>, and includes the other generated functions.</p>
|
||
</section>
|
||
</section>
|
||
<section id="acknowledgements">
|
||
<h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2>
|
||
<p>The following people provided invaluable input during the development
|
||
of this PEP and code: Ivan Levkivskyi, Guido van Rossum, Hynek
|
||
Schlawack, Raymond Hettinger, and Lisa Roach. I thank them for their
|
||
time and expertise.</p>
|
||
<p>A special mention must be made about the <code class="docutils literal notranslate"><span class="pre">attrs</span></code> project. It was a
|
||
true inspiration for this PEP, and I respect the design decisions they
|
||
made.</p>
|
||
</section>
|
||
<section id="references">
|
||
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
|
||
<aside class="footnote-list brackets">
|
||
<aside class="footnote brackets" id="id15" role="doc-footnote">
|
||
<dt class="label" id="id15">[<a href="#id1">1</a>]</dt>
|
||
<dd>attrs project on github
|
||
(<a class="reference external" href="https://github.com/python-attrs/attrs">https://github.com/python-attrs/attrs</a>)</aside>
|
||
<aside class="footnote brackets" id="id16" role="doc-footnote">
|
||
<dt class="label" id="id16">[<a href="#id2">2</a>]</dt>
|
||
<dd>George Sakkis’ recordType recipe
|
||
(<a class="reference external" href="http://code.activestate.com/recipes/576555-records/">http://code.activestate.com/recipes/576555-records/</a>)</aside>
|
||
<aside class="footnote brackets" id="id17" role="doc-footnote">
|
||
<dt class="label" id="id17">[<a href="#id3">3</a>]</dt>
|
||
<dd>DictDotLookup recipe
|
||
(<a class="reference external" href="http://code.activestate.com/recipes/576586-dot-style-nested-lookups-over-dictionary-based-dat/">http://code.activestate.com/recipes/576586-dot-style-nested-lookups-over-dictionary-based-dat/</a>)</aside>
|
||
<aside class="footnote brackets" id="id18" role="doc-footnote">
|
||
<dt class="label" id="id18">[<a href="#id4">4</a>]</dt>
|
||
<dd>attrdict package
|
||
(<a class="reference external" href="https://pypi.python.org/pypi/attrdict">https://pypi.python.org/pypi/attrdict</a>)</aside>
|
||
<aside class="footnote brackets" id="id19" role="doc-footnote">
|
||
<dt class="label" id="id19">[<a href="#id5">5</a>]</dt>
|
||
<dd>StackOverflow question about data container classes
|
||
(<a class="reference external" href="https://stackoverflow.com/questions/3357581/using-python-class-as-a-data-container">https://stackoverflow.com/questions/3357581/using-python-class-as-a-data-container</a>)</aside>
|
||
<aside class="footnote brackets" id="id20" role="doc-footnote">
|
||
<dt class="label" id="id20">[<a href="#id6">6</a>]</dt>
|
||
<dd>David Beazley metaclass talk featuring data classes
|
||
(<a class="reference external" href="https://www.youtube.com/watch?v=sPiWg5jSoZI">https://www.youtube.com/watch?v=sPiWg5jSoZI</a>)</aside>
|
||
<aside class="footnote brackets" id="id21" role="doc-footnote">
|
||
<dt class="label" id="id21">[<a href="#id7">7</a>]</dt>
|
||
<dd>Python documentation for __hash__
|
||
(<a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__hash__">https://docs.python.org/3/reference/datamodel.html#object.__hash__</a>)</aside>
|
||
<aside class="footnote brackets" id="id22" role="doc-footnote">
|
||
<dt class="label" id="id22">[<a href="#id8">8</a>]</dt>
|
||
<dd><a class="pep reference internal" href="../pep-0526/#class-and-instance-variable-annotations" title="PEP 526 – Syntax for Variable Annotations § Class and instance variable annotations">ClassVar discussion in PEP 526</a></aside>
|
||
<aside class="footnote brackets" id="id23" role="doc-footnote">
|
||
<dt class="label" id="id23">[<a href="#id9">9</a>]</dt>
|
||
<dd>Start of python-ideas discussion
|
||
(<a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2017-May/045618.html">https://mail.python.org/pipermail/python-ideas/2017-May/045618.html</a>)</aside>
|
||
<aside class="footnote brackets" id="id24" role="doc-footnote">
|
||
<dt class="label" id="id24">[<a href="#id10">10</a>]</dt>
|
||
<dd>GitHub repo where discussions and initial development took place
|
||
(<a class="reference external" href="https://github.com/ericvsmith/dataclasses">https://github.com/ericvsmith/dataclasses</a>)</aside>
|
||
<aside class="footnote brackets" id="id25" role="doc-footnote">
|
||
<dt class="label" id="id25">[<a href="#id11">11</a>]</dt>
|
||
<dd>Support __slots__?
|
||
(<a class="reference external" href="https://github.com/ericvsmith/dataclasses/issues/28">https://github.com/ericvsmith/dataclasses/issues/28</a>)</aside>
|
||
<aside class="footnote brackets" id="id26" role="doc-footnote">
|
||
<dt class="label" id="id26">[<a href="#id12">12</a>]</dt>
|
||
<dd>why not just attrs?
|
||
(<a class="reference external" href="https://github.com/ericvsmith/dataclasses/issues/19">https://github.com/ericvsmith/dataclasses/issues/19</a>)</aside>
|
||
<aside class="footnote brackets" id="id27" role="doc-footnote">
|
||
<dt class="label" id="id27">[<a href="#id13">13</a>]</dt>
|
||
<dd><a class="pep reference internal" href="../pep-0008/" title="PEP 8 – Style Guide for Python Code">PEP 8</a> names for asdict and astuple
|
||
(<a class="reference external" href="https://github.com/ericvsmith/dataclasses/issues/110">https://github.com/ericvsmith/dataclasses/issues/110</a>)</aside>
|
||
<aside class="footnote brackets" id="id28" role="doc-footnote">
|
||
<dt class="label" id="id28">[<a href="#id14">14</a>]</dt>
|
||
<dd>Copying mutable defaults
|
||
(<a class="reference external" href="https://github.com/ericvsmith/dataclasses/issues/3">https://github.com/ericvsmith/dataclasses/issues/3</a>)</aside>
|
||
</aside>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>This document has been placed in the public domain.</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0557.rst">https://github.com/python/peps/blob/main/peps/pep-0557.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0557.rst">2023-09-09 17:39:29 GMT</a></p>
|
||
|
||
</article>
|
||
<nav id="pep-sidebar">
|
||
<h2>Contents</h2>
|
||
<ul>
|
||
<li><a class="reference internal" href="#notice-for-reviewers">Notice for Reviewers</a></li>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a></li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#field-objects"><code class="docutils literal notranslate"><span class="pre">Field</span></code> objects</a></li>
|
||
<li><a class="reference internal" href="#post-init-processing">post-init processing</a></li>
|
||
<li><a class="reference internal" href="#class-variables">Class variables</a></li>
|
||
<li><a class="reference internal" href="#init-only-variables">Init-only variables</a></li>
|
||
<li><a class="reference internal" href="#frozen-instances">Frozen instances</a></li>
|
||
<li><a class="reference internal" href="#inheritance">Inheritance</a></li>
|
||
<li><a class="reference internal" href="#default-factory-functions">Default factory functions</a></li>
|
||
<li><a class="reference internal" href="#mutable-default-values">Mutable default values</a></li>
|
||
<li><a class="reference internal" href="#module-level-helper-functions">Module level helper functions</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#discussion">Discussion</a><ul>
|
||
<li><a class="reference internal" href="#python-ideas-discussion">python-ideas discussion</a></li>
|
||
<li><a class="reference internal" href="#support-for-automatically-setting-slots">Support for automatically setting <code class="docutils literal notranslate"><span class="pre">__slots__</span></code>?</a></li>
|
||
<li><a class="reference internal" href="#why-not-just-use-namedtuple">Why not just use namedtuple?</a></li>
|
||
<li><a class="reference internal" href="#why-not-just-use-typing-namedtuple">Why not just use typing.NamedTuple?</a></li>
|
||
<li><a class="reference internal" href="#why-not-just-use-attrs">Why not just use attrs?</a></li>
|
||
<li><a class="reference internal" href="#post-init-parameters">post-init parameters</a></li>
|
||
<li><a class="reference internal" href="#asdict-and-astuple-function-names">asdict and astuple function names</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected ideas</a><ul>
|
||
<li><a class="reference internal" href="#copying-init-false-fields-after-new-object-creation-in-replace">Copying <code class="docutils literal notranslate"><span class="pre">init=False</span></code> fields after new object creation in replace()</a></li>
|
||
<li><a class="reference internal" href="#automatically-support-mutable-default-values">Automatically support mutable default values</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#examples">Examples</a><ul>
|
||
<li><a class="reference internal" href="#custom-init-method">Custom __init__ method</a></li>
|
||
<li><a class="reference internal" href="#a-complicated-example">A complicated example</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0557.rst">Page Source (GitHub)</a>
|
||
</nav>
|
||
</section>
|
||
<script src="../_static/colour_scheme.js"></script>
|
||
<script src="../_static/wrap_tables.js"></script>
|
||
<script src="../_static/sticky_banner.js"></script>
|
||
</body>
|
||
</html> |