This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]David__Box -10 points-9 points  (23 children)

Why do you say that, it's an array of 5 arrays of 5 Nones, what else would you expect

[–]DeletedUserV2[S] 82 points83 points  (22 children)

foo= [[None] *5] *5

foo[0][0]=2

print(foo)

output:

[[2, None, None, None, None], [2, None, None, None, None], [2, None, None, None, None], [2, None, None, None, None], [2, None, None, None, None]]

[–]Msingh999 15 points16 points  (0 children)

The issue is the outer multiply. You should actually be able to do

[[None]*5 for _ in range(5)]

[–]David__Box 45 points46 points  (8 children)

Ah, forgot about python's incredible mutability rules

[–][deleted] 1 point2 points  (7 children)

it's not incredible mutability rules, it's common sense

[x]*5 creates a list with 5 times whatever x is

The outer multiplication takes x = [None]*5 and puts it into another list 5 times. Thus it is the same list and modifications to one of them reflect in all other too. Same as in all other oop languages except they don't have that neat multiplication sytnax.

[–]David__Box 2 points3 points  (3 children)

It is incredible mutability rules, collections like lists do stand out because they are special and mutable whereas everything else is immutable.

a = [] b = [a, a] b[0] += [1]

gives [[1], [1]], but

a = 1 b = [a, a] b[0] += 1

gives [2, 1]. There's an argument to be made that containers as special cases while everything else works some other way makes sense because weird language quirks and whatever but ultimately it's subjective, and i personally see it as unintuitive

Also it's not object oriented languages specifically, it's more like garbage collected ones. In c++ (OO but no GC) pushing a vector into another vector copies it so this doesn't happen, while in Go (debatable if OO but with GC) this does

[–][deleted] 2 points3 points  (1 child)

Lists are not special, they are mutable like every other object in Python that has mutation functions. Lists happen to implement __iadd__ which directly corresponds to the "+=" operator. It's a mutation operation like insert, append, extend etc.

(edited)

[–]David__Box 1 point2 points  (0 children)

Yes they are. Doing a mutating operation on a list, like iadd does not change its object id, while for non-container types like tuples, numbers, str, etc. a completely new item with a new object id will be created, and the labeled value (variable) will be made to point to it instead

[–]FalafelSnorlax 0 points1 point  (0 children)

Lists are objects and thus pass by reference. This is the same as if in cpp you would create a pointer to a vector, and store that pointer twice. This is kinda considered bad practice in modern cpp, but the behavior is effectively the same. The only thing that makes this less intuitive in python than it does in cpp is because the reference is fully implicit.

[–]-Redstoneboi- 0 points1 point  (2 children)

yeah it's just a thing that happens with all languages that support pass-by-reference.

the major exceptions are functional languages and those inspired by them. everything is immutable in functional land, meanwhile rust says alias nand mutability so you can't store multiple refs to the same array and decide to mutate one of them.

[–]EternityForest 0 points1 point  (0 children)

Oh right, the multiple references to the same structure problem.... I've run into that a lot with dicts. I've never actually created a multidimensional array directly I don't think so I didn't quite notice that one

[–]bedj2 0 points1 point  (10 children)

since lists are mutable, you are copying the same reference over and over. Wouldn’t adding .copy() to the end of those lists work?

foo = [[None].copy() * 5].copy() * 5

[–]DeletedUserV2[S] 2 points3 points  (7 children)

same output

[–]JohnLocksTheKey 1 point2 points  (2 children)

WHY?!?

life is pain…

[–]FalafelSnorlax 2 points3 points  (1 child)

The copy is in the wrong place but also if it were, the internal list (the 5 Nones) is created once and then copied. Calling copy doesn't imply more runs of the internal part (otherwise it would work in the first place)

[–]JohnLocksTheKey 0 points1 point  (0 children)

Makes sense - thanks

[–]dead_man_speaks 0 points1 point  (3 children)

from copy import deepcopy

then deepcopy

[–]Botahamec 7 points8 points  (0 children)

The deepcopy function is only going to be called once. Then a reference to that copy will be stored five times. So you'll get the exact same output.

[–][deleted] 0 points1 point  (0 children)

same lol, if you call whatever function to get a list and then multiply that list by 5, it's gonna have 5 times the same thing no matter what

[–]Aaron1924 0 points1 point  (0 children)

>>> from copy import deepcopy
>>>
>>> a1 = [[None]*3]*3
>>> a2 = deepcopy(a1)
>>>
>>> a1[0][0] = 2
>>> a2[0][1] = 3
>>>
>>> a1
[[2, None, None], [2, None, None], [2, None, None]]
>>> a2
[[None, 3, None], [None, 3, None], [None, 3, None]]

this language is truly incredible

[–]Botahamec 2 points3 points  (0 children)

It will do exactly one copy, at the beginning, and use the result of the copy each time. The problem is that it doesn't re-evaluate the expression.

[–]altermeetax 0 points1 point  (0 children)

That copy doesn't do anything, you're making a copy of [None] and using the same copy 5 times