Immutable and Mutable Types

Discussion in 'Python' started by Bernard Lim, Mar 17, 2008.

  1. Bernard Lim

    Bernard Lim Guest

    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:

    >>> a = 1
    >>> b = 1
    >>> a is b

    True
    >>> id(a)

    10901000
    >>> id(b)

    10901000

    Is this correct?

    Regards
    Bernard
    Bernard Lim, Mar 17, 2008
    #1
    1. Advertising

  2. Bernard Lim

    Dan Bishop Guest

    Bernard Lim wrote:
    > 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:
    >
    > >>> a = 1
    > >>> b = 1
    > >>> a is b

    > True
    > >>> id(a)

    > 10901000
    > >>> id(b)

    > 10901000
    >
    > Is this correct?


    Yes, that's correct. However,

    >>> a = 100
    >>> b = 100
    >>> a is b

    False
    >>> id(a)

    135644988
    >>> id(b)

    135645000
    Dan Bishop, Mar 17, 2008
    #2
    1. Advertising

  3. Bernard Lim

    Ben Finney Guest

    Bernard Lim <> writes:

    > <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.

    --
    \ “The power of accurate observation is frequently called |
    `\ cynicism by those who don't have it.†—George Bernard Shaw |
    _o__) |
    Ben Finney
    Ben Finney, Mar 17, 2008
    #3
  4. Bernard Lim

    Guest

    > >>> a = 1
    > >>> b = 1
    > >>> a is b

    > True
    > >>> id(a)

    > 10901000
    > >>> id(b)

    > 10901000


    Isn't this because integers up to a certain range are held in a single
    memory location, thus why they are the same?
    , Mar 17, 2008
    #4
  5. Bernard Lim

    Duncan Booth Guest

    wrote:

    >> >>> a = 1
    >> >>> b = 1
    >> >>> a is b

    >> True
    >> >>> id(a)

    >> 10901000
    >> >>> id(b)

    >> 10901000

    >
    > 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:

    >>> a = 1
    >>> b = 5-4
    >>> a is b

    False
    >>> id(a)

    43L
    >>> id(b)

    44L
    >>>
    Duncan Booth, Mar 17, 2008
    #5
  6. wrote:

    >> >>> a = 1
    >> >>> b = 1
    >> >>> a is b

    >> True
    >> >>> id(a)

    >> 10901000
    >> >>> id(b)

    >> 10901000

    >
    > 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
    Diez B. Roggisch, Mar 17, 2008
    #6
  7. Bernard Lim

    Duncan Booth Guest

    "Diez B. Roggisch" <> wrote:

    > 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?

    >>> a = 1
    >>> b = 5-4
    >>> if a is b: print 'yes!'

    ....
    yes!
    >>> b = ****
    >>> if a is b: print 'yes!'

    ....
    >>> b

    1
    >>> type(b)

    <type 'int'>
    Duncan Booth, Mar 17, 2008
    #7
  8. On Mon, 17 Mar 2008 10:40:43 +0000, Duncan Booth wrote:

    > 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?


    --
    Steven
    Steven D'Aprano, Mar 17, 2008
    #8
  9. Bernard Lim

    Stargaming Guest

    On Mon, 17 Mar 2008 10:40:43 +0000, Duncan Booth wrote:
    > 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?
    >
    >>>> a = 1
    >>>> b = ****
    >>>> if a is b: print 'yes!'

    > ...
    >>>> b

    > 1
    >>>> type(b)

    > <type 'int'>


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

    >>> b = type('type', (type,), {'__repr__': lambda self:

    ... "<type 'int'>"})('int', (object,), {'__repr__':
    ... lambda self:"1"})()
    >>> b

    1
    >>> type(b)

    <type 'int'>
    >>> 1 is b

    False

    What's my prize?
    Stargaming, Mar 17, 2008
    #9
  10. Bernard Lim

    Duncan Booth Guest

    Steven D'Aprano <> wrote:

    > On Mon, 17 Mar 2008 10:40:43 +0000, Duncan Booth wrote:
    >
    >> 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.


    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.
    Duncan Booth, Mar 17, 2008
    #10
  11. Bernard Lim

    Duncan Booth Guest

    Stargaming <> wrote:

    > On Mon, 17 Mar 2008 10:40:43 +0000, Duncan Booth wrote:
    >> 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?
    >>
    >>>>> a = 1
    >>>>> b = ****
    >>>>> if a is b: print 'yes!'

    >> ...
    >>>>> b

    >> 1
    >>>>> type(b)

    >> <type 'int'>

    >
    > I know it! Using CPython 2.5.2a0, 2.6a1+ and 3.0a3+::
    >
    > >>> b = type('type', (type,), {'__repr__': lambda self:

    > ... "<type 'int'>"})('int', (object,), {'__repr__':
    > ... lambda self:"1"})()
    > >>> b

    > 1
    > >>> type(b)

    > <type 'int'>
    > >>> 1 is b

    > 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.
    Duncan Booth, Mar 17, 2008
    #11
  12. Bernard Lim

    Stargaming Guest

    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'>
    Stargaming, Mar 17, 2008
    #12
  13. Bernard Lim

    Duncan Booth Guest

    Stargaming <> wrote:

    > 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'>


    "the whole point of setting the puzzle was that I expected it to be ripped
    to shreds", thanks for supporting my expectations.
    Duncan Booth, Mar 17, 2008
    #13
  14. In article <Xns9A64A2776A5Aduncanbooth@127.0.0.1>,
    Duncan Booth <> wrote:
    > 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-
    Matthew Woodcraft, Mar 17, 2008
    #14
  15. Bernard Lim

    Duncan Booth Guest

    Matthew Woodcraft <> wrote:

    > In article <Xns9A64A2776A5Aduncanbooth@127.0.0.1>,
    > Duncan Booth <> wrote:
    >> 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).
    >

    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.
    Duncan Booth, Mar 17, 2008
    #15
  16. Bernard Lim

    Duncan Booth Guest

    Stargaming <> wrote:

    > 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.
    Duncan Booth, Mar 18, 2008
    #16
  17. Bernard Lim

    Terry Reedy Guest

    "Duncan Booth" <> wrote in message
    news:Xns9A65624FD1932duncanbooth@127.0.0.1...
    | Stargaming <> wrote:
    |
    | > 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

    >>> a,b=1,1//1
    >>> a is b

    False

    IDLE 3.0a3
    >>> a,b=1,1//1
    >>> a is b

    True

    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:
    >>> 1*1 is 1

    True
    >>> 1+1 is 2

    True
    >>> 1-1 is 0

    True
    >>> 1**1 is 1

    True

    so the omission with // is plausibly a bug.

    Terry Jan Reedy
    Terry Reedy, Mar 19, 2008
    #17
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. hiwa
    Replies:
    4
    Views:
    479
    Oliver Wong
    Mar 8, 2006
  2. Replies:
    2
    Views:
    367
    John Roth
    Apr 15, 2004
  3. Anne Wangnick
    Replies:
    1
    Views:
    306
    Peter Hansen
    Aug 31, 2004
  4. Replies:
    5
    Views:
    397
  5. Deb Wyatt

    immutable vs mutable

    Deb Wyatt, Jun 3, 2014, in forum: Python
    Replies:
    13
    Views:
    53
    Mark H Harris
    Jun 4, 2014
Loading...

Share This Page