I can't seem to find an explanation anywhere for this...
Suppose I have a vector y initialized to all zeroes:
from numpy import *
y = zeros(5)
It could also be a plain python array, I don't think it really matters.
I noticed that the behavior of the for x in y:
statement is that it makes a copy of each element in y, thus when you modify x, it does not modify y.
for x in y:
x = 1
print y
output : array([ 0., 0., 0., 0., 0.])
My question is the following : Why is that so? I thought in Python everything was all byReference and that there is very little byValue passing around?
How could I do the following, using referenced variables? Doing the following seems to work :
for i in range(len(y)):
y[i] = 2*rand()-1
But from what I know about Python开发者_运维技巧, this is wrong and I believe it will be slow when I start using vectors of thousands or millions of values.
What else could I do?
You can replace this:
y = np.zeros(5)
for i in range(len(y)):
y[i] = 2*rand()-1
with:
y=2*np.random.rand(5)-1
If y is a Python sequence (tuple, list, array, etc) or Numpy array,
for x in y:
iterates through y
, setting x
to each element in y
.
If the elements are immutable objects like str
, int
, float
or a Numpy numeric type,
then changing the value of x
does not change the value in y
.
If the elements are mutable objects, such as a list
, then mutating the list
will affect y
.
Think of it this way: x
is "pointing" to an object:
y---> [o----o----o----o]
^
|
x
When you write an assignment statement:
x += 1
Now you are reassigning x
to "point" to a different object:
y---> [o----o----o----o]
x---> o
On the other hand, if you mutate a list with, say, x.append(1)
, then
x
still points to the same object (e.g. a list), but you've mutated the contents of that list.
Python does use "references" only. But what is called "reference" in Python (and Java, and propably a handful of other languages) is not like in "pass-by-reference" or "references in C++". When you iterate over something like for i in iterable:
, i
points to the current item. But when you do i = ...
in the loop, you're overwriting that reference with a new one instead of replacing the object pointed to. So actually, Python (and all the other languages mentioned) are pass-by-value. Except that those values are always (kind of) pointers.
Note that this does not apply for i.attr = ...
! Setting members is different, it's a method call (one of .__setitem__
of the object's __dict__
, .__setattr__
or .__setattribute__
afaik). So if you did i.some_member
in the loop, you would indeed alter the items of the iterable. Also note that this is of course moot with immutable objects like numbers or strings - you cannot possibly alter those.
If you want to alter e.g. a list directly, you need to use indices (although you shouldn't need range(len(...))
, you can use enumerate(the_list)
and you get indices and the current items). Also consider generating the values you want in the first place by using e.g. a list comprehension.
Please someone correct me if I am wrong, but the integer type in Python is not mutable and that is why when you perform some operation on one of the elements of the vector you get back a new object. I looked and looked but haven't found any confirmation of this hunch but when I try
>>> def zeros(length):
... vector = []
... for each in range(length):
... vector.append([0])
... return vector
...
>>> zeros(8)
[[0], [0], [0], [0], [0], [0], [0], [0]]
>>> y = zeros(8)
>>> for x in y:
... x[0] += 1
...
>>> y
[[1], [1], [1], [1], [1], [1], [1], [1]]
and we know that lists are mutable we get what we expect.
I don't see how that code sample is wrong at all - as long as y
is accessible from the scope that the for
loop is in, all should be good.
精彩评论