Why does initializing the array arr
work when it is done as a list comprehension (I think that is what the following example is --not sure), but not when each array location is initialized individually?
For example, this works: (a)
arr=[]
arr=[0 for i in range(5)]
but (b),
arr=[]
arr[0]=0
arr[1]=0
etc, doesn't.
Isn't the arr=[0 for i in range(5)]
instruction essentially doing what is done in (b) above in one fell swoop?
I realize that array sizes need to be predefined (or allocated). So, I can understand something like
arr= [0]*5
or using numpy,
arr = np.empty(10, dtype=object)
work. However, I don't see how (a) preallocates the array dimension "ahead of time". How does python interpret (a) vs. (b) above?
CodePudding user response:
Firstly, there is no point in declaring a variable if you rebind it later anyway:
arr = [] # <-- this line is entirely pointless
arr = [0 for i in range(5)]
Secondly, the two expressions
[0 for i in range(5)]
[0] * 5
create a new list
object, whereas
arr[0] = 0
mutates an existing one, namely it wants to reassign the first element of arr
. Since this doesn't exist, you will see an error. You could do instead:
arr = []
arr.append(0)
arr.append(0)
to fill an initially empty list
incrementally.
Note that a Python list is not an Array
in, let's say, the Java sense that it has a predefined size. It is more like an ArrayList
.
CodePudding user response:
It doesn't pre-allocate. It's basically just appending in a loop, just in nice form (syntactic sugar).
Why it doesn't pre-allocate? Because to pre-allocate, we would need to know the length of the iterable, which may be a generator and it would use it up. And also, comprehension can have an if clause, limiting what eventually gets into the list. (See also generator comprehensions, which create generators - no pre-allocation because it's lazily evaluated)
Let's take a look at documentation:
https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it. For example, this listcomp combines the elements of two lists if they are not equal:
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
and it’s equivalent to:
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
See? Equivalent to append, not to pre-allocated (n*[0]) list.
CodePudding user response:
I don't see how (a) preallocates the array dimension "ahead of time".
It doesn't. This:
arr=[]
arr=[0 for i in range(5)]
creates an empty array (I think it's more accurately called a list but I'm not a strong Python person) and stores it in arr
, then creates an entirely new unrelated array and puts the new one in arr
, throwing the old array away. It doesn't initialize the array you created with arr=[]
. You can remove the first line (arr=[]
) entirely; it doesn't do anything useful.
You can see that they're different arrays like this:
# Create a blank array, store it in `a`
a = []
# Store that same array in `b`
b = a
# Show that they're the same array
print(a == b);
# Create a new array and put it in `a`
a = [0 for i in range(5)]
# Show that they aren't the same array
print(a == b);
The output is
True False
So just use arr=[0 for i in range(5)]
or, if you want to do it separately, use append
:
a = []
a.append(0)
a.append(0)
print(a)
which outputs [0, 0]
.