Making immutable instances

B

Ben Finney

Howdy all,

How can a (user-defined) class ensure that its instances are
immutable, like an int or a tuple, without inheriting from those
types?

What caveats should be observed in making immutable instances?
 
G

Giovanni Bajo

Ben said:
How can a (user-defined) class ensure that its instances are
immutable, like an int or a tuple, without inheriting from those
types?

What caveats should be observed in making immutable instances?


In short, you can't. I usually try harder to derive from tuple to achieve this
(defining a few read-only properties to access item through attributes). Using
__slots__ is then required to avoid people adding attributes to the instance.

In fact, I don't know the rationale. I would think it would be a great addition
to be able to say __immutable__ = True or something like that. Or at least, I'd
be grateful if someone explained me why this can't or shouldn't be done.
 
M

Mike Meyer

Giovanni Bajo said:
In short, you can't. I usually try harder to derive from tuple to achieve this
(defining a few read-only properties to access item through attributes). Using
__slots__ is then required to avoid people adding attributes to the instance.

Note that this property of __slots__ is an implementation detail. You
can't rely on it working in the future.

I'm curious as to why you care if people add attributes to your
"immutable" class. Personally, I consider that instances of types
don't let me add attributes to be a wart.

<mike
 
G

Guest

Mike Meyer said:
I'm curious as to why you care if people add attributes to your
"immutable" class. Personally, I consider that instances of types
don't let me add attributes to be a wart.

I agree. To me it seems like the OP kind of wants to implement
encapsulation from the back way.

Anyway, as he doesn't mention anything about using this immutability for
any kind of optimisation, I assume it's about "Control".
 
B

Ben Finney

Mike Meyer said:
Note that this property of __slots__ is an implementation detail.
You can't rely on it working in the future.

That's one thing I meant when I asked about caveats: implementation
warnings. Thanks.

Are there other, more dependable, ways of making immutable objects?
I'm curious as to why you care if people add attributes to your
"immutable" class. Personally, I consider that instances of types
don't let me add attributes to be a wart.

And this is another meaning: style caveats. I genuinely want to
understand these issues.

I refer you to a similar question: do you consider it a wart that you
can't add attributes to instances of Python's built-in types?
Traceback (most recent call last):
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'int' object has no attribute 'base'

Is this a wart? Why?

If it's not a wart, why would it be a wart for user-defined types to
have the same behaviour?
 
M

Mike Meyer

Ben Finney said:
Are there other, more dependable, ways of making immutable objects?

Your answer can be found at <URL:
http://www.cs.bgu.ac.il/~omri/Humor/write_in_c.html >
I refer you to a similar question: do you consider it a wart that you
can't add attributes to instances of Python's built-in types?

Didn't I just say that I thought it was a wart?
Is this a wart? Why?

Because it means I have to wrap them to if I want a typefoo with an
extra attribute. I don't have to do that for classes. It makes builtin
types different from user-defined classes in ways other than
performance, which is a bad thing.
If it's not a wart, why would it be a wart for user-defined types to
have the same behaviour?

It's a wart because user-defined classes *don't* have the same
behavior.

<mike
 
A

Alex Martelli

Ben Finney said:
How can a (user-defined) class ensure that its instances are
immutable, like an int or a tuple, without inheriting from those
types?

You can make a good start by defining __setattr__, __delattr__ (and
__setitem__ and __delitem__ if your class is a container) to raise
exceptions. Of course, these restrictions can be easily worked around
by a sufficiently determined attacker... but if you have to think of the
user of your code as an attacker, you've got worse problems than this
trifling one.

Do not define any of the in-place operator special methods, such as
__iadd__ and friends (or, if you're inheriting from a class that does
define them, override them to raise exceptions).

What caveats should be observed in making immutable instances?

Remember that your redefined __setattr__ IS "in place" even when you're
initializing your istance, so remember to delegate attribute setting to
the superclass (the other special methods mentioned above are less
likely to byte you).

You will probably want to define __hash__ and __eq__ if you're going to
the trouble of making instances immutable.


Alex
 
B

Ben Finney

Alex Martelli said:
Ben Finney said:
How can a (user-defined) class ensure that its instances are
immutable, like an int or a tuple, without inheriting from those
types?

You can make a good start by defining __setattr__, __delattr__ (and
__setitem__ and __delitem__ if your class is a container) to raise
exceptions.
[...]
Do not define any of the in-place operator special methods, such as
__iadd__ and friends (or, if you're inheriting from a class that
does define them, override them to raise exceptions).

That sounds more Pythonic than hacking __slots__. Thanks.
Remember that your redefined __setattr__ IS "in place" even when
you're initializing your istance, so remember to delegate attribute
setting to the superclass (the other special methods mentioned above
are less likely to byte you).

