Hacker News new | past | comments | ask | show | jobs | submit login

TL;DR is that multiplying a list performs a shallow copy of the list's elements. Great article. The only thing it's missing is a snippet that shows what you should do instead:

  [[] for _ in range(N)]
Since generator-like expressions evaluates the inner expression (i.e. the []) on each iteration, the elements of the outer list are guaranteed to be unique references.



I only use the [x] * n form of array initialization for primitive types (int,float,bool,string). So

  #good
  a = [True] * 8 # bool is primitive
  b = [[True] * 8 for i in range(8)] # range creates new lists each time

  #bad
  a = [{}] * 8 # dict is not a primitive
  b = [[True] * 8] * 8 # list is not a primitive


This is pretty subtle. I wish more languages explicitly distinguished references and values like C++ and Rust.


In Python, everything is a reference, so you don't need special syntax to mark them.

There just happen to be some types where you cannot change the referenced value in any way. These behave like value types in other languages.

(If you believe that they should be marked explicitly, do you also believe the same thing about Rust? I.e. should the Rust compiler throw an error if you never actually change a reference type, as you "should" be using a value type in that case?)


Everything falls in place when you accept that in python all names (including list indexes) are just pointers to an object, and objects aren't copied unless you explicitly copy them. Everything else follows from that.

If you had done [1] * 4 you'd ended up with a list of four references to the same object (1) as well. It's just that 1 isn't mutable, so nobody cares.


F# does this nicely with the mutable keyword and the <- assignment operator.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: