python passing by reference - python

Strange things happens today when I do things like this:
a = np.array([1, 2, 3])
b = a
b[0] = 0
print a, b
And then the value seems to be passed by reference! The answer becomes:
result: [0 2 3] [0 2 3]
But usually I think the variable in python is passed by value, like this:
a = np.array([1, 2, 3])
b = a
b = np.array([0, 2, 3])
print a, b
And then the answer becomes:
result: [1 2 3] [0 2 3]
But why is that happenning? How do I decide whether the variable is passed through reference or value? Some people said it was because of the mutable object, but I still don't quite get it. So can you explain it for me? Thanks a lot!

As it doesn't have anything directly to do with NumPy, let's rewrite it as:
a = [1, 2, 3]
b = a
b = [0, 2, 3]
print a, b
The key thing is that names and values are separate things. So on the first line:
a = [1, 2, 3]
You create a list and assign it to a
Now on the next line you assign a to b. There is still only 1 list, both names simply refer to it.
Next you create a new list and assign it to b. Now b refers to the new list instead of the old list. In turn now only 1 name (a) refers to the other list, instead of 2 names.
Thus if we take the first example, where you do:
b[0] = 0
There both a and b reference the same list, thus the change can be observed from both names, as both names refers to the same list.

Variable assignment in Python is always assignment to a reference. In other words, in Python, variables are names, and assigning a variable is declaring a name for something.
a = np.array([1, 2, 3])
b = a
b[0] = 0
print a, b
This means let a be the name for that first array, then let b be a name for a, but a means the first array, so really we're just saying let b also be a name for the first array. Then b[0] = 0 means change the first element of the array. Then when you print a and b, you are printing the same thing twice (the array), because a and b are names for the same thing.
a = np.array([1, 2, 3])
b = a
b = np.array([0, 2, 3])
print a, b
The first two lines are the same as last time. a is a name for the array and so is b. Then b = np.array([0, 2, 3]) means that b is now a name for this new array. So when you print a and b you are printing a, which is still the name for the first array, and b, which is the name for the second array.

Related

Multivariable assignment: order_item, created = [duplicate]

I tried to use multiple assignment as show below to initialize variables, but I got confused by the behavior, I expect to reassign the values list separately, I mean b[0] and c[0] equal 0 as before.
a=b=c=[0,3,5]
a[0]=1
print(a)
print(b)
print(c)
Result is:
[1, 3, 5]
[1, 3, 5]
[1, 3, 5]
Is that correct? what should I use for multiple assignment?
what is different from this?
d=e=f=3
e=4
print('f:',f)
print('e:',e)
result:
('f:', 3)
('e:', 4)
If you're coming to Python from a language in the C/Java/etc. family, it may help you to stop thinking about a as a "variable", and start thinking of it as a "name".
a, b, and c aren't different variables with equal values; they're different names for the same identical value. Variables have types, identities, addresses, and all kinds of stuff like that.
Names don't have any of that. Values do, of course, and you can have lots of names for the same value.
If you give Notorious B.I.G. a hot dog,* Biggie Smalls and Chris Wallace have a hot dog. If you change the first element of a to 1, the first elements of b and c are 1.
If you want to know if two names are naming the same object, use the is operator:
>>> a=b=c=[0,3,5]
>>> a is b
True
You then ask:
what is different from this?
d=e=f=3
e=4
print('f:',f)
print('e:',e)
Here, you're rebinding the name e to the value 4. That doesn't affect the names d and f in any way.
In your previous version, you were assigning to a[0], not to a. So, from the point of view of a[0], you're rebinding a[0], but from the point of view of a, you're changing it in-place.
You can use the id function, which gives you some unique number representing the identity of an object, to see exactly which object is which even when is can't help:
>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120
>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216
Notice that a[0] has changed from 4297261120 to 4297261216—it's now a name for a different value. And b[0] is also now a name for that same new value. That's because a and b are still naming the same object.
Under the covers, a[0]=1 is actually calling a method on the list object. (It's equivalent to a.__setitem__(0, 1).) So, it's not really rebinding anything at all. It's like calling my_object.set_something(1). Sure, likely the object is rebinding an instance attribute in order to implement this method, but that's not what's important; what's important is that you're not assigning anything, you're just mutating the object. And it's the same with a[0]=1.
user570826 asked:
What if we have, a = b = c = 10
That's exactly the same situation as a = b = c = [1, 2, 3]: you have three names for the same value.
But in this case, the value is an int, and ints are immutable. In either case, you can rebind a to a different value (e.g., a = "Now I'm a string!"), but the won't affect the original value, which b and c will still be names for. The difference is that with a list, you can change the value [1, 2, 3] into [1, 2, 3, 4] by doing, e.g., a.append(4); since that's actually changing the value that b and c are names for, b will now b [1, 2, 3, 4]. There's no way to change the value 10 into anything else. 10 is 10 forever, just like Claudia the vampire is 5 forever (at least until she's replaced by Kirsten Dunst).
* Warning: Do not give Notorious B.I.G. a hot dog. Gangsta rap zombies should never be fed after midnight.
Cough cough
>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>>
In python, everything is an object, also "simple" variables types (int, float, etc..).
When you changes a variable value, you actually changes it's pointer, and if you compares between two variables it's compares their pointers.
(To be clear, pointer is the address in physical computer memory where a variable is stored).
As a result, when you changes an inner variable value, you changes it's value in the memory and it's affects all the variables that point to this address.
For your example, when you do:
a = b = 5
This means that a and b points to the same address in memory that contains the value 5, but when you do:
a = 6
It's not affect b because a is now points to another memory location that contains 6 and b still points to the memory address that contains 5.
But, when you do:
a = b = [1,2,3]
a and b, again, points to the same location but the difference is that if you change the one of the list values:
a[0] = 2
It's changes the value of the memory that a is points on, but a is still points to the same address as b, and as a result, b changes as well.
Yes, that's the expected behavior. a, b and c are all set as labels for the same list. If you want three different lists, you need to assign them individually. You can either repeat the explicit list, or use one of the numerous ways to copy a list:
b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects
Assignment statements in Python do not copy objects - they bind the name to an object, and an object can have as many labels as you set. In your first edit, changing a[0], you're updating one element of the single list that a, b, and c all refer to. In your second, changing e, you're switching e to be a label for a different object (4 instead of 3).
You can use id(name) to check if two names represent the same object:
>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
Lists are mutable; it means you can change the value in place without creating a new object. However, it depends on how you change the value:
>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]
If you assign a new list to a, then its id will change, so it won't affect b and c's values:
>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]
Integers are immutable, so you cannot change the value without creating a new object:
>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1
in your first example a = b = c = [1, 2, 3] you are really saying:
'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]
If you want to set 'a' equal to 1, 'b' equal to '2' and 'c' equal to 3, try this:
a, b, c = [1, 2, 3]
print(a)
--> 1
print(b)
--> 2
print(c)
--> 3
Hope this helps!
What you need is this:
a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1 # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)
Simply put, in the first case, you are assigning multiple names to a list. Only one copy of list is created in memory and all names refer to that location. So changing the list using any of the names will actually modify the list in memory.
In the second case, multiple copies of same value are created in memory. So each copy is independent of one another.
The code that does what I need could be this:
# test
aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)
# initialization
a,b,c,d=[[0 for n in range(3)] for i in range(4)]
# changing values
a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)
Result:
('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
To assign multiple variables same value I prefer list
a, b, c = [10]*3#multiplying 3 because we have 3 variables
print(a, type(a), b, type(b), c, type(c))
output:
10 <class 'int'> 10 <class 'int'> 10 <class 'int'>
Initialize multiple objects:
import datetime
time1, time2, time3 = [datetime.datetime.now()]*3
print(time1)
print(time2)
print(time3)
output:
2022-02-25 11:52:59.064487
2022-02-25 11:52:59.064487
2022-02-25 11:52:59.064487
E.g: basically a = b = 10 means both a and b are pointing to 10 in the memory, you can test by id(a) and id(b) which comes out exactly equal to a is b as True.
is matches the memory location but not its value, however == matches the value.
let's suppose, you want to update the value of a from 10 to 5, since the memory location was pointing to the same memory location you will experience the value of b will also be pointing to 5 because of the initial declaration.
The conclusion is to use this only if you know the consequences otherwise simply use , separated assignment like a, b = 10, 10 and won't face the above-explained consequences on updating any of the values because of different memory locations.
The behavior is correct. However, all the variables will share the same reference. Please note the behavior below:
>>> a = b = c = [0,1,2]
>>> a
[0, 1, 2]
>>> b
[0, 1, 2]
>>> c
[0, 1, 2]
>>> a[0]=1000
>>> a
[1000, 1, 2]
>>> b
[1000, 1, 2]
>>> c
[1000, 1, 2]
So, yes, it is different in the sense that if you assign a, b and c differently on a separate line, changing one will not change the others.
Here are two codes for you to choose one:
a = b = c = [0, 3, 5]
a = [1, 3, 5]
print(a)
print(b)
print(c)
or
a = b = c = [0, 3, 5]
a = [1] + a[1:]
print(a)
print(b)
print(c)

Extend separate lists with the outputs of a function

I was wondering if there was a more elegant way of doing the following:
EDIT:
def whaa(x):
# Let's not get too picky about this function
return list(range(0,x)), list(range(-1,x))
a, b = whaa(10)
c = whaa(20)
a.extend(c[0])
b.extend(c[1])
EDIT: The behavior of the function is dependent on the input. And I want the corresponding outputs to go neatly into the same list.
Essentially, what I want to do is access the individual elements of the output tuple and extend my lists without going through the trouble of storing the output into a separate variable. It seems like given this construct, it's not something that's possible but I'm open to suggestions!
You can do it like this:
for x, y in zip([a, b], c):
x.extend(y)
But then why have you not just left a and b in a list in the first place?
c = whaa(10)
for x, y in zip(c, whaa(20)):
x.extend(y)
a, b = c # save unpacking until the end
Use a for loop to extend each element of the returned tuple:
a, b = tuple(x * 2 for x in whaa())
a
# [1, 2, 3, 1, 2, 3]
b
# [2, 3, 4, 2, 3, 4]
For the updated question, you can use zip as the answer of #John:
a, b = tuple(x + y for x, y in zip(whaa(10), whaa(20)))

To remove an element and assign the list in one line

So as the question says, is it possible to remove an element and return the list in one line?
So if lets say
a = [1, 3, 2, 4]
b = a.remove(1)
This would set b to be NoneType, so I was wondering if there's a way to do this.
I suppose this would work:
a = [1, 3, 2, 4]
b = (a.remove(1), a)[1]
This is assuming that you want to do both things:
Modify the original list by removing the element, and
Return the (now modified) list.
EDIT
Another alternative:
b = a.remove(1) or a
Both are fairly confusing; I'm not sure I would use either in code.
EDIT 2
Since you mentioned wanting to use this in a lambda... are you aware that you can use an actual named function any time you would otherwise use a lambda?
E.g. instead of something like this:
map(lambda x: x.remove(1) and x, foo)
You can do this:
def remove_and_return(x):
x.remove(1)
return x
map(remove_and_return, foo)
Since remove returns None, you could just or a to it:
>>> a = [1, 3, 2, 4]
>>> a.remove(1) or a
[3, 2, 4]
List a assign to b:
b = a.remove(1) or a
a is b # True
Using b is the same to use a, and it's dangerous if you get a final object like [b, c, d], because if you update one of this list element, the all elements will update too.
Compare to:
import copy
b = a.remove(1) or copy.copy(a)
I think this way should be safer.
pure functional solution that does not touch passed list
>>> a = [1,2,3]
>>> (lambda a,b: (lambda a0,a1: (a0.remove(b),a1)[-1] )( *((a.copy(),)*2) ))(a, 3)
[1,2]
>>> a
[1,2,3]

Saving one vector variable on to another in python

I have a script that requires to transfer values of one vector onto another.
Code looks like:
b = [1,2,3]
for i ranging from (0,2):
a[i] = b[i] #(transfer all three elements of b to a)
Doing this gets an error
- IndexError: list assignment index out of range
What am i missing ? Thanks for help.
a = b[:]
should more than suffice
the list needs to be the right size if you are referencing it by index. ie. The list a doesn't have 3 elements. I'd just create a new list with a list constructor or even easier do this
a = list(b)
I have a script that requires to transfer values of one vector onto another
I think there is some confusion here between the variable and its content:
>>> a = [10,20]
>>> b = [1,2,3]
>>> c = a
>>> a,b,c
([10, 20], [1, 2, 3], [10, 20])
This create two lists. With two variables (a and c) referencing the same one.
If you write:
>>> a = [10,20]
>>> b = [1,2,3]
>>> c = a
>>> a = b[:]
>>> a,b,c
([1, 2, 3], [1, 2, 3], [10, 20])
You actually create a third list. An bind it to the variable a. But c still hold a reference to the original list.
If you want to really alter the original list, write that instead:
>>> a = [10,20]
>>> b = [1,2,3]
>>> c = a
>>> a[:] = b[:] # "replace every item of the first list
# by every item of the second list"
>>> a,b,c
([1, 2, 3], [1, 2, 3], [1, 2, 3])

Delete list via referent ? > Was : How do you alias variable in Python ? or similar?

In Perl I can say this :
> perl -e '#a=(1,2,3);$b=\#a;$$b[1]=5;print #a'
153
#a=(1,2,3);
$b=\#a;
$$b[1]=5;
print #a
i.e. I can change the original variable #a via the reference $b.
How can I do that in Python ?
=========
Sorry my mistake I was trying to delete the content of the referent array "a" via the reference "b".. and it does not seem to work.
What is the correct way to delete a via b
> a = [1,2,3]
> b = a
> b
[1, 2, 3]
> b = []
> a
[1, 2, 3]
'a' still not empty i.e. I have reference to variable and I want to clean it up via the reference?
All types in Python are references. That means you cannot re-assign an actual variable and expect it to change the original variable. However, you can certainly modify a variable through a "reference" which is automatically created on copy.
a = [1,2,3]
b = a
b[0] = 0
print a
Output
[0,2,3]
If you want to delete a list via a reference, you can use the other solution or do the following:
b[:] = []
You can mutate a list via deletion as such:
>>> a = [1, 2, 3]
>>> b = a
>>> del b[:]
>>> b
[]
>>> a
[]
The special syntax del b[:] is equivalent to b.__delitem__(slice(0, len(b))) - that is, it calls a method on the object that b points to, which ends up mutating that object, instead of assigning a different object to b which is what b = [] does.

Categories

Resources