Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

attr default based on other attributes #165

Closed
2mol opened this issue Mar 8, 2017 · 5 comments
Closed

attr default based on other attributes #165

2mol opened this issue Mar 8, 2017 · 5 comments
Labels

Comments

@2mol
Copy link

2mol commented Mar 8, 2017

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?

@Tinche
Copy link
Member

Tinche commented Mar 8, 2017

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.

@bluetech
Copy link

bluetech commented Mar 8, 2017

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:

  • I think you can use 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:

  • The value is persisted.
  • In mutable objects, the value doesn't change if the dependencies change, unlike a dynamically-computed property.

But I think "default" is clear on the behavior it has.

@Tinche
Copy link
Member

Tinche commented Mar 9, 2017

At first glance I like the proposed API :)

@hynek
Copy link
Member

hynek commented Mar 22, 2017

I guess we could have a lot fun with decorators based on _CountingAttr.

@hynek
Copy link
Member

hynek commented May 12, 2017

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).

hynek added a commit that referenced this issue May 16, 2017
* Add takes_self to Factory and @_CountingAttr.default

Fixes #165

* Add dark @Tinche magic
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants