Immutable and Mutable Types

B

Bernard Lim

Hi,

I'm reading the Python Reference Manual in order to gain a better understanding
of Python under the hood.

On the last paragraph of 3.1, there is a statement on immutable and mutable
types as such:

<paraphrase>
Depending on implementation, for immutable types, operations that compute
new values may or may not actually return a reference to any existing object
with the same type and value, while for mutable objects this is (strictly)?
not allowed.
</paraphrase>

Using the example given in 3.1, how do I verify that this is true pertaining
to immutable types? Below is my understanding on verifying the above statement:
10901000

Is this correct?

Regards
Bernard
 
D

Dan Bishop

Bernard said:
Hi,

I'm reading the Python Reference Manual in order to gain a better understanding
of Python under the hood.

On the last paragraph of 3.1, there is a statement on immutable and mutable
types as such:

<paraphrase>
Depending on implementation, for immutable types, operations that compute
new values may or may not actually return a reference to any existing object
with the same type and value, while for mutable objects this is (strictly)?
not allowed.
</paraphrase>

Using the example given in 3.1, how do I verify that this is true pertaining
to immutable types? Below is my understanding on verifying the above statement:

10901000

Is this correct?

Yes, that's correct. However,
135645000
 
B

Ben Finney

Bernard Lim said:
<paraphrase>
Depending on implementation, for immutable types, operations that
compute new values may or may not actually return a reference to any
existing object with the same type and value, while for mutable
objects this is (strictly)? not allowed.
</paraphrase>

Using the example given in 3.1, how do I verify that this is true
pertaining to immutable types?

You don't. By the language definition, it's entirely up to the
implementation whether *and when* to do this.

So, even if you find a particular session that does this, there's no
telling whether it'll stop happening at some future session using
*exactly the same inputs* -- and, if it did change, that would also be
entirely within the definition of the language.

If something in a language specification says "this set of conditions
leads to undefined behaviour", or "this aspect is implementation
defined", then *absolutely any behaviour* that fits the rest of the
definition is allowed, even if that results in non-determinism from
the programmer's perspective.

In short: Don't ever rely on such behaviour labelled with these "may
or may not" phrases, even if you run some tests and appear to get
predictable results.
 
D

Duncan Booth

Isn't this because integers up to a certain range are held in a single
memory location, thus why they are the same?
Yes, in *some* implementations of Python this is exactly what happens. The
exact range, or indeed whether it happens at all, and (if it does happen)
whether the range depends on the phase of the moon are all undefined.

This, for example, is what I just got for a similar sequence:
 
D

Diez B. Roggisch

Isn't this because integers up to a certain range are held in a single
memory location, thus why they are the same?

As the OP said:

<paraphrase>
Depending on implementation, for immutable types, operations that compute
new values may or may not actually return a reference to any existing object
with the same type and value, while for mutable objects this is (strictly)?
not allowed.
</paraphrase>

Which is exactly what happens - the actual implementation chose to cache
some values based on heuristics or common sense - but no guarantees are
made in either way.

Diez
 
D

Duncan Booth

Diez B. Roggisch said:
Which is exactly what happens - the actual implementation chose to cache
some values based on heuristics or common sense - but no guarantees are
made in either way.

Here's a puzzle for those who think they know Python:

Given that I masked out part of the input, which version(s) of Python might
give the following output, and what might I have replaced by asterisks?
....
yes!
<type 'int'>
 
S

Steven D'Aprano

Here's a puzzle for those who think they know Python:

Given that I masked out part of the input, which version(s) of Python
might give the following output, and what might I have replaced by
asterisks?

There's too many variables -- at least five Python implementations that I
know of (CPython, Jython, PyPy, IronPython, and the Lisp-based
implementation that I can never remember the name of), and given that
this is an implementation-dependent feature it could have changed at any
time, in any version number (say, between minor releases). And there's
literally an infinite number of ways to get b equal to an int with the
value 1.

So I think unless somebody happens to have stumbled across this
behaviour, it's not predictable.

But having said that, I'm going to take a stab in the dark:

The line "b = ****" should be "b = int('1')"

and the version is CPython 1.4.

Am I close?
 
S

Stargaming

Here's a puzzle for those who think they know Python:

Given that I masked out part of the input, which version(s) of Python
might give the following output, and what might I have replaced by
asterisks?

<type 'int'>

I know it! Using CPython 2.5.2a0, 2.6a1+ and 3.0a3+::
False

What's my prize?
 
D

Duncan Booth

Steven D'Aprano said:
There's too many variables -- at least five Python implementations
that I know of (CPython, Jython, PyPy, IronPython, and the Lisp-based
implementation that I can never remember the name of), and given that
this is an implementation-dependent feature it could have changed at
any time, in any version number (say, between minor releases). And
there's literally an infinite number of ways to get b equal to an int
with the value 1.

True, there are a lot of variables, but perhaps not as many as you think.
For example, you can't get that output from IronPython or PyPy (or at least
not the versions I have kicking around) as they won't print 'yes!' for the
first test. You are correct though it is possible with both CPython and
Jython.
So I think unless somebody happens to have stumbled across this
behaviour, it's not predictable.

