<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On 13 October 2017 at 04:21, Martin Teichmann <span dir="ltr"><<a href="mailto:lkb.teichmann@gmail.com" target="_blank">lkb.teichmann@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">For me, the dataclasses were a typical example for inheritance, to be<br>
more precise, for metaclasses. I was astonished to see them<br>
implemented using decorators, and I was not the only one, citing<br>
Guido:<br>
<span class=""><br>
> I think it would be useful to write 1-2 sentences about the problem with<br>
> inheritance -- in that case you pretty much have to use a metaclass, and the<br>
> use of a metaclass makes life harder for people who want to use their own<br>
> metaclass (since metaclasses don't combine without some manual<br>
> intervention).<br>
<br>
</span>Python is at a weird point here. At about every new release of Python,<br>
a new idea shows up that could be easily solved using metaclasses, yet<br>
every time we hesitate to use them, because of said necessary manual<br>
intervention for metaclass combination.<br></blockquote><div><br></div><div>Metaclasses currently tend to serve two distinct purposes:</div><div><br></div><div>1. Actually altering the runtime behaviour of a class and its children in non-standard ways (e.g. enums, ABCs, ORMs)</div>2. Boilerplate reduction in class definitions, reducing the amount of code you need to write as the author of that class<div><br></div><div>Nobody has a problem with using metaclasses for the first purpose - that's what they're for.</div><div><br></div><div>It's the second use case where they're problematic, as the fact that they're preserved on the class becomes a leaky implementation detail, and the lack of a JIT in CPython means they can also end up being expensive from a runtime performance perspective.</div><div><br></div><div>Mixin classes have the same problem: something that the author may want to handle as an internal implementation detail leaks through to the runtime state of the class object.<br></div> </div><div class="gmail_quote">Code generating decorators like functools.total_ordering and dataclasses.dataclass (aka attr.s) instead aim at the boilerplate reduction problem directly: they let you declare in the class body the parts that you need to specify as the class designer, and then fill in at class definition time the parts that can be inferred from that base.</div><div class="gmail_quote"><br></div><div class="gmail_quote">If all you have access to is the runtime class, it behaves almost exactly as if you had written out all the autogenerated methods by hand (there may be subtle differences in the method metadata, such as the values of `__qualname__` and `__globals__`).</div><div class="gmail_quote"><br></div><div class="gmail_quote">Such decorators also do more work at class definition time in order to reduce the amount of runtime overhead introduced by reliance on chained method calls in a non-JITted Python runtime.</div><div class="gmail_quote"><br></div><div class="gmail_quote">As such, the code generating decorators have a clear domain of applicability: boilerplate reduction for class definitions without impacting the way instances behave (other than attribute and method injection), and without implicitly impacting subclass definitions (other than through regular inheritance behaviour).</div><div class="gmail_quote"><br></div><div class="gmail_quote">As far as the dataclass interaction with `__slots__` goes, that's a problem largely specific to slots (and `__metaclass__` before it), in that they're the only characteristics of a class definition that affect how CPython allocates memory for the class object itself (the descriptors for the slots are stored as a pointer array after the class struct, rather than only in the class dict).</div><div class="gmail_quote"><br></div><div class="gmail_quote">Given PEP 526 variable annotations, __slots__ could potentially benefit from a __metaclass__ style makeover, allowing an "infer_slots=True" keyword argument to type.__new__ to request that the list of slots be inferred from __annotations__ (Slot inference would conflict with setting class level default values, but that's a real conflict, as you'd be trying to use the same name on the class object for both the slot descriptor and the default value)<br></div><div class="gmail_quote"><br></div><div class="gmail_quote"><div>Cheers,</div><div>Nick.<br></div><br></div>-- <br><div class="gmail_signature" data-smartmail="gmail_signature">Nick Coghlan   |   <a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>   |   Brisbane, Australia</div>
</div></div>