So, for a class that needs to set attributes in __init__ (but after
that, become immutable), how do I get around this? Should I make a
_FooFunctionality class, and then inherit from that to make Foo as the
immutable class that actually gets exported?
You will probably want to define __hash__ and __eq__ if you're going
to the trouble of making instances immutable.

Good tip, thanks.
 
B

Ben Finney

Alex Martelli said:
Ben Finney said:
How can a (user-defined) class ensure that its instances are
immutable, like an int or a tuple, without inheriting from those
types?

[...]
Of course, these restrictions can be easily worked around by a
sufficiently determined attacker... but if you have to think of the
user of your code as an attacker, you've got worse problems than
this trifling one.

I've probably stumbled across something that people often want to do
for ill-considered reasons; I don't really understand the reaction I'm
getting.

Why is "I want to make objects immutable" seen as "I don't trust my
users"? Are Python's existing immutable types also seen the same way?
If not, why the distinction?

For context, I'm intending for the immutability of class instances to
be a properly documented part of the class protocol. Does that sound
more reasonable?
 
A

Alex Martelli

Ben Finney said:
So, for a class that needs to set attributes in __init__ (but after
that, become immutable), how do I get around this? Should I make a

As I said, you delegate attribute setting to the superclass. E.g:
.... def __setattr__(*a): raise TypeError, 'immutable'
.... def __init__(self, v=23):
.... super(Immut,self).__setattr__('v', v)
.... Traceback (most recent call last):
File "<stdin>", line 1, in ?


Alex
 
A

Alex Martelli

Ben Finney said:
I've probably stumbled across something that people often want to do
for ill-considered reasons; I don't really understand the reaction I'm
getting.

Why is "I want to make objects immutable" seen as "I don't trust my
users"? Are Python's existing immutable types also seen the same way?
If not, why the distinction?

A type implemented in C offers different possibilities than one
implemented in Python -- no deep conceptual reason, just practical ones.

A substantial fraction of the time, people asking "how do I stop the
users of my code from doing X" proceed by screaming "but they could
still do X if they hopped on their left foot in a moonless midnight
while sprinkling bat's blood!" as a response to any suggestion. So,
when you ask "how do I stop the users of my code from doing X" without
qualification, you're quite likely to get such disclaimers. If you
don't want them, learn to ask about stopping your users from
ACCIDENTALLY doing X, and no reasonable respondant will fail to notice
the qualification.

For context, I'm intending for the immutability of class instances to
be a properly documented part of the class protocol. Does that sound
more reasonable?

There was nothing unreasonable in your original request, either; and I
don't think that making it explicit that you intended to document the
restriction would have changed my answer (although an explicit
acknowlegment that you're looking for restrictions against accidental
misuse rather than against determined attackers surely would).


Alex
 
B

bonono

Alex said:
A substantial fraction of the time, people asking "how do I stop the
users of my code from doing X" proceed by screaming "but they could
still do X if they hopped on their left foot in a moonless midnight
while sprinkling bat's blood!" as a response to any suggestion. So,
when you ask "how do I stop the users of my code from doing X" without
qualification, you're quite likely to get such disclaimers. If you
don't want them, learn to ask about stopping your users from
ACCIDENTALLY doing X, and no reasonable respondant will fail to notice
the qualification.
Interestingly, that is what I read from the OP, even without the
"ACCIDENTALLY".
 
M

Mike

Ben Finney said:
Howdy all,

How can a (user-defined) class ensure that its instances are
immutable, like an int or a tuple, without inheriting from those
types?

What caveats should be observed in making immutable instances?

IMHO, this is usually (but not always) a mistake. (If you're programming a
missle guidance system, or it makes your program go faster it's not a
mistake :))
So are PRIVATE, CONST (all types), SEALED, FINAL, etc -- even the best
programmer doesn't foresee what a user will want to do to make best use of
his components, and many a time I've been annoyed (in Java and MS
frameworks) by not being able to access/modify/subclass a member/class that
I know is there because it has to be there (or because I can see it in the
debugger), but it's not accessable because the programmer was overly clever
and had been to OOP school. A fine-grained capability architecture married
to the language and runtime where I can declare my own level cleverness to
override the programmer's would be nice, but I think Python's voluntary
DoThis, _DoThisIfYouReallyHaveTo, and __You'dBetterKnowWhatYou'reDoing__
approach is a better way to go than the undefeatable keyword approach.

m
 
G

Giovanni Bajo

Mike said:
Note that this property of __slots__ is an implementation detail. You
can't rely on it working in the future.

I don't "rely" on it. I just want to catch bugs in my code.
I'm curious as to why you care if people add attributes to your
"immutable" class. Personally, I consider that instances of types
don't let me add attributes to be a wart.

To catch stupid bugs, typos and whatnot. If an instance is immutable, you can't
modify it, period. If you do it, it's a bug. So why not have the bug raises an
exception, rather than go unnoticed?

I don't see your point, either. Why would you want to add attributes to an
object documented to be immutable?
 
G

Giovanni Bajo

