python-attrs / attrs

Python Classes Without Boilerplate
https://www.attrs.org/
MIT License
5.28k stars 368 forks source link

evolve() and attr.ib(init=False, default="foo") #251

Open hynek opened 7 years ago

hynek commented 7 years ago

As pointed out in https://github.com/ericvsmith/dataclasses/issues/42#issuecomment-330198369 attr.evolve() behaves rather weird if an attribute has

  1. init=False,
  2. a default/__attrs_post_init__, and
  3. attribute(s) are modified on the instance.

The new resulting object will always have the default value.

I think I can live with that limitation if we document it and it's probably a good reason to resolve #207 with sticking assoc around with a better name?


Giving it further thought, the raison d'être for evolve are immutable objects…so I think it's fine to just document this edge case and leave it be?

ljluestc commented 1 year ago

import attr

@attr.s class MyDataClass: init_value: int = attr.ib(init=False) default_value: int = attr.ib(default=0)

def __attrs_post_init__(self):
    self.init_value = 42  # Modifying an attribute in __attrs_post_init__

def evolve_example(): original_instance = MyDataClass() evolved_instance = attr.evolve(original_instance, init_value=100)

return evolved_instance

Documentation for the edge case

print(""" As pointed out in ericvsmith/dataclasses#42 (comment), attr.evolve() behaves in a specific way when an attribute has:

  1. init=False,
  2. a default/attrs_post_init, and
  3. attribute(s) are modified on the instance.

The new resulting object will always have the default value for that attribute. This behavior is because evolve is primarily designed for immutable objects.

Given the nature of evolve and its purpose for immutable objects, it is acceptable to document this edge case and leave it as is. """)

Example usage of attr.evolve()

evolved_obj = evolve_example() print(f"Evolved object: {evolved_obj}")