Hi,
working on making Attributes immutable, and also thinking about immutability in Python in general, to evaluate how hard would it be to actually allow attrs to create immutable classes.
(Side note: adding __slots__ to the Attribute class is probably a significant win for code bases with a lot of attrs classes, and can be trivially done by renaming the existing field _attributes to __slots__. This will be in the pull request too.)
Googling and playing around: the only way of making truly immutable classes in Python, I think, is by subclassing namedtuple. I'm not a huge fan of this approach though. My biggest philosophical objection is the fact that if this is done, isinstance(obj, tuple) and isinstance(obj, typing.Sequence) will be true. Instances of namedtuple subclasses are basically sequences/tuples. To me this is iffy.
Another approach is by disabling __setattr__, preferably combined with __slots__ so the __dict__ can't be modified directly. This isn't bulletproof, and __init__ basically depends on it not being bulletproof to set the initial values. The instances can still be changed by doing object.__setattr__(obj, 'name', val). If this is good enough, this approach is relatively straightforward.
What are your thoughts on this subject?
Here's my playground: https://github.com/Tinche/attrs/tree/feature/immutable-attributes
Another approach might be using read-only properties?
(but the setattr hack is good enough. for sake of completion, in characteristic I did this this way: https://github.com/hynek/characteristic/blob/master/characteristic.py#L442-L482 💃
🎉
Ok, now that the Attribute class has been optimized, what do you think about supporting immutability for user attr classes themselves? I think we have three logical courses of action.
@attr.s
class MyCoolClass(object):
x = attr.ib()
y = attr.ib(immutable=True)
Attributes.@attr.s(immutable=True)
class MyCoolClass(object):
x = attr.ib()
y = attr.ib()
We already have attr.assoc to deal with fully immutable classes, so that's nice.
I volunteer to do the work if we decide to implement something, of course.
@glyph is gonna be very excited. :)
Def class level. And I kind of think it should be a separate decorator: @immutable . Opinions?
Agreed on both counts. We will probably want to support a different set of options (do immutable instances without slots make sense?) and maybe do different validations, so a separate decorator makes sense.
Can we come up with a fancier name than @immutable though? @frozen? (precedent would be frozenset).
Naming-wise, I was thinking @value.
(Also, remember those backticks when you're talking about decorators; whoever got the handle "classmethod" on github probably gets a million notifications a second ;o))
Hm so value would be the pure but way to generic name, immutable the explicit one, and frozen the Pythonic one. :/
At this point I'm tending to frozen because attrs is confusing enough for beginners. More opinions?
Implementation wise I'd suspect that object.setattr is slow and it would be wiser to seal off setattr after initialization. Benchmarks welcome. :)
@tinche you still wanna tackle this?
Yeah, but I can't commit to a time schedule right now (might be a week or two). Itching to give it a go yourself?
I also don't have a strong opinion on the name. value was my first idea, I guess since I've written Clojure and they like to brag on their persistent (immutable) data structures and how they can be treated like values (i.e. semantically no difference between an integer and an immutable dict/map).
On the other hand, I'm pretty sure this would be lost on basically all my Python-writing colleagues at work, who have not dabbled in functional programming at all as far as I know. On the third hand, I don't know if frozen would be 100% clear either. ¯_(ツ)_/¯ naming things sucks.
@hynek when you have a final ruling, let me know :)
I feel like whatever I decide here, I’ll regret later. :(
I’ve spend the past three days apologizing for .s/.ib so I kind of don’t want to rush into more regret. _sigh_
But I think @immutable is out because it’s too long.
Don't apologize for .s/.ib. It's like Python's whitespace indentation; you get used to it and realize it's brilliant. Almost everyone who actually uses attrs for a few days doesn't bother even using the srs bsns aliases.
Most helpful comment
Don't apologize for
.s/.ib. It's like Python's whitespace indentation; you get used to it and realize it's brilliant. Almost everyone who actually usesattrsfor a few days doesn't bother even using the srs bsns aliases.