Hi!
I have unsucessfully tried to define a default value by referencing other attributes. I'm sure the code below doesnt' work for some obvious or fundamental reason, but I would be grateful for comments on how to do something like it:
import attr
from attr.validators import instance_of
import datetime
@attr.s
class Something:
some_date = attr.ib(validator=instance_of(datetime.date))
some_number = attr.ib(convert=float)
name = attr.ib(validator=instance_of(str),
default="Generic Name {0} - {1}%".format(
some_date.strftime("%d-%b-%Y"),
some_number * 100)
)
s = Something(some_date=datetime.date.today(), some_number=0.375)
I included the .strftime() conversion to highlight that name doesn't see a float and a date, but a _CountingAttr object, hence I get an AttributeError (and a TypeError for some_number * 100). Since I can't reference self either, what would be the correct way to do this?
Hi,
for now I suggest using __attrs_post_init__ (http://attrs.readthedocs.io/en/stable/examples.html?highlight=attrs_post_init#other-goodies). The linked example is basically what you want.
I have wanted to do this several times. For example, you have a test of a function which takes many inputs. You want to test this function with many different cases. So (of course) you create a quick e.g. @attr.s class TestInput:... for that. In many cases you want some attribute to have a default depending on other attributes, but still be able to override it. The existing options are not so good for this:
these for this but it's cumbersome.__attrs_post_init__ is not good because you can't override it.What I'd want, following the ad-hoc validators example, is something like this:
@attr.s
class C:
x = attr.ib()
y = attr.ib()
z = attr.ib()
@z.default
def z_default(self, attribute):
return self.x + self.y
This is sensitive to the initialization order. However, if you have a stateful factory, the order is already important, so this is not new. And the natural order is the definition order which is intuitive.
Another issue is that this makes it possible to specify multiple defaults. I would just raise an error if this is detected.
Final issue I can think of is that an occasional user might confuse this with a property, while it does not behave like a property:
But I think "default" is clear on the behavior it has.
At first glance I like the proposed API :)
I guess we could have a lot fun with decorators based on _CountingAttr.
I think I want this in 17.1 but it entirely depends on the goodwill of reviewers (most likely @Tinche – I gotta shanghai some innocent souls at PyCon).
Most helpful comment
I have wanted to do this several times. For example, you have a test of a function which takes many inputs. You want to test this function with many different cases. So (of course) you create a quick e.g.
@attr.s class TestInput:...for that. In many cases you want some attribute to have a default depending on other attributes, but still be able to override it. The existing options are not so good for this:thesefor this but it's cumbersome.__attrs_post_init__is not good because you can't override it.What I'd want, following the ad-hoc validators example, is something like this:
This is sensitive to the initialization order. However, if you have a stateful factory, the order is already important, so this is not new. And the natural order is the definition order which is intuitive.
Another issue is that this makes it possible to specify multiple defaults. I would just raise an error if this is detected.
Final issue I can think of is that an occasional user might confuse this with a property, while it does not behave like a property:
But I think "default" is clear on the behavior it has.