-
-
Notifications
You must be signed in to change notification settings - Fork 407
Description
Hi Everyone,
First, thanks for the great tool -- I really love writing Python code with it!
I find myself doing things like this a lot when I want b and c to only run when called (and then only once with the result cached).
import attr
from cached_property import cached_property
@attr.s
class MyClass:
a = attr.ib(
validator=attr.validators.instance_of(int),
converter=int,
)
@cached_property
def b(self):
return str(self.a) * 3
@cached_property
def c(self):
return int(self.b) * 3
foo = MyClass(a='1')
print(f'foo.a: {foo.a}')
print(f'foo.b: {foo.b}')
print(f'foo.c: {foo.c}')
But then I have to "break out of attrs" and loose the other goodness it provides. I know I can do something like this:
import attr
@attr.s
class MyClass:
a = attr.ib(
validator=attr.validators.instance_of(int),
converter=int,
)
b = attr.ib(
validator=attr.validators.instance_of(str),
default=attr.Factory(
takes_self=True,
factory=lambda self: str(self.a) * 3,
)
)
c = attr.ib(
validator=attr.validators.instance_of(int),
default=attr.Factory(
takes_self=True,
factory=lambda self: int(self.b) * 3,
)
)
foo = MyClass(a='1')
print(f'foo.a: {foo.a}')
print(f'foo.b: {foo.b}')
print(f'foo.c: {foo.c}')
But then b and c get "run" or "built" every time, even if they aren't needed (and I'm obviously assuming in reality they do more work they my toy example here shows).
What I really want to do is something like:
import attr
@attr.s
class MyClass:
a = attr.ib(
validator=attr.validators.instance_of(int),
converter=int,
)
b = attr.ib(
validator=attr.validators.instance_of(str),
lazy=True,
default=attr.Factory(
takes_self=True,
factory=lambda self: str(self.a) * 3,
)
)
c = attr.ib(
validator=attr.validators.instance_of(int),
lazy=True,
default=attr.Factory(
takes_self=True,
factory=lambda self: int(self.b) * 3,
)
)
foo = MyClass(a='1')
print(f'foo.a: {foo.a}')
print(f'foo.b: {foo.b}')
print(f'foo.c: {foo.c}')
Or even better, something that doesn't require me to specify takes_self=True every time... maybe something like:
import attr
@attr.s
class MyClass:
a = attr.ib(
validator=attr.validators.instance_of(int),
converter=int,
)
b = attr.ib(
validator=attr.validators.instance_of(str),
lazy=True,
builder=attr.Builder(
factory=lambda self: str(self.a) * 3,
)
)
c = attr.ib(
validator=attr.validators.instance_of(int),
lazy=True,
builder=attr.Builder(
factory=lambda self: int(self.b) * 3,
)
)
foo = MyClass(a='1')
print(f'foo.a: {foo.a}')
print(f'foo.b: {foo.b}')
print(f'foo.c: {foo.c}')
Thoughts on if something like this would be possible? I haven't dug into guts of attrs yet enough to know if it's doable, but it seems like it would be a great enhancement if something like that is possible. I came not too long ago from a non-Python environment where we built classes almost exactly like I show in my last example above, and it's a super efficient and expressive way to do things.
Thanks!