Hi Eric,

A few quick comments:

Why do you even have a hash= argument on individual fields? For the whole class, I can imagine you might want to explicitly mark a whole class as unhashable, but it seems like the only thing you can do with the field-level hash= argument is to create a class where the __hash__ and __eq__ take different fields into account, and why would you ever want that?

Though honestly I can see a reasonable argument for removing the class-level hash= option too. And even if you keep it you might want to error on some truly nonsensical options like defining __hash__ without __eq__. (Also watch out that Python's usual rule about defining __eq__ blocking the inheritance of __hash__ does not kick in if __eq__ is added after the class is created.)

I've sometimes wished that attrs let me control whether it generated equality methods (eq/ne/hash) separately from ordering methods (lt/gt/...). Maybe the cmp= argument should take an enum with options none/equality-only/full?

The "why not attrs" section kind of reads like "because it's too popular and useful"?

-n

On Sep 8, 2017 08:44, "Eric V. Smith" <eric@trueblade.com> wrote:
Oops, I forgot the link. It should show up shortly at https://www.python.org/dev/peps/pep-0557/.

Eric.


On 9/8/17 7:57 AM, Eric V. Smith wrote:
I've written a PEP for what might be thought of as "mutable namedtuples
with defaults, but not inheriting tuple's behavior" (a mouthful, but it
sounded simpler when I first thought of it). It's heavily influenced by
the attrs project. It uses PEP 526 type annotations to define fields.
>From the overview section:

@dataclass
class InventoryItem:
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

Will automatically add these methods:

  def __init__(self, name: str, unit_price: float, quantity_on_hand: int
= 0) -> None:
      self.name = name
      self.unit_price = unit_price
      self.quantity_on_hand = quantity_on_hand
  def __repr__(self):
      return
f'InventoryItem(name={self.name!r},unit_price={self.unit_price!r},quantity_on_hand={self.quantity_on_hand!r})'

  def __eq__(self, other):
      if other.__class__ is self.__class__:
          return (self.name, self.unit_price, self.quantity_on_hand) ==
(other.name, other.unit_price, other.quantity_on_hand)
      return NotImplemented
  def __ne__(self, other):
      if other.__class__ is self.__class__:
          return (self.name, self.unit_price, self.quantity_on_hand) !=
(other.name, other.unit_price, other.quantity_on_hand)
      return NotImplemented
  def __lt__(self, other):
      if other.__class__ is self.__class__:
          return (self.name, self.unit_price, self.quantity_on_hand) <
(other.name, other.unit_price, other.quantity_on_hand)
      return NotImplemented
  def __le__(self, other):
      if other.__class__ is self.__class__:
          return (self.name, self.unit_price, self.quantity_on_hand) <=
(other.name, other.unit_price, other.quantity_on_hand)
      return NotImplemented
  def __gt__(self, other):
      if other.__class__ is self.__class__:
          return (self.name, self.unit_price, self.quantity_on_hand) >
(other.name, other.unit_price, other.quantity_on_hand)
      return NotImplemented
  def __ge__(self, other):
      if other.__class__ is self.__class__:
          return (self.name, self.unit_price, self.quantity_on_hand) >=
(other.name, other.unit_price, other.quantity_on_hand)
      return NotImplemented

Data Classes saves you from writing and maintaining these functions.

The PEP is largely complete, but could use some filling out in places.
Comments welcome!

Eric.

P.S. I wrote this PEP when I was in my happy place.

_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: https://mail.python.org/mailman/options/python-dev/njs%40pobox.com