Mike said:
It's a wart because user-defined classes *don't* have the same
behavior.

Then *my* solution for this would be to give user-defined classes a way to
behave like builtins, eg. explicitally and fully implement immutability.
Immutability is an important concept in Python programs, and I'm impressed it
does not have explicit support.
 
G

Giovanni Bajo

Mike said:
IMHO, this is usually (but not always) a mistake. (If you're
programming a missle guidance system, or it makes your program go
faster it's not a mistake :))
So are PRIVATE, CONST (all types), SEALED, FINAL, etc -- even the best
programmer doesn't foresee what a user will want to do to make best
use of his components, and many a time I've been annoyed (in Java and
MS frameworks) by not being able to access/modify/subclass a
member/class that I know is there because it has to be there (or
because I can see it in the debugger), but it's not accessable
because the programmer was overly clever and had been to OOP school.

There's a big difference. An immutable object has a totally different semantic,
compared to a mutable object. If you document it to be immutable, and maybe
even provide __eq__ /__hash__, adding attributes from it is surely an user bug.
And surely a bug for which I'd expect an exception to be raised.

Sometimes, I play with some of my objects and I have to go back and check
documentation whether they are immutable or not, to make sure I use the correct
usage pattern. That's fine, this is what docs are for, but couldn't Python give
me some way to enforce this so that, if I or some other dev do the mistake, it
doesn't go unnoticed?
 
M

Mike Meyer

Giovanni Bajo said:
I don't "rely" on it. I just want to catch bugs in my code.

I certainly hope you're not relying on it to catch bugs. You should do
proper testing instead. Not only will that catch pretty much all the
bugs you mention later - thus resolving you of the need to handcuff
clients of your class - it will catch lots of other bugs as well.
To catch stupid bugs, typos and whatnot. If an instance is immutable, you can't
modify it, period. If you do it, it's a bug. So why not have the bug raises an
exception, rather than go unnoticed?

It's only a bug if you didn't mean to do it. If you meant to do it,
you're taking advantage of a powerful feature of Python. If you feel
the need to restrict the use of such features, maybe you should
consider a less powerful language? There are lots of languages around
that prevent accidental creation of attributes - and lots of other
similar usages that are bugs if you don't mean to do them.
I don't see your point, either. Why would you want to add attributes to an
object documented to be immutable?

The documentation is immaterial. The *need* is what's important. I've
had use cases were some object was almost exactly what I wanted,
except I needed another bit of data dragged along. Python lets me do
that for most classes, which is one of the things that I like about it
- it doesn't handcuff me.
Then *my* solution for this would be to give user-defined classes a way to
behave like builtins, eg. explicitally and fully implement immutability.

Well, if you want to propose a change to the language, you need a good
use case to demonstrate the benefits of such a change. Do you have
such a use case? Catching bugs doesn't qualify, otherwise Python would
be radically different from what it is.
Immutability is an important concept in Python programs, and I'm impressed it
does not have explicit support.

I'm not convinced that immutability is that important a concept. Yeah,
you have to know about it, but it seems more like an implementation
detail than a crucial concept. I'm not sure it's more important than
things like interned strings and the sharing of small integers. Most
of the discussion of immutables here seems to be caused by newcomers
wanting to copy an idiom from another language which doesn't have
immutable variables. Their real problem is usually with binding, not
immutability.

<mike
 
A

Alex Martelli

Interestingly, that is what I read from the OP, even without the
"ACCIDENTALLY".

While I followed the maxim "in the face of ambiguity, refuse the
temptation to guess" and didn't put words in his mouth that he had in
fact not said: I explained what he could do, and how that wouldn't work
IF he did NOT mean ``accidentally''. Not sure if this refusal to assume
that the requests are _necessarily_ sensible comes from my several years
of experience fielding question in this and other newsgroups, or my even
more years of experience as a consultant, both freelance and "in-house"
within large organizations...


Alex
 
B

bonono

Alex said:
While I followed the maxim "in the face of ambiguity, refuse the
temptation to guess" and didn't put words in his mouth that he had in
fact not said: I explained what he could do, and how that wouldn't work
IF he did NOT mean ``accidentally''. Not sure if this refusal to assume
that the requests are _necessarily_ sensible comes from my several years
of experience fielding question in this and other newsgroups, or my even
more years of experience as a consultant, both freelance and "in-house"
within large organizations...
I fully understand that, in the "formal" life. But I usually lax that
on occasions like here.
 
B

bonono

Mike said:
I certainly hope you're not relying on it to catch bugs. You should do
proper testing instead. Not only will that catch pretty much all the
bugs you mention later - thus resolving you of the need to handcuff
clients of your class - it will catch lots of other bugs as well.
That is an ideal case and we all know the real world is not ideal or
every program would be bug free. And I don't think anyone would solely
relies on the language feature for spotting bugs. Whether this kind of
guard is useful is another story.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,160
Latest member
CollinStri
Top