# Mutable objects inside tuples - good or bad?

J

I find this programming pattern to be useful... but can it cause problems?

Python 3.3.2+ (default, Feb 28 2014, 00:52:16)
[GCC 4.8.1] on linux
a = [1,2,3]
b = [4,5,6]
c = (a,b)
c ([1, 2, 3], [4, 5, 6])
c[0][0] = 0
c
([0, 2, 3], [4, 5, 6])

G

#### Gary Herron

I find this programming pattern to be useful... but can it cause problems?
No.

What kind of problems are you considering? It won't break Python. It's
perfectly legal code.

The tuple c is still immutable, consisting of two specific objects, and
(as always) without regard to the specifics or contents of those two
objects.

Gary Herron

Python 3.3.2+ (default, Feb 28 2014, 00:52:16)
[GCC 4.8.1] on linux
a = [1,2,3]
b = [4,5,6]
c = (a,b)
c ([1, 2, 3], [4, 5, 6])
c[0][0] = 0
c
([0, 2, 3], [4, 5, 6])

D

#### Devin Jeanpierre

No.

What kind of problems are you considering? It won't break Python. It's
perfectly legal code.

Agreed. Putting mutable objects inside tuples is common and totally OK.
The tuple c is still immutable, consisting of two specific objects, and (as
always) without regard to the specifics or contents of those two objects.

You can choose to define mutability that way, but in many contexts
you'll find that definition not very useful.

c is such that you could have another variable d, where the following
interpreter session fragment is easily possible:
False

-- Devin
Python 3.3.2+ (default, Feb 28 2014, 00:52:16)
[GCC 4.8.1] on linux
a = [1,2,3]
b = [4,5,6]
c = (a,b)
c

([1, 2, 3], [4, 5, 6])
c[0][0] = 0
c

([0, 2, 3], [4, 5, 6])

C

#### Chris Angelico

Agreed. Putting mutable objects inside tuples is common and totally OK.

There are many programming habits that can cause problems, even though
they won't break Python and are legal code.
You can choose to define mutability that way, but in many contexts
you'll find that definition not very useful.

c is such that you could have another variable d, where the following
interpreter session fragment is easily possible:

False

What you're looking at here is hashability, not mutability. Compare:
a = (1,2,3)
hash(a) 2528502973977326415
b = ([1],[2],[3])
hash(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Both tuples are immutable, but the former is hashable because all its
members are hashable, while the latter is not. You can't trust that
equality with b is constant:
c = ([1],[2],[3])
b == c True
b[2][0]=4
b == c
False

Going back to the original question, though: I do not believe that
putting mutable objects inside tuples is a problem. I've done it
frequently myself, and it's never caused confusion. So go right ahead,
do it if it makes sense!

ChrisA

D

#### Devin Jeanpierre

There are many programming habits that can cause problems, even though
they won't break Python and are legal code.

Yes, but this isn't one of them.
What you're looking at here is hashability, not mutability. Compare:

No, that is not what I am looking at. There are hashable objects with
the property I described, and unhashable objects without it.

My point in that example was that sometimes it is more useful to talk
about entire objects and their behavior as a whole. Calling the object
"immutable" when it has mutable state is misleading in this context.

-- Devin

R

#### Rustom Mody

You can choose to define mutability that way, but in many contexts
you'll find that definition not very useful.

c is such that you could have another variable d, where the following
interpreter session fragment is easily possible:

False

Its called referential transparency (or rather the lack of it)
And that is why it (Johns question) is not a good idea.

In general worshipping overzealously at the altar of RT produces functional
programming. To the non-zealots this has the characteristic of
"Throw out baby with bathwater"

On the other hand imperative programming is a source of more problems than
people realize:
http://blog.languager.org/2012/11/imperative-programming-lessons-not.html

P

#### Paul KÃ¶lle

Am 06.04.2014 09:25, schrieb Gary Herron:
No.

What kind of problems are you considering? It won't break Python. It's
perfectly legal code.

The tuple c is still immutable, consisting of two specific objects, and
(as always) without regard to the specifics or contents of those two
objects.
It seems a tuple's immutability is debatable, or is this another
instance of the small-integer-reuse-implementation-detail-artifact?

Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
[GCC 4.4.5] on linux2
a = ([1,2],[3,4])
b = a
a is b True
a == b True
c = (1,2,3)
d = (1,2,3)
c is d False
c == d
True

cheers
Paul

C

#### Chris Angelico

It seems a tuple's immutability is debatable, or is this another instanceof
the small-integer-reuse-implementation-detail-artifact?

Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
[GCC 4.4.5] on linux2

a = ([1,2],[3,4])
b = a
a is b True
a == b True
c = (1,2,3)
d = (1,2,3)
c is d False
c == d
True

That's nothing to do with mutability or reuse. With a and b, you
assigned one to be the same as the other, so they are by definition
identical (and equal; tuples assume that identity implies equality,
even though that may not be true of their elements). With c and d, you
assigned separate tuples, so they're allowed to be separate objects.
I'm not sure if they're allowed to be constant-folded, but CPython
apparently isn't doing so. They are still equal, though; they contain
equal elements, ergo they are equal. (Note that (1, 2, 3) and (1.0,
2.0, 3.0) are equal, but they obviously can't be identical any more
than "1 is 1.0" can ever be True.)

ChrisA

P

#### Paul KÃ¶lle

Am 07.04.2014 17:44, schrieb Chris Angelico:
It seems a tuple's immutability is debatable, or is this another instance of
the small-integer-reuse-implementation-detail-artifact?

Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
[GCC 4.4.5] on linux2

a = ([1,2],[3,4])
b = a
a is b True
a == b True
c = (1,2,3)
d = (1,2,3)
c is d False
c == d
True

That's nothing to do with mutability or reuse. With a and b, you
assigned one to be the same as the other, so they are by definition
identical (and equal; tuples assume that identity implies equality,
even though that may not be true of their elements). With c and d, you
assigned separate tuples, so they're allowed to be separate objects.
I'm not sure if they're allowed to be constant-folded, but CPython
apparently isn't doing so. They are still equal, though; they contain
equal elements, ergo they are equal. (Note that (1, 2, 3) and (1.0,
2.0, 3.0) are equal, but they obviously can't be identical any more
than "1 is 1.0" can ever be True.)

ChrisA
Thanks Chris, stupid error indeed

cheers
Paul

C

#### Chris Angelico

Thanks Chris, stupid error indeed

Error, at least This is why we have a mailing list: errors,
inaccuracies, and typos, regardless of who makes them or when, are
pretty much guaranteed to be caught.

ChrisA

T

#### Terry Reedy

An implementation would be allowed to make that True, as it does for
small ints and short strings that could be identifiers.
True
True

However, duplicate tuples are much rarer than duplicate identifier strings.

S

#### Steven D'Aprano

An implementation would be allowed to make that True, as it does for
small ints and short strings that could be identifiers.

And indeed, that happens in at least one circumstance in Python 3.3:

py> a, b = [(1, 2, 3) for _ in range(2)]
py> a is b
True

But:

py> x = 3
py> a, b = [(1, 2, x) for _ in range(2)]
py> a is b
False

As Terry knows, but for the benefit of others who may not, the re-use of
objects leading to object identity ("a is b") is an implementation detail
which *cannot be relied on*. It can change without notice, and is not a
promise of the language.

True
True

In this case, it is a promise of the language that a will equal b: a and
b must be bound to strings with the same value. But an implementation
detail whether Python creates two strings, both with value "one", or just
a single string, and uses it for both a and b.