But having said that, I'm going to take a stab in the dark:

The line "b = ****" should be "b = int('1')"

and the version is CPython 1.4.

Am I close?
I don't have a copy of 1.4 to check so I'll believe you, but you can
certainly get the output I asked for with much more recent versions.

For the answer I actually want each asterisk substitutes for exactly one
character.
 
D

Duncan Booth

Stargaming said:
I know it! Using CPython 2.5.2a0, 2.6a1+ and 3.0a3+::

False

What's my prize?

Ok, that one is impressive, I should have thought of putting some more
checks (like asserting type(a) is type(b)), but then the whole point of
setting the puzzle was that I expected it to be ripped to shreds. You
can queue up behind Steven to choose any of the exciting prizes on my
non-existent prize table.

Meanwhile, I'll fall back on the same quibbling getout I used with
Steven: too long.
 
S

Stargaming

For the answer I actually want each asterisk substitutes for exactly one
character.

Played around a bit and found that one:

Python 3.0a3+ (py3k:61352, Mar 12 2008, 12:58:20)
[GCC 4.2.3 20080114 (prerelease) (Debian 4.2.2-7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.<type 'int'>
 
D

Duncan Booth

Stargaming said:
For the answer I actually want each asterisk substitutes for exactly one
character.

Played around a bit and found that one:

Python 3.0a3+ (py3k:61352, Mar 12 2008, 12:58:20)
[GCC 4.2.3 20080114 (prerelease) (Debian 4.2.2-7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.<type 'int'>

"the whole point of setting the puzzle was that I expected it to be ripped
to shreds", thanks for supporting my expectations.
 
M

Matthew Woodcraft

I don't have a copy of 1.4 to check so I'll believe you, but you can
certainly get the output I asked for with much more recent versions.
For the answer I actually want each asterisk substitutes for exactly one
character.

Then I'll guess you're looking for something like '0==0', with Python
2.2 or so (so that you'd get the PyTrue object).

-M-
 
D

Duncan Booth

Matthew Woodcraft said:
Then I'll guess you're looking for something like '0==0', with Python
2.2 or so (so that you'd get the PyTrue object).
Yes, that was the answer I was looking for: CPython 1.5.x (or maybe
earlier?) to 2.2.x without a bool type but separate values for true and
false (as used by PyWin). Also it seems that Jython 2.2.1 (and probably
other versions) behaves the same way.

I'm intrigued though by Stargaming's answer with 1//1, I must look into
that one.
 
D

Duncan Booth

Stargaming said:
For the answer I actually want each asterisk substitutes for exactly one
character.

Played around a bit and found that one:

Python 3.0a3+ (py3k:61352, Mar 12 2008, 12:58:20)
[GCC 4.2.3 20080114 (prerelease) (Debian 4.2.2-7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.<type 'int'>

I've had a look to see why this happens: long division (and in Python 3 all
integers are longs) allocates a new long to hold the result of the division
so it will never use one of the preallocated 'small int' values.

That makes sense so far as it goes, but I'm slightly suprised if it isn't
worth an extra check somewhere for numerator fitting in a machine int and
shortcutting the long division.
 
T

Terry Reedy

|
| > On Mon, 17 Mar 2008 16:03:19 +0000, Duncan Booth wrote:
| >
| >> For the answer I actually want each asterisk substitutes for exactly
one
| >> character.
| >
| > Played around a bit and found that one:
| >
| > Python 3.0a3+ (py3k:61352, Mar 12 2008, 12:58:20)
| > [GCC 4.2.3 20080114 (prerelease) (Debian 4.2.2-7)] on linux2
| > Type "help", "copyright", "credits" or "license" for more information.
| >>>> a = 1
| >>>> b = 1//1
| >>>> if a is b: print('yes!')
| > ...
| >>>> b
| > 1
| >>>> type(b)
| ><type 'int'>
|
| I've had a look to see why this happens: long division (and in Python 3
all
| integers are longs) allocates a new long to hold the result of the
division
| so it will never use one of the preallocated 'small int' values.
|
| That makes sense so far as it goes, but I'm slightly suprised if it isn't
| worth an extra check somewhere for numerator fitting in a machine int and
| shortcutting the long division.

I submitted the following to the issue tracker
http://bugs.python.org/issue2417

Python 3.0a3 (r30a3:61161, Mar 1 2008, 22:51:17) [MSC v.1500 32 bit
(Intel)] on win32
False

IDLE 3.0a3True

ditto for 2.5.2 interpreter

On c.l.p, Duncan Booth wrote
I've had a look to see why this happens: long division (and in Python 3 all
integers are longs) allocates a new long to hold the result of the division
so it will never use one of the preallocated 'small int' values.

That maybe explains the change from 2.5 but not the difference from IDLE.
More important, the small int checks are present with the other operations:True

so the omission with // is plausibly a bug.

Terry Jan Reedy
 

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,767
Messages
2,569,573
Members
45,046
Latest member
Gavizuho

Latest Threads

Top