Eric V. Smith <e...@trueblade.com> added the comment:

Here is my proposal for making it easier for the user to supply dunder methods 
that dataclasses would otherwise automatically add to the class.

For all of these cases, when I talk about init=, repr=, eq=, order=, hash=, or 
frozen=, I'm referring to the parameters to the dataclass decorator.

When checking if a dunder method already exists, I mean check for an entry in 
the class's __dict__. I never check to see if a member is defined in a base 
class.

Let's get the easy ones out of the way first.

__init__
If __init__ exists or init=False, don't generate __init__.

__repr__
If __repr__ exists or repr=False, don't generate __repr__.

__setattr__
__delattr__
If frozen=True and either of these methods exist, raise a TypeError. These 
methods are needed for the "frozen-ness" of the class to be implemented, and if 
they're already set then that behavior can't be enforced.

__eq__
If __eq__ exists or eq=False, don't generate __eq__.

And now the harder ones:

__ne__
I propose to never generate a __ne__ method. Python will call __eq__ and negate 
the result for you. If you feel you really need a non-matching __ne__, then you 
can write it yourself without interference from dataclasses.

__lt__
__le__
__gt__
__ge__
I propose to treat each of these individually, but for each of them using the 
value of the order= parameter. So:
If __lt__ exists or order=False, don't generate __lt__.
If __le__ exists or order=False, don't generate __le__.
If __gt__ exists or order=False, don't generate __gt__.
If __ge__ exists or order=False, don't generate __ge__.
If for some crazy reason you want to define some of these but not others, then 
set order=False and write your desired methods.

__hash__
Whether dataclasses might generate __hash__ depends on the values of hash=, 
eq=, and frozen=. Note that the default value of hash= is None. See below for 
an explanation.

If hash=False, never generate __hash__. If hash=True, generate __hash__ unless 
it already exists.

If hash=None (the default), then use this table to decide whether and how to 
generate __hash__:
eq=?    frozen=?        __hash__
False   False           do not generate __hash__
False   True            do not generate __hash__
True    False           set __hash__ to None unless it already exists
True    True            generate __hash__ unless it already exists
                         and is None

Note that it's almost always a bad idea to specify hash=True or hash=False. The 
action based on the above table (where hash=None, the default), is usually the 
correct behavior.

One special case to recognize is if the class defines a __eq__. In this case, 
Python will assign __hash__=None before the dataclass decorator is called. The 
decorator cannot distinguish between these two cases (except possibly by using 
the order of __dict__ keys, but that seems overly fragile):

@dataclass
class A:
    def __eq__(self, other): pass

@dataclass
class B:
    def __eq__(self, other): pass
    __hash__ = None

This is the source of the last line in the above table: for a dataclass where 
eq=True, frozen=True, and hash=None, if __hash__ is None it will still be 
overwritten. The assumption is that this is what the user wants, but it's a 
tricky corner case. It also occurs if setting hash=True and defining __eq__. 
Again, it's not expected to come up in normal usage.

----------
keywords:  -patch

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue32513>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to