Attrs: Thoughts on immutability

Created on 11 Aug 2016  Â·  15Comments  Â·  Source: python-attrs/attrs

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

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 uses attrs for a few days doesn't bother even using the srs bsns aliases.

All 15 comments

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.

  • Don't add anything to attrs, let users deal with immutability themselves somehow. Maybe add a recipe or two to the docs?
  • Support immutability on a per-attribute basis. I guess you'd be able to declare certain attributes as immutable, then we'd handle it somehow (i.e. generate properties without setters, but technical stuff not important right now)
@attr.s
class MyCoolClass(object):
    x = attr.ib()
    y = attr.ib(immutable=True)
  • Support immutability on a class level. Allow users to declare an entire class as immutable and that's that. Technically, we'd probably use the setattr switcharoo thing like for 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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

madig picture madig  Â·  3Comments

Nicholas-Mitchell picture Nicholas-Mitchell  Â·  15Comments

aragilar picture aragilar  Â·  17Comments

sky-code picture sky-code  Â·  7Comments

hynek picture hynek  Â·  9Comments