[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Python-Dev] Is static typing still optional?

On 29 January 2018 at 12:08, Guido van Rossum <guido at python.org> wrote:
> I think this is a good candidate for fine-tuning during the beta period.
> Though honestly Python's own rules for when a class is hashable or not are
> the root cause for the complexity here -- since we decided to implicitly set
> __hash__ = None when you define __eq__, it's hardly surprising that
> dataclasses are having a hard time making natural rules.

In Raymond's example, the problem is the opposite: data classes are
currently interpreting "hash=False" as "Don't add a __hash__
implementation" rather than "Make this unhashable". That
interpretation isn't equivalent due to object.__hash__ existing by
default. (Reviewing Eric's table again, I believe this problem still
exists in the 3.7b1 variant as well - I just missed it the first time
I read that)

I'd say the major argument in favour of Raymond's suggestion (i.e.
always requiring an explicit "hash=True" in the dataclass decorator
call if you want the result to be hashable) is that even if we *do*
come up with a completely consistent derivation rule that the
decorator can follow, most *readers* aren't going to know that rule.
It would become a Python gotcha question for tech interviews:

Which of the following class definitions are hashable and what is
their hash based on?:

    class A:
        field: int

    class B:
        field: int

    class C:
        field: int

   @dataclass(eq=False, frozen=True)
    class D:
        field: int

    @dataclass(eq=True, frozen=True)
    class E:
        field: int

    class F:
        field: int

    @dataclass(frozen=True, hash=True)
    class G:
        field: int

    @dataclass(eq=True, frozen=True, hash=True)
    class H:
        field: int

Currently the answers are:

- A: not hashable
- B: hashable (by identity) # Wat?
- C: hashable (by field hash)
- D: hashable (by identity) # Wat?
- E: hashable (by field hash)
- F: hashable (by field hash)
- G: hashable (by field hash)
- H: hashable (by field hash)

If we instead make the default "hash=False" (and interpret that as
meaning "Inject __hash__=None"), then you end up with the following
much simpler outcome that can be mapped directly to the decorator
"hash" parameter:

- A: not hashable
- B: not hashable
- C: not hashable
- D: not hashable
- E: not hashable
- F: hashable (by field hash)
- G: hashable (by field hash)
- H: hashable (by field hash)

Inheritance of __hash__ could then be made explicitly opt-in by way of
a "dataclasses.INHERIT" constant.


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia