Semantics of the empty list

D

Dave Opstad

I fully understand why this happens:

----------------------------
a = [[], [], [], [], []]
b = [[]] * 5
a [[], [], [], [], []]
b [[], [], [], [], []]
a == b True
id(a[0]) == id(a[1]) False
id(b[0]) == id(b[1]) True
a[1].append(42)
a [[], [42], [], [], []]
b[1].append(42)
b
[[42], [42], [42], [42], [42]]
----------------------------

What is curious to me is the implication that there are multiple,
distinct empty list objects (whose ids are not equal). Is this the case
for all mutable objects?

I wonder if it would be useful to have some symbol whose semantics are
"distinct mutable." For the sake of discussion, let's say the symbol \\
means the same as [] (i.e. it's the empty list), but with this "distinct
mutable" semantic. Then I could safely do something like:
b = [\\] * 5
b [[], [], [], [], []]
id(b[0]) == id(b[1])
False

to safely initialize b as a list of distinct empty lists. As it is now,
I have to do something like this:

in order to get the semantic I want. It's not any great hardship, of
course, but I have been bitten by the [[]] * 5 case and its cousins on
several occasions, and it might be nice if there were a separate
semantic as described above to make it more clear what's going on.

Just some random thoughts...

Dave
 
L

Larry Bates

Dave,

I agree that it is HIGHLY unlikely that if a
programmer types:

b=[[]]*5

what they wanted is to create a list with 5 instances
of the same empty list, but I don't have a suggestion
for an "alternative" grammar. It's almost like this
"shortcut" shouldn't work at all because it will almost
always create something you didn't want. Then you would
HAVE to use loop/append or list comprehension method
that work as expected.

Larry Bates
Syscon, Inc.

Dave Opstad said:
I fully understand why this happens:

----------------------------
a = [[], [], [], [], []]
b = [[]] * 5
a [[], [], [], [], []]
b [[], [], [], [], []]
a == b True
id(a[0]) == id(a[1]) False
id(b[0]) == id(b[1]) True
a[1].append(42)
a [[], [42], [], [], []]
b[1].append(42)
b
[[42], [42], [42], [42], [42]]
----------------------------

What is curious to me is the implication that there are multiple,
distinct empty list objects (whose ids are not equal). Is this the case
for all mutable objects?

I wonder if it would be useful to have some symbol whose semantics are
"distinct mutable." For the sake of discussion, let's say the symbol \\
means the same as [] (i.e. it's the empty list), but with this "distinct
mutable" semantic. Then I could safely do something like:
b = [\\] * 5
b [[], [], [], [], []]
id(b[0]) == id(b[1])
False

to safely initialize b as a list of distinct empty lists. As it is now,
I have to do something like this:
b = [[] for x in range(5)]

in order to get the semantic I want. It's not any great hardship, of
course, but I have been bitten by the [[]] * 5 case and its cousins on
several occasions, and it might be nice if there were a separate
semantic as described above to make it more clear what's going on.

Just some random thoughts...

Dave
 
T

Tim Roberts

Dave Opstad said:
What is curious to me is the implication that there are multiple,
distinct empty list objects (whose ids are not equal). Is this the case
for all mutable objects?

Any list, string, tuple, or dictionary can be empty. The fact that a list
is "empty" does not automatically turn it into "THE empty list object".

Your first example creates 5 new objects, each of which becomes an empty
list, and stores a reference to each in the outer list. Your second
example creates 1 new object, and stores 5 references to it in the outer
list.
 
J

Jacek Generowicz

Dave Opstad said:
I fully understand why this happens:

----------------------------
a = [[], [], [], [], []]
b = [[]] * 5
a [[], [], [], [], []]
b [[], [], [], [], []]
a == b True
id(a[0]) == id(a[1]) False
id(b[0]) == id(b[1]) True
a[1].append(42)
a [[], [42], [], [], []]
b[1].append(42)
b
[[42], [42], [42], [42], [42]]

Where is this implication ?
I wonder if it would be useful to have some symbol whose semantics are
"distinct mutable."

Distinct from _what_ ?

You'd have to include the memory address of each object in its printed
representation ... which would make it unreadable (unless you want to
make it really evil).

For the sake of discussion, let's say the symbol \\
means the same as [] (i.e. it's the empty list), but with this "distinct
mutable" semantic. Then I could safely do something like:
[[], [], [], [], []]

How do I know, from this printed representation, whether the ids of
the elements are the same ?

Besides, you are now, implicitly, overriding the behaviour of
list.__mul__.
id(b[0]) == id(b[1])
False

to safely initialize b as a list of distinct empty lists. As it is now,
I have to do something like this:
b = [[] for x in range(5)]

in order to get the semantic I want. It's not any great hardship, of
course, but I have been bitten by the [[]] * 5 case and its cousins on
several occasions, and it might be nice if there were a separate
semantic as described above to make it more clear what's going on.

As you point out, there is a (pretty simple) way of expressing exactly
what you want. Inventing some line noise to cover one special case,
would encourage inventing some more line noise to cover the next
special case, which would encourage inventing some more line noise
.... and pretty soon we'd have Perl.

For example, how would extend your idea to disambiguate between the
possible meanings of "[[foo()]] * 5" ?

Now, how about "[[foo()]*5] * 5" ?
 

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

No members online now.

Forum statistics

Threads
474,202
Messages
2,571,057
Members
47,662
Latest member
salsusa

Latest Threads

Top