I am still a bit confused about how arguments are passed in python.
I thought non-primitive types are passed by reference, but why does the following code not print [1] then?
def listTest(L):
L = L + [1]
def main:
l = []
listTest(l)
print l #prints []
and how could I make it work.
I guess I need to pass "a pointer to L" by reference
In listTest() you are rebinding L to a new list object; L + [1] creates a new object that you then assign to L. This leaves the original list object that L referenced before untouched.
You need to manipulate the list object referenced by L directly by calling methods on it, such as list.append():
def listTest(L):
L.append(1)
or you could use list.extend():
def listTest(L):
L.extend([1])
or you could use in-place assignment, which gives mutable types the opportunity to alter the object in-place:
def listTest(L):
L += [1]
Related
I am new to python and was going through the python3 docs. In python strings are said to be immutable, then how is this possible:
if __name__ == '__main__':
l = 'string'
print(l)
l = l[:2]
print(l)
returns this output:
string
st
Informally, l now points to a new immutable string, which is a copy of a part of the old one.
What you cannot do is modify a string in place.
a = "hello"
a[0] = "b" # not allowed to modify the string `a` is referencing; raises TypeError
print(a) # not reached, since we got an exception at the previous line
but you can do this:
a = "hello"
a = "b" + a[1:] # ok, since we're making a new copy
print(a) # prints "bello"
The key to understanding this problem is to realize that the variable in Python is just a "pointer" pointing to an underlying object. And you confused the concept of immutable object and immutable variable(which does not exist in Python).
For instance, in your case, l was initially a pointer pointing to a str object with content "string". But later, you "redirect" it to a new str object whose content is "st". Note that when the program runs to the line l = l[:2], it's the pointer being modified, not the object pointed to by the pointer. If you wish, you can also "redirect" l to another object with type other than str, say l = 123. Just remember, the original object pointed to by l(str "string") is not modified at all, it's still there in the memory (before it is garbage-collected), but just no longer pointed to by l.
For you to better understand the concept of immutable object, let's look at a mutable object. For example, list in Python is mutable.
l = [1, 2, 3] # a new list with three elements
l.append(4) # append a new element 4 to the list (l is now modified!!!)
In the code above, we modified l by appending a new element to it. Throughout the program, l points to the same object, but it's the object pointed to by l that is changed in the process.
Strings themselves are immutable, but the variables can be bound to anything.
The id() method checks the "id" of an object. See below.
>>> l = 'string'
>>> print(l, id(l))
string 2257903593544
>>> l = l[:2]
>>> print(l, id(l))
st 2257912916040
l was bounded to a new immutable object which only contains "st".
See my code in python 3.4. I can get around it fine. It bugs me a little. I'm guessing it's something to do with foo2 resetting a rather than treating it as list 1.
def foo1(a):
a.append(3) ### add element 3 to end of list
return()
def foo2(a):
a=a+[3] #### add element 3 to end of list
return()
list1=[1,2]
foo1(list1)
print(list1) ### shows [1,2,3]
list1=[1,2]
foo2(list1)
print(list1) #### shows [1,2]
In foo2 you do not mutate the original list referred to by a - instead, you create a new list from list1 and [3], and bind the result which is a new list to the local name a. So list1 is not changed at all.
There is a difference between append and +=
>>> a = []
>>> id(a)
11814312
>>> a.append("hello")
>>> id(a)
11814312
>>> b = []
>>> id(b)
11828720
>>> c = b + ["hello"]
>>> id(c)
11833752
>>> b += ["hello"]
>>> id(b)
11828720
As you can see, append and += have the same result; they add the item to the list, without producing a new list. Using + adds the two lists and produces a new list.
In the first example, you're using a method that modifies a in-place. In the second example, you're making a new a that replaces the old a but without modifying the old a - that's usually what happens when you use the = to assign a new value. One exception is when you use slicing notation on the left-hand side: a[:] = a + [3] would work as your first example did.
This is the code I type. Here the value of int has not changed outside of function but value of list has changed.I was expecting the value of list will not change. What is the reason?
>>> def p1(list1):
list1[0]=1000
>>> def p2(i):
i+=10
>>> li=[1,2,3,4,5]
>>> int1=10
>>> p1(li)
>>> p2(int1)
>>> li
[1000, 2, 3, 4, 5]
>>> int1
10
Note that in p1, you are not assigning a value to list1, which is the name of a variable local to p1. You are actually assigning to the first element of the list object referenced by list1, which is the same list object referenced by li in the enclosing scope.
In p2, on the other hand, i+=10 does assign a value to the local variable i, not the variable int1 in the enclosing scope. This is because the += operator on objects of type int do not actually modify the object, but return a new object instead. (That is, for an int, i+=10 is equivalent to i = i + 10.)
Just to show that += can operate on the underlying object directly, consider this function:
def p3(list1):
list1 += [10]
Then run it on a list:
>>> foo = [1,2,3]
>>> p3(list1)
>>> foo
[1, 2, 3, 10]
Here, the call list1 += [10] is really equivalent to list1.extend([10]), not list1 = list1 + [10], due to how list.__iadd__ is defined. Here, you are again not assigning a value to the name list1, but rather invoking a method on the object referenced by list1 (which is the same object referenced by foo).
(Update: as pointed out by user2357112, technically you do assign to list1, but list.__iadd__ is designed to properly assign the same list back, so that the end result is that you still have a reference to the same mutable object you started with.)
Assigning to a variable, like i+=10, is an operation on a variable, while index assignment, like list1[0]=1000, is an operation on the object a variable refers to.
When you call p2(int1), the local variable i gets set to refer to the int int1 referred to, and then the i+=10 statement redirects i to refer to a new, larger integer. int1 still refers to the old integer.
When you call p1(li), the local variable list1 gets set to refer to the list li referred to, and then the list1[0]=1000 statement sets the first cell of the list to refer to 1000. li still points to this list throughout the modification, and after the function returns, li still reflects the change.
(There's another complexity here in that += also requests that an object mutate itself if it's mutable, in addition to performing an assignment, but that doesn't come up in this code.)
A list is like a pointer to a list object.
#For example
a =[0,0]
b = a
b[0]=1
resulting in
b
[1,0]
Where as an int is an a different beast
#For Example
a = 0
b = a
b +=1
resulting in
a
1
to get the int behaviour from a listyou can make a clone of a list by passing it to list()
#for example
a = [0,0]
b = list(a)
b[0]=1
resulting in
a
[0,0]
and if you want the list behaviour from an int I guess you would have to just put it in a list.
>>> c=[1,100,3]
>>>def nullity (lst):
lst=[]
>>>nullity (c)
>>>c
[1,100,3]
Why c doesn't return []? Isn't nullity(c) mean c=lst so thy both point now at []?
Python has pass-by-value semantics, meaning that when you pass a parameter to a function, it receives a copy to the object's reference, but not the object itself. So, if you reassign a function parameter to something else (as you did), the assignment is local to the function, the original object "outside" the function remains unchanged. A completely different situation happens if you change something inside the object:
def test(a):
a[0] = 0
lst = [1, 2, 3]
test(lst)
lst
=> [0, 2, 3]
In the above snippet the list's elements were changed, that's because both lst and a are pointing to the exact same object. Now back to the original question - notice that in Python 3.2 and below there isn't a clear() method (it was added in Python 3.3), here is a related discussion. But you can do this for the same effect, and it'll work in Python 2.x and 3.x:
def nullity(lst):
del lst[:]
You're reassigning local variable lst to a new empty list. To empty an existing list, you need to delete all its members:
del lst[:]
Lets use the id() function
In [14]: c=[1,2,3]
In [15]: def nullity (lst):
...: print id(lst)
...: lst=[]
...: print id(lst)
...:
In [16]: id(c)
Out[16]: 33590520
In [17]: nullity(c)
33590520
33591024
When doing the reassignment a new local object is created which is not the same as the one used when calling the function.
Python does not allow you to replace parameters. You are only changing what "lst" refers to within the function, but it does not affect the calling code.
Instead change the list itself by clearing it:
def nullity (lst):
lst.clear() # With Python >= 3.3
>>> def test():
... a.remove(1)
>>> a = [1,2]
>>> test()
>>> print a
[2]
Why does a equal [2] rather than [1,2]?
List is mutable. If you pass it to a function, and the function changes it, it stays changed.
Use an immutable structure: tuple: a = (1,2)
Pass a copy of original list: b = list(a); b.remove(1) — now a and b have different contents, a hasn't changed.
Also, try not to use mutable global data. Either pass a to the function, or have a as an attribute of an object, and the function as its method.
It's not clear what you want. Your test() function modifies the global 'a' list, so it's unsurprising that 'a' gets modified.
If you want 'test' to work on a copy of a instead directly on a, you may copy it first.
For example,
def test():
a2 = list(a)
a2.remove(1)
Lists are mutable objects, they are meant to be changed. If you want to forbid changes, convert it to a tuple (e.g. a = (1, 2)) instead. Tuples are immutable, so it's not possible to change them without copying and re-assigning the variable.
Because the list a exists in the global namespace and when you call a remove on it, the value 1 is removed.
If you don't want it to be modified, simply create a new list. If you call remove on the list a, of course it going to remove the value.