You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As part of “Projectimport attrs” (see also #408) we get a unique opportunity to change default options/behaviors that grew over the years but couldn't be fixed due to backward-compatibility restrictions.
The rough plan is create a new, friendlier API on top of @attr.s and attr.ib() that won't go anywhere.
The following is cast in stone:
auto_attribs=True
consistent (Python 3-style) hashing behavior on every Python version
One thing that I just can't make up my mind is related to #223: should we make slots=True the default? I’m quite confident that in 99,9% of cases it's the right thing to do and will guide people to write better classes.
However on the other hand, our approach of rewriting classes breaks in certain scenarios, usually involving metaclasses.
So the question is, whether we want to tolerate a higher rate of bogus bug reports/help requests or make the default case nicer?
I welcome your input.
Finally a controversial idea: we could make import attrs Python 3 only. There isn't much baggage we'd get rid of but there is some and 2020 is less than a year ahead. It would also allow us to embrace enums as part of our API.
"we could make import attrs Python 3 only." -> +1.
"It would also allow us to embrace enums as part of our API." -> That would be awesome.
Reacted by Robbie Clarken, Stefan Wójcik, Chris Lasher, George Macon, Berislav Lopac, Yevhenii Kovalchuk, Rami Chowdhury, Tom Brown, Samuel Bishop, Max Kühn and 3 more
I absolutely agree with your point on slots=True, but I would recommend not making it the default. I'm always surprised by how many people have no idea that this amazing feature exists; you need to at the very least explain in the documentation what it is anyway, so it can be used to recommend its use and guide people to write better classes. 🙂
I'm also in favour of Python3-only import attrs.
What else is bugging you?
A question: My main issue with attrs has always been its decorator-based syntax and inability to instantiate using __init__. To be clear, it's my personal preference and not a general opinion on How Things Should Be Done [TM], but I've always liked the subclassing approach found in e.g. schematics. Also, I've often explained the concept of data classes (regardless of the implementation) as "ORM without the R", as it maps nicely with people's experience with Django and others. So I just have to ask, it there a possibility to have an optional method of implementing attrs as a parent/abstract class and/or a mixin?
In a world of AWS Lambda and other RAM-second based billing systems, making "slots=True" the default will literally save people money (since it uses less RAM, and avoids pointless dict creation for each instance).
If it fails, then folks are likely to get noisy exceptions that a few asked-and-answered questions on Stack Overflow will teach them to resolve with "slots=False".
Reacted by Brian Skinn, George Macon, Ryan Gabbard, Brandon H. Gomes, Shawn Morel and Peter Schutt
If it fails, then folks are likely to get noisy exceptions that a few asked-and-answered questions on Stack Overflow will teach them to resolve with "slots=False".
Could even seed SO with a couple of self-answered Q&A covering common error cases where slots=False is the fix.
If it fails, then folks are likely to get noisy exceptions that a few asked-and-answered questions on Stack Overflow will teach them to resolve with "slots=False".
I tend to agree; it just goes a bit against attrs’s principle of “just adding some stuff” to your classes.
Reacted by Chris Lasher, Doug Philips, Josh Kuhn, Samuel Bishop and Marc Rintsch
My feelpinion: frozen=True should be the default. Probably the option should be renamed to mutable, so you say mutable=True for when you know what you are doing.
My feelpinion: frozen=True should be the default. Probably the option should be renamed to mutable, so you say mutable=True for when you know what you are doing.
Disagree here. frozen=True only works for me in situations where (1) the entire contents of all instances will be known at instantiation-time in all cases, and (2) where no derived/calculated members will be needed (i.e., where @propertys are sufficient for all derived members because the calculations are fast).
I use attrs classes a lot in my data analysis work, and often I'll have something expensive I need to do with the arguments passed in at initialization. I absolutely want to do that expensive thing exactly once for each instance, and AFAIK I have to do it as something like:
I agree with frozen=False as the default, but kinda like using the term mutable, unless there is a significant difference between the meaning in attrs and Python in general. So I guess I would vote for the mutable=True as default.
Oh, and I think that @bskinn's argument against frozen=True equally applies to slots=True; I still think they should both be off by default. Unless I'm mistaken, slots applies to the attributes defined via attrs and no additional attributes can't be added, is that right?
The reason why it's called frozen is because that's how we call these things in Python (frozenset etc) and I don't want to open a new nomenclature out of purity. I've seen good things fail too often because of the stubbornness of its maintainers to be a good Python citizen.
And yes, slots=True prevents new attributes and you really shouldn't add ad-hoc attributes so that's a feature. If you need it, you can always say slots=False and the class has a warning attached to itself, that the attribute list is not comprehensive.
>>>fromfunctoolsimportpartial>>>>>>auto=partial(attrs, auto_attribs=True, auto_exc=True)
>>>>>>@auto(frozen=True) # additional parameters
... classFoo:
... foo: int
...
>>>@auto# no parameters and no parentheses
... classBar:
... bar: str
...
>>>Foo?
Initsignature: Foo(foo: int) ->None
...
>>>Bar?
Initsignature: Bar(bar: str) ->None
...
You can even stack these:
>>>frozen=partial(auto, frozen=True)
Given how easy and readable and pythonic this is, I think having the least magical defaults is very reasonable. Perhaps with the exception of cache_hash, are there reasons to not enable this by default for frozen instances?
Mypy is the bigger problem here, as it currently doesn't understand partial, and doesn't even understand auto = attrs; @auto; class ...
P.S. I personally would like to be able to drop parentheses on attrib like this:
Mypy is the bigger problem here, as it currently doesn't understand partial, and doesn't even understand auto = attrs; @auto; class ...
This partial application decorator-forwarding usecase can be managed with a micro-plugin for mypy - you basically need something that says:
import typing as ty
from mypy.plugin import Plugin, ClassDefContext
from mypy.plugins.attrs import attrs_class_maker_callback
MODULE_PATH_TO_YOUR_DECORATOR = "your.module.path.autoattrs"
class AutoAttrsDecoratorPlugin(Plugin):
def get_class_decorator_hook(
self, fullname: str
) -> ty.Optional[ty.Callable[[ClassDefContext], None]]:
def fwd_auto(cls_def_ctx: ClassDefContext):
if fullname == MODULE_PATH_TO_YOUR_DECORATOR:
attr_class_maker_callback(cls_def_ctx, True)
if fullname == MODULE_PATH_TO_YOUR_DECORATOR:
return fwd_auto
return None
def plugin(_version):
return AutoAttrsDecoratorPlugin
What this doesn't support is things like frozen - however the mypy attrs plugin itself could be trivially rewritten to expose those parameters directly for these sorts of mypy plugins.
@hynek do you think mypy would accept a PR to make the built-in plugin more directly reusable? This whole mess of plugin code could probably be rewritten to be a one-liner that 'registers' a decorator with the mypy plugin.
@hynek do you think mypy would accept a PR to make the built-in plugin more directly reusable? This whole mess of plugin code could probably be rewritten to be a one-liner that 'registers' a decorator with the mypy plugin.
I've opened an issue asking for this almost two years ago: python/mypy#5406
one thing which breaks some functional programming stuff, is the fact you can't do bracketed lookup with attrs objects
a = {"name": "a"}
a["name"]
# a
@attrs
class Person:
name: str = attrib()
a = Person(name="a")
a["name"]
# TypeError: 'Person' object is not subscriptable
means you can't do functional lenses, lookPath, assocPath, dissocPath, evolvePath, are all legit, makes me miss Ramda!
how can we make attrs better for functional programming?
Activity
sfermigier commentedon Jan 20, 2019
"we could make import attrs Python 3 only." -> +1.
"It would also allow us to embrace enums as part of our API." -> That would be awesome.
berislavlopac commentedon Jan 20, 2019
I absolutely agree with your point on
slots=True, but I would recommend not making it the default. I'm always surprised by how many people have no idea that this amazing feature exists; you need to at the very least explain in the documentation what it is anyway, so it can be used to recommend its use and guide people to write better classes. 🙂I'm also in favour of Python3-only
import attrs.A question: My main issue with
attrshas always been its decorator-based syntax and inability to instantiate using__init__. To be clear, it's my personal preference and not a general opinion on How Things Should Be Done [TM], but I've always liked the subclassing approach found in e.g.schematics. Also, I've often explained the concept of data classes (regardless of the implementation) as "ORM without the R", as it maps nicely with people's experience with Django and others. So I just have to ask, it there a possibility to have an optional method of implementingattrsas a parent/abstract class and/or a mixin?Thanks for all the amazing work!
hynek commentedon Jan 20, 2019
No, but you could probably use metaclasses and our programmatic interface (make_class) to build it yourself?
ncoghlan commentedon Jan 20, 2019
In a world of AWS Lambda and other RAM-second based billing systems, making "slots=True" the default will literally save people money (since it uses less RAM, and avoids pointless dict creation for each instance).
If it fails, then folks are likely to get noisy exceptions that a few asked-and-answered questions on Stack Overflow will teach them to resolve with "slots=False".
bskinn commentedon Jan 20, 2019
Could even seed SO with a couple of self-answered Q&A covering common error cases where
slots=Falseis the fix.hynek commentedon Jan 20, 2019
I tend to agree; it just goes a bit against
attrs’s principle of “just adding some stuff” to your classes.jml commentedon Jan 20, 2019
My feelpinion:
frozen=Trueshould be the default. Probably the option should be renamed tomutable, so you saymutable=Truefor when you know what you are doing.bskinn commentedon Jan 20, 2019
Disagree here.
frozen=Trueonly works for me in situations where (1) the entire contents of all instances will be known at instantiation-time in all cases, and (2) where no derived/calculated members will be needed (i.e., where@propertys are sufficient for all derived members because the calculations are fast).I use attrs classes a lot in my data analysis work, and often I'll have something expensive I need to do with the arguments passed in at initialization. I absolutely want to do that expensive thing exactly once for each instance, and AFAIK I have to do it as something like:
With
frozen=True, I can't setself.expensive_derived_varin__attrs_post_init__.That being said, I have no idea whether my needs here at all represent the majority of use-cases.
hynek commentedon Jan 20, 2019
I love myself some value types but
frozen=Trueain’t happening. :)It just doesn’t fit Python too well. But IIRC we’ve decided on an own function for it?
berislavlopac commentedon Jan 20, 2019
I agree with
frozen=Falseas the default, but kinda like using the termmutable, unless there is a significant difference between the meaning inattrsand Python in general. So I guess I would vote for themutable=Trueas default.Oh, and I think that @bskinn's argument against
frozen=Trueequally applies toslots=True; I still think they should both be off by default. Unless I'm mistaken,slotsapplies to the attributes defined viaattrsand no additional attributes can't be added, is that right?berislavlopac commentedon Jan 20, 2019
Possibly, will look into it.
hynek commentedon Jan 21, 2019
The reason why it's called
frozenis because that's how we call these things in Python (frozenset etc) and I don't want to open a new nomenclature out of purity. I've seen good things fail too often because of the stubbornness of its maintainers to be a good Python citizen.And yes,
slots=Trueprevents new attributes and you really shouldn't add ad-hoc attributes so that's a feature. If you need it, you can always sayslots=Falseand the class has a warning attached to itself, that the attribute list is not comprehensive.jml commentedon Jan 21, 2019
Did we? Where can I read about that?
hynek commentedon Jan 21, 2019
#408 (comment)
It's gonna be
@attrs.defineand@attrs.frozen. Depending on https://hynek.me/about/#gratitude I might add anattrs.mutablealias fordefine. ;)33 remaining items
oakkitten commentedon Jun 25, 2020
You can simply use
partial:You can even stack these:
Given how easy and readable and pythonic this is, I think having the least magical defaults is very reasonable. Perhaps with the exception of
cache_hash, are there reasons to not enable this by default for frozen instances?Mypy is the bigger problem here, as it currently doesn't understand
partial, and doesn't even understandauto = attrs; @auto; class ...P.S. I personally would like to be able to drop parentheses on
attriblike this:hynek commentedon Jun 29, 2020
Sadly the mypy angle ruined this approach that I used to use all the time. :(
petergaultney commentedon Jun 29, 2020
This partial application decorator-forwarding usecase can be managed with a micro-plugin for mypy - you basically need something that says:
What this doesn't support is things like frozen - however the mypy attrs plugin itself could be trivially rewritten to expose those parameters directly for these sorts of mypy plugins.
@hynek do you think mypy would accept a PR to make the built-in plugin more directly reusable? This whole mess of plugin code could probably be rewritten to be a one-liner that 'registers' a decorator with the mypy plugin.
hynek commentedon Jun 29, 2020
I've opened an issue asking for this almost two years ago: python/mypy#5406
FWIW, #650 is of interest here too.
oakkitten commentedon Jun 30, 2020
by the way, while assignment doesn't make mypy happy, importing under a different name does:
bionicles commentedon Jul 19, 2020
one thing which breaks some functional programming stuff, is the fact you can't do bracketed lookup with attrs objects
means you can't do functional lenses, lookPath, assocPath, dissocPath, evolvePath, are all legit, makes me miss Ramda!
how can we make attrs better for functional programming?
berislavlopac commentedon Jul 19, 2020
You can always implement your own
__getitem__...bionicles commentedon Jul 19, 2020
worked like a charm, thanks for reminding me, that would be a fun default to add!
hynek commentedon Aug 14, 2020
Please check out #666 y'all.
hynek commentedon May 5, 2021
So FTR, NG APIs will ship as stable in 21.1, now is the last chance to ask for changes in APIs that are annoying.
So far there's #705 but I'm sure there might be more?
To be clear: define/mutable/frozen/field are set in stone now. But there's a buttload of other APIs that may benefit from polish?
botant commentedon May 6, 2021
I just want to say thanks for
order=Falseby default. 🙏