/usr/share/doc/python-attr/html/why.html is in python-attr-doc 17.4.0-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Why not… — attrs 17.4.0 documentation</title>
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '17.4.0',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt'
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="attrs by Example" href="examples.html" />
<link rel="prev" title="Overview" href="overview.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head>
<body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<div class="section" id="why-not">
<span id="why"></span><h1>Why not…<a class="headerlink" href="#why-not" title="Permalink to this headline">¶</a></h1>
<p>If you’d like third party’s account why <code class="docutils literal"><span class="pre">attrs</span></code> is great, have a look at Glyph’s <a class="reference external" href="https://glyph.twistedmatrix.com/2016/08/attrs.html">The One Python Library Everyone Needs</a>!</p>
<div class="section" id="tuples">
<h2>…tuples?<a class="headerlink" href="#tuples" title="Permalink to this headline">¶</a></h2>
<div class="section" id="readability">
<h3>Readability<a class="headerlink" href="#readability" title="Permalink to this headline">¶</a></h3>
<p>What makes more sense while debugging:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="n">Point</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
</pre></div>
</div>
<p>or:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</pre></div>
</div>
<p>?</p>
<p>Let’s add even more ambiguity:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="n">Customer</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span> <span class="n">reseller</span><span class="o">=</span><span class="mi">23</span><span class="p">,</span> <span class="n">first_name</span><span class="o">=</span><span class="s2">"Jane"</span><span class="p">,</span> <span class="n">last_name</span><span class="o">=</span><span class="s2">"John"</span><span class="p">)</span>
</pre></div>
</div>
<p>or:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="p">(</span><span class="mi">42</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="s2">"Jane"</span><span class="p">,</span> <span class="s2">"John"</span><span class="p">)</span>
</pre></div>
</div>
<p>?</p>
<p>Why would you want to write <code class="docutils literal"><span class="pre">customer[2]</span></code> instead of <code class="docutils literal"><span class="pre">customer.first_name</span></code>?</p>
<p>Don’t get me started when you add nesting.
If you’ve never run into mysterious tuples you had no idea what the hell they meant while debugging, you’re much smarter than yours truly.</p>
<p>Using proper classes with names and types makes program code much more readable and <a class="reference external" href="https://arxiv.org/pdf/1304.5257.pdf">comprehensible</a>.
Especially when trying to grok a new piece of software or returning to old code after several months.</p>
</div>
<div class="section" id="extendability">
<h3>Extendability<a class="headerlink" href="#extendability" title="Permalink to this headline">¶</a></h3>
<p>Imagine you have a function that takes or returns a tuple.
Especially if you use tuple unpacking (eg. <code class="docutils literal"><span class="pre">x,</span> <span class="pre">y</span> <span class="pre">=</span> <span class="pre">get_point()</span></code>), adding additional data means that you have to change the invocation of that function <em>everywhere</em>.</p>
<p>Adding an attribute to a class concerns only those who actually care about that attribute.</p>
</div>
</div>
<div class="section" id="namedtuples">
<h2>…namedtuples?<a class="headerlink" href="#namedtuples" title="Permalink to this headline">¶</a></h2>
<p><a class="reference external" href="/usr/share/doc/python3-doc/html/library/collections.html#collections.namedtuple" title="(in Python v3.6)"><code class="xref py py-func docutils literal"><span class="pre">collections.namedtuple()</span></code></a>s are tuples with names, not classes. <a class="footnote-reference" href="#history" id="id1">[1]</a>
Since writing classes is tiresome in Python, every now and then someone discovers all the typing they could save and gets really excited.
However that convenience comes at a price.</p>
<p>The most obvious difference between <code class="docutils literal"><span class="pre">namedtuple</span></code>s and <code class="docutils literal"><span class="pre">attrs</span></code>-based classes is that the latter are type-sensitive:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">attr</span>
<span class="gp">>>> </span><span class="n">C1</span> <span class="o">=</span> <span class="n">attr</span><span class="o">.</span><span class="n">make_class</span><span class="p">(</span><span class="s2">"C1"</span><span class="p">,</span> <span class="p">[</span><span class="s2">"a"</span><span class="p">])</span>
<span class="gp">>>> </span><span class="n">C2</span> <span class="o">=</span> <span class="n">attr</span><span class="o">.</span><span class="n">make_class</span><span class="p">(</span><span class="s2">"C2"</span><span class="p">,</span> <span class="p">[</span><span class="s2">"a"</span><span class="p">])</span>
<span class="gp">>>> </span><span class="n">i1</span> <span class="o">=</span> <span class="n">C1</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">i2</span> <span class="o">=</span> <span class="n">C2</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">i1</span><span class="o">.</span><span class="n">a</span> <span class="o">==</span> <span class="n">i2</span><span class="o">.</span><span class="n">a</span>
<span class="go">True</span>
<span class="gp">>>> </span><span class="n">i1</span> <span class="o">==</span> <span class="n">i2</span>
<span class="go">False</span>
</pre></div>
</div>
<p>…while a <code class="docutils literal"><span class="pre">namedtuple</span></code> is <em>intentionally</em> <a class="reference external" href="https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences">behaving like a tuple</a> which means the type of a tuple is <em>ignored</em>:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">from</span> <span class="nn">collections</span> <span class="k">import</span> <span class="n">namedtuple</span>
<span class="gp">>>> </span><span class="n">NT1</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span><span class="s2">"NT1"</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">NT2</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span><span class="s2">"NT2"</span><span class="p">,</span> <span class="s2">"b"</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">t1</span> <span class="o">=</span> <span class="n">NT1</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">t2</span> <span class="o">=</span> <span class="n">NT2</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">t1</span> <span class="o">==</span> <span class="n">t2</span> <span class="o">==</span> <span class="p">(</span><span class="mi">1</span><span class="p">,)</span>
<span class="go">True</span>
</pre></div>
</div>
<p>Other often surprising behaviors include:</p>
<ul>
<li><p class="first">Since they are a subclass of tuples, <code class="docutils literal"><span class="pre">namedtuple</span></code>s have a length and are both iterable and indexable.
That’s not what you’d expect from a class and is likely to shadow subtle typo bugs.</p>
</li>
<li><p class="first">Iterability also implies that it’s easy to accidentally unpack a <code class="docutils literal"><span class="pre">namedtuple</span></code> which leads to hard-to-find bugs. <a class="footnote-reference" href="#iter" id="id2">[3]</a></p>
</li>
<li><p class="first"><code class="docutils literal"><span class="pre">namedtuple</span></code>s have their methods <em>on your instances</em> whether you like it or not. <a class="footnote-reference" href="#pollution" id="id3">[2]</a></p>
</li>
<li><p class="first"><code class="docutils literal"><span class="pre">namedtuple</span></code>s are <em>always</em> immutable.
Not only does that mean that you can’t decide for yourself whether your instances should be immutable or not, it also means that if you want to influence your class’ initialization (validation? default values?), you have to implement <a class="reference external" href="/usr/share/doc/python3-doc/html/reference/datamodel.html#object.__new__" title="(in Python v3.6)"><code class="xref py py-meth docutils literal"><span class="pre">__new__()</span></code></a> which is a particularly hacky and error-prone requirement for a very common problem. <a class="footnote-reference" href="#immutable" id="id4">[4]</a></p>
</li>
<li><p class="first">To attach methods to a <code class="docutils literal"><span class="pre">namedtuple</span></code> you have to subclass it.
And if you follow the standard library documentation’s recommendation of:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Point</span><span class="p">(</span><span class="n">namedtuple</span><span class="p">(</span><span class="s1">'Point'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'x'</span><span class="p">,</span> <span class="s1">'y'</span><span class="p">])):</span>
<span class="c1"># ...</span>
</pre></div>
</div>
<p>you end up with a class that has <em>two</em> <code class="docutils literal"><span class="pre">Point</span></code>s in its <a class="reference external" href="/usr/share/doc/python3-doc/html/library/stdtypes.html#class.__mro__" title="(in Python v3.6)"><code class="xref py py-attr docutils literal"><span class="pre">__mro__</span></code></a>: <code class="docutils literal"><span class="pre">[<class</span> <span class="pre">'point.Point'>,</span> <span class="pre"><class</span> <span class="pre">'point.Point'>,</span> <span class="pre"><type</span> <span class="pre">'tuple'>,</span> <span class="pre"><type</span> <span class="pre">'object'>]</span></code>.</p>
<p>That’s not only confusing, it also has very practical consequences:
for example if you create documentation that includes class hierarchies like <a class="reference external" href="http://www.sphinx-doc.org/en/stable/ext/autodoc.html">Sphinx’s autodoc</a> with <code class="docutils literal"><span class="pre">show-inheritance</span></code>.
Again: common problem, hacky solution with confusing fallout.</p>
</li>
</ul>
<p>All these things make <code class="docutils literal"><span class="pre">namedtuple</span></code>s a particularly poor choice for public APIs because all your objects are irrevocably tainted.
With <code class="docutils literal"><span class="pre">attrs</span></code> your users won’t notice a difference because it creates regular, well-behaved classes.</p>
<div class="admonition-summary admonition">
<p class="first admonition-title">Summary</p>
<p>If you want a <em>tuple with names</em>, by all means: go for a <code class="docutils literal"><span class="pre">namedtuple</span></code>. <a class="footnote-reference" href="#perf" id="id5">[5]</a>
But if you want a class with methods, you’re doing yourself a disservice by relying on a pile of hacks that requires you to employ even more hacks as your requirements expand.</p>
<p class="last">Other than that, <code class="docutils literal"><span class="pre">attrs</span></code> also adds nifty features like validators, converters, and (mutable!) default values.</p>
</div>
<p class="rubric">Footnotes</p>
<table class="docutils footnote" frame="void" id="history" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id1">[1]</a></td><td><p class="first">The word is that <code class="docutils literal"><span class="pre">namedtuple</span></code>s were added to the Python standard library as a way to make tuples in return values more readable.
And indeed that is something you see throughout the standard library.</p>
<p class="last">Looking at what the makers of <code class="docutils literal"><span class="pre">namedtuple</span></code>s use it for themselves is a good guideline for deciding on your own use cases.</p>
</td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="pollution" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id3">[2]</a></td><td><code class="docutils literal"><span class="pre">attrs</span></code> only adds a single attribute: <code class="docutils literal"><span class="pre">__attrs_attrs__</span></code> for introspection.
All helpers are functions in the <code class="docutils literal"><span class="pre">attr</span></code> package.
Since they take the instance as first argument, you can easily attach them to your classes under a name of your own choice.</td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="iter" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id2">[3]</a></td><td><a class="reference internal" href="api.html#attr.astuple" title="attr.astuple"><code class="xref py py-func docutils literal"><span class="pre">attr.astuple()</span></code></a> can be used to get that behavior in <code class="docutils literal"><span class="pre">attrs</span></code> on <em>explicit demand</em>.</td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="immutable" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id4">[4]</a></td><td><code class="docutils literal"><span class="pre">attrs</span></code> offers <em>optional</em> immutability through the <code class="docutils literal"><span class="pre">frozen</span></code> keyword.</td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="perf" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id5">[5]</a></td><td>Although <code class="docutils literal"><span class="pre">attrs</span></code> would serve you just as well!
Since both employ the same method of writing and compiling Python code for you, the performance penalty is negligible at worst and in some cases <code class="docutils literal"><span class="pre">attrs</span></code> is even faster if you use <code class="docutils literal"><span class="pre">slots=True</span></code> (which is generally a good idea anyway).</td></tr>
</tbody>
</table>
</div>
<div class="section" id="dicts">
<h2>…dicts?<a class="headerlink" href="#dicts" title="Permalink to this headline">¶</a></h2>
<p>Dictionaries are not for fixed fields.</p>
<p>If you have a dict, it maps something to something else.
You should be able to add and remove values.</p>
<p><code class="docutils literal"><span class="pre">attrs</span></code> lets you be specific about those expectations; a dictionary does not.
It gives you a named entity (the class) in your code, which lets you explain in other places whether you take a parameter of that class or return a value of that class.</p>
<p>In other words: if your dict has a fixed and known set of keys, it is an object, not a hash.
So if you never iterate over the keys of a dict, you should use a proper class.</p>
</div>
<div class="section" id="hand-written-classes">
<h2>…hand-written classes?<a class="headerlink" href="#hand-written-classes" title="Permalink to this headline">¶</a></h2>
<p>While we’re fans of all things artisanal, writing the same nine methods all over again doesn’t qualify for me.
I usually manage to get some typos inside and there’s simply more code that can break and thus has to be tested.</p>
<p>To bring it into perspective, the equivalent of</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nd">@attr</span><span class="o">.</span><span class="n">s</span>
<span class="gp">... </span><span class="k">class</span> <span class="nc">SmartClass</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="gp">... </span> <span class="n">a</span> <span class="o">=</span> <span class="n">attr</span><span class="o">.</span><span class="n">ib</span><span class="p">()</span>
<span class="gp">... </span> <span class="n">b</span> <span class="o">=</span> <span class="n">attr</span><span class="o">.</span><span class="n">ib</span><span class="p">()</span>
<span class="gp">>>> </span><span class="n">SmartClass</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="go">SmartClass(a=1, b=2)</span>
</pre></div>
</div>
<p>is</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">class</span> <span class="nc">ArtisanalClass</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">__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="n">b</span><span class="p">):</span>
<span class="gp">... </span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="n">a</span>
<span class="gp">... </span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">b</span>
<span class="gp">...</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">return</span> <span class="s2">"ArtisanalClass(a=</span><span class="si">{}</span><span class="s2">, b=</span><span class="si">{}</span><span class="s2">)"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span><span class="p">)</span>
<span class="gp">...</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">__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="gp">... </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="gp">... </span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</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">a</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">b</span><span class="p">)</span>
<span class="gp">... </span> <span class="k">else</span><span class="p">:</span>
<span class="gp">... </span> <span class="k">return</span> <span class="bp">NotImplemented</span>
<span class="gp">...</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">__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="gp">... </span> <span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="gp">... </span> <span class="k">if</span> <span class="n">result</span> <span class="ow">is</span> <span class="bp">NotImplemented</span><span class="p">:</span>
<span class="gp">... </span> <span class="k">return</span> <span class="bp">NotImplemented</span>
<span class="gp">... </span> <span class="k">else</span><span class="p">:</span>
<span class="gp">... </span> <span class="k">return</span> <span class="ow">not</span> <span class="n">result</span>
<span class="gp">...</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">__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="gp">... </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="gp">... </span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</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">a</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">b</span><span class="p">)</span>
<span class="gp">... </span> <span class="k">else</span><span class="p">:</span>
<span class="gp">... </span> <span class="k">return</span> <span class="bp">NotImplemented</span>
<span class="gp">...</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">__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="gp">... </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="gp">... </span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</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">a</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">b</span><span class="p">)</span>
<span class="gp">... </span> <span class="k">else</span><span class="p">:</span>
<span class="gp">... </span> <span class="k">return</span> <span class="bp">NotImplemented</span>
<span class="gp">...</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">__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="gp">... </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="gp">... </span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</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">a</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">b</span><span class="p">)</span>
<span class="gp">... </span> <span class="k">else</span><span class="p">:</span>
<span class="gp">... </span> <span class="k">return</span> <span class="bp">NotImplemented</span>
<span class="gp">...</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">__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="gp">... </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="gp">... </span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</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">a</span><span class="p">,</span> <span class="n">other</span><span class="o">.</span><span class="n">b</span><span class="p">)</span>
<span class="gp">... </span> <span class="k">else</span><span class="p">:</span>
<span class="gp">... </span> <span class="k">return</span> <span class="bp">NotImplemented</span>
<span class="gp">...</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">__hash__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">return</span> <span class="nb">hash</span><span class="p">((</span><span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span><span class="p">))</span>
<span class="gp">>>> </span><span class="n">ArtisanalClass</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="go">ArtisanalClass(a=1, b=2)</span>
</pre></div>
</div>
<p>which is quite a mouthful and it doesn’t even use any of <code class="docutils literal"><span class="pre">attrs</span></code>’s more advanced features like validators or defaults values.
Also: no tests whatsoever.
And who will guarantee you, that you don’t accidentally flip the <code class="docutils literal"><span class="pre"><</span></code> in your tenth implementation of <code class="docutils literal"><span class="pre">__gt__</span></code>?</p>
<p>It also should be noted that <code class="docutils literal"><span class="pre">attrs</span></code> is not an all-or-nothing solution.
You can freely choose which features you want and disable those that you want more control over:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nd">@attr</span><span class="o">.</span><span class="n">s</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="gp">... </span><span class="k">class</span> <span class="nc">SmartClass</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="gp">... </span> <span class="n">a</span> <span class="o">=</span> <span class="n">attr</span><span class="o">.</span><span class="n">ib</span><span class="p">()</span>
<span class="gp">... </span> <span class="n">b</span> <span class="o">=</span> <span class="n">attr</span><span class="o">.</span><span class="n">ib</span><span class="p">()</span>
<span class="gp">...</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">return</span> <span class="s2">"<SmartClass(a=</span><span class="si">%d</span><span class="s2">)>"</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,)</span>
<span class="gp">>>> </span><span class="n">SmartClass</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="go"><SmartClass(a=1)></span>
</pre></div>
</div>
<div class="admonition-summary admonition">
<p class="first admonition-title">Summary</p>
<p>If you don’t care and like typing, we’re not gonna stop you.</p>
<p>However it takes a lot of bias and determined rationalization to claim that <code class="docutils literal"><span class="pre">attrs</span></code> raises the mental burden on a project given how difficult it is to find the important bits in a hand-written class and how annoying it is to ensure you’ve copy-pasted your code correctly over all your classes.</p>
<p class="last">In any case, if you ever get sick of the repetitiveness and drowning important code in a sea of boilerplate, <code class="docutils literal"><span class="pre">attrs</span></code> will be waiting for you.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="index.html">
<img class="logo" src="_static/attrs_logo.svg" alt="Logo"/>
</a></p>
<h3><a href="index.html">Table Of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Why not…</a><ul>
<li><a class="reference internal" href="#tuples">…tuples?</a><ul>
<li><a class="reference internal" href="#readability">Readability</a></li>
<li><a class="reference internal" href="#extendability">Extendability</a></li>
</ul>
</li>
<li><a class="reference internal" href="#namedtuples">…namedtuples?</a></li>
<li><a class="reference internal" href="#dicts">…dicts?</a></li>
<li><a class="reference internal" href="#hand-written-classes">…hand-written classes?</a></li>
</ul>
</li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
<li>Previous: <a href="overview.html" title="previous chapter">Overview</a></li>
<li>Next: <a href="examples.html" title="next chapter"><code class="docutils literal"><span class="pre">attrs</span></code> by Example</a></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<div><input type="text" name="q" /></div>
<div><input type="submit" value="Go" /></div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
©2018, Hynek Schlawack.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 1.6.7</a>
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.8</a>
</div>
</body>
</html>
|