Python programming problems with class and lists [duplicate] - python

This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Closed 7 years ago.
I was coding with Python and a bug made me notice this.
Say, I have this code:
class test1(object):
def __init__(self):
self.hello = ["hello"]
self.text_back = test2().stuff(self.hello)
print "With list: ",self.hello[0], self.text_back[0]
class test2(object):
def stuff(self,text1):
self.asdf = text1
self.asdf[0] = "goodbye"
return self.asdf
a = test1()
#---------------
class test3(object):
def __init__(self):
self.hello = "hello"
self.text_back = test4().stuff(self.hello)
print "Without list:",self.hello, self.text_back
class test4(object):
def stuff(self,text1):
self.asdf = text1
self.asdf = "goodbye"
return self.asdf
b = test3()
The output is:
With list: goodbye goodbye
Without list: hello goodbye
I have the identical code between the two, except that one is list and one isn't. Why am I getting different results?

In code, we are not creating new variable for list, we just create reference variable name.
Demo:
>> l1 = ["hello"]
>>> l1= l1
>>> l2= l1
>>> id(l1), id(l2)
(3071912844L, 3071912844L)
>>> l1
['hello']
>>> l2
['hello']
>>> l2[0] = "goodbye"
>>> l2
['goodbye']
>>> l1
['goodbye']
>>>
Normally copying a list:
>>> l1 = [1,2,3]
>>> l2 = l1[:]
>>> id(l1), id(l2)
(3071912556L, 3071912972L)
>>> l1.append(4)
>>> l1
[1, 2, 3, 4]
>>> l2
[1, 2, 3]
>>>
Nested datastructures:
>>> l1 = [1,2, [1,2], [3,4]]
>>> l2 = l1[:]
>>> l1.append(4)
>>> l1
[1, 2, [1, 2], [3, 4], 4]
>>> l2
[1, 2, [1, 2], [3, 4]]
>>> l1[2].append(3)
>>> l1
[1, 2, [1, 2, 3], [3, 4], 4]
>>> l2
[1, 2, [1, 2, 3], [3, 4]]
>>>
Deep copies in Python
>>> import copy
>>> l1 = [1,2, [1,2], [3,4]]
>>> l2 = copy.deepcopy(l1)
>>> l1.append(4)
>>> l1[2].append(3)
>>> l1
[1, 2, [1, 2, 3], [3, 4], 4]
>>> l2
[1, 2, [1, 2], [3, 4]]
>>>
Link

Related

What's the difference between list.append(a[:]) and list.append(a) in python?

This is related to leetcode problem #39.
I started with results.append(solution), which does not append properly to the list, and found in the solution that results.append(solution[:]) works. What's the difference between these two syntax?
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
results=[]
def backtracking(candidates,target,start,solution,results):
#print(start,target,solution,results)
if target<0:
return
if target==0:
results.append(solution[:])
return
for i in range(start,len(candidates)):
solution.append(candidates[i])
backtracking(candidates,target-candidates[i],i,solution,results)
solution.pop()
backtracking(candidates,target,0,[],results)
return results
a[:] will create a new list.
c.append(b) appends the list b to c.
Following code will help understand this better -
>>> a=[1,2,3]
>>> b=[4,5]
>>> c=[1,2,3]
>>> a.append(b) #append b
>>> c.append(b[:]) #create new list and append
>>> a
[1, 2, 3, [4, 5]]
>>> c
[1, 2, 3, [4, 5]]
>>> b
[4, 5]
>>> a[3][0]=99 #modify a
>>> a
[1, 2, 3, [99, 5]] #a modified
>>> b
[99, 5] #so does b
>>> c
[1, 2, 3, [4, 5]] #modify c
>>> c[3][1]=99
>>> c #c modified
[1, 2, 3, [4, 99]]
>>> b #original b did not get modified
[99, 5]
>>>
As you can see from the id of the objects, making a slice creates a new list
>>> a = [1, 2, 3]
>>> id(a)
2711680383816
>>> id(a[:])
2711683338696
whereas assigning the list directly refers to the same object
>>> b = a
>>> id(b)
2711680383816
a is a list and a[:] is a new list with all elements copied.
>>> a = [1, 2, 3]
>>> a == a[:]
True
>>> a is a[:]
False
Let's have another list b = ["a", "b"]. append adds whatever you give it to the end of a list. If you append another list, the reference to that list gets added and might lead to un expected behaviours:
>>> b.append(a)
>>> b
["a", "b", [1, 2, 3]]
>>> a[0] = "c"
>>> b
["a", "b", ["c", 2, 3]]
>>> b[2][1] = 42
>>> a
["c", 42, 3]
>>> a is b[2]
True
You can see, that after appending a, if you change an element in a, it also changes in b. This is because b only has a reference to a. To prevent that, you can instead do b.append(a[:]). This will copy the values in a, so when you then change values in a, values in b stay what they were when you copied them:
>>> b.append(a)
>>> b
["a", "b", [1, 2, 3]]
>>> a[0] = "c"
>>> b
["a", "b", [1, 2, 3]]
>>> b[2][1] = 42
>>> a
["c", 2, 3]
>>> a is b[2]
False
So in your question, using solution[:] makes sure that whatever has been added to the results doesn't change when solution.append happens on the next iteration of the for loop.

Equal strings auto updating (List object reference)

I recently did an exam for university and I got asked what would be the output of this program:
def fun(x):
y=x
x.append(4)
print(str(x)+" "+str(y))
fun(["one","two",3,5.0])
I answered that the y list would be ["one","two", 3,5.0] and after appending 4 to it, the x list would be equal to the same but with a 4 at the end of it. To my surprise, when I printed both lists, they were equal even though the x list update was performed after establishing an equality between both lists. Why did this happen?
Thank you
You have given reference if list x to y. So any change in list x would also affect list y.
y=x
For example:
>>> x = ["one","two",3,5.0]
>>> y = x
>>> x[3] = 4
>>> x
['one', 'two', 3, 4]
>>> y
['one', 'two', 3, 4]
Here both x and y have same identity.
>>> x is y
True
>>> id(x)
3073118540L
>>> id(y)
3073118540L
You can better understand this using swampy module:
>>> from swampy.Lumpy import Lumpy
>>> lump = Lumpy()
>>> x = ["one","two",3,5.0]
>>> y = x
>>> x[3] = 4
>>> lump.object_diagram()
What you were expecting can be achieved by copying the list x to list y like this:
>>> x = ["one","two",3,5.0]
>>> y = x[:]
>>> x.pop()
5.0
>>> x
['one', 'two', 3]
>>> y
['one', 'two', 3, 5.0]
So by copying the content from x to y, they don't hold the same identity:
>>> id(x)
3073240428L
>>> id(y)
3073240588L
>>> x is y
False
Using swampy:
>>> from swampy.Lumpy import Lumpy
>>> lump = Lumpy()
>>> x = ["one","two",3,5.0]
>>> y = x[:]
>>> lump.draw_object()
>>> lump.object_diagram()
For better explanation visit here How do I copy an object in Python?
Actually x and y are labels that reference to object so when you assign y=x you crate 2 reference to one object , so when you change one of them you change the main object .
Also you may note that x , y are local variables when you made inplace changes like append you changed the main object , but if you use assignment python create a new object :
>>> def fun(x):
... y=x
... x=x+[3]
... print(str(x)+" "+str(y))
...
>>> fun(["one","two",3,5.0])
['one', 'two', 3, 5.0, 3] ['one', 'two', 3, 5.0]
in-place changes to objects do not classify names as locals; only actual name
assignments do. For instance, if the name L is assigned to a list at the top level of a
module, a statement L = X within a function will classify L as a local, but L.append(X)
will not. In the latter case, we are changing the list object that L references, not L itself—
L is found in the global scope as usual, and Python happily modifies it without requiring
a global (or nonlocal ) declaration. As usual, it helps to keep the distinction between
names and objects clear: changing an object is not an assignment to a name.(from learning python by mark lutz)
class A:
global L
L=[1,2]
def b(self):
L=[0,0]
return L
def c(self):
L.append(5)
return L
a=A()
print a.b()
print a.c()
result :
[0, 0]
[1, 2, 5]
Because list are mutable objects. see Python Data Model
In [1]: a = [1]
In [3]: b = a
In [4]: b
Out[4]: [1]
In [5]: b.append(2)
In [6]: a
Out[6]: [1, 2]
In [7]: b
Out[7]: [1, 2]
In [8]: id(a), id(b)
Out[8]: (140260765233376, 140260765233376)
Because the name y is bound to the same list as x
y = x
This is a great drawing of how that looks:
x y
| /
| /
["one", "two", 3, 5.0]
x.append(4)
x y
| /
| /
["one", "two", 3, 5.0, 4]
You can try following example. It will help you to get differences between assignment operator and method like copy(shallow), deepcopy.
>>> import copy
>>> l1 = [1,2, [1,2]]
>>> l1
[1, 2, [1, 2]]
#Create l2, l3, l4 by copy, deepcopy method and normal assignment.
>>> l2 = copy.copy(l1)
>>> l3 = copy.deepcopy(l1)
>>> l4 = l1
>>> l2
[1, 2, [1, 2]]
>>> l3
[1, 2, [1, 2]]
>>> l4
>>> [1, 2, [1, 2]]
#-----------------------Now Append value to l1
>>> l1.append(9)
>>> l1
[1, 2, [1, 2], 9]
>>> l2
[1, 2, [1, 2]]
>>> l3
[1, 2, [1, 2]]
>>> l4
>>> [1, 2, [1, 2], 9]
#-----------------------Now Append value to l1[2]
>>> l1[2].append(5)
>>> l1
[1, 2, [1, 2, 5], 9]
>>> l2
[1, 2, [1, 2, 5]]
>>> l3
[1, 2, [1, 2]]
>>> l4
>>> [1, 2, [1, 2, 5], 9]
#------------------------

python list index is the same list [duplicate]

This question already has answers here:
Confusing [...] List in Python: What is it?
(9 answers)
Closed 9 years ago.
I just seen a output like below - just want to know what is happening here.
>>> l = [1,2,3,4]
>>> l[0]=l
>>> l
[[...], 2, 3, 4]
Why the l[0] value has displayed like this? Can anyone explain me why this behavior.
I was thinking it'd return like, [[1,2,3,4], 2, 3, 4].
Cheers,
Kalai
It shows the ... because otherwise it would have to infinitely recurse.
A list object in Python is a pointer to a list- assigning it like l[0] = l doesn't make a copy. For instance, try
l1 = [1, 2, 3, 4]
l2 = [1, 2]
l2[0] = l1
print l2
# [[1, 2, 3, 4], 2]
l2[0].append(5)
print l1
# [1, 2, 3, 4, 5]
Notice that even though you never changed l1 explicitly, it has now been appended to.
Therefore, when you place a list within itself, that item of the list is still a link to the entire list. After your code above, try doing:
l[1] # ==> 2
l[0][1] # ==> 2
l[0][0][1] # ==> 2
Use a copy of the list to avoid infinite recursion:
In [10]: l = [1,2,3,4]
In [11]: l[0] = l[:]
In [12]: l
Out[12]: [[1, 2, 3, 4], 2, 3, 4]
If you would have used a PrettyPrinter, the output would had been self explanatory
>>> l = [1,2,3,4]
>>> l[0]=l
>>> l
[[...], 2, 3, 4]
>>> pp = pprint.PrettyPrinter(indent = 4)
>>> pp.pprint(l)
[<Recursion on list with id=70327632>, 2, 3, 4]
>>> id(l)
70327632

Learning loops and lists

I'm a Python beginner and I'm currently going through Zed Shaw's course "Learn Python the Hardway"
So, in exercise 32 we are told:
How do you make a 2-dimensional (2D) list?
That's a list in a list like this: [[1,2,3],[4,5,6]]
I did this:
# Extra 1
global_list = [[1, 2, 3]]
inside_list = []
for i in global_list[0]:
inside_list.append(i)
global_list.append(inside_list)
print(global_list)
But I’m not entirely convinced that's the correct way. My question is: Is there a way to get the same result without ever leaving the for i in.... loop?
I also tried this, to no avail.
global_list = [[1, 2, 3]]
inside_list = []
for i in global_list[0]:
inside_list.append(i)
global_list.append(inside_list)
print(global_list)
Thanks in advance for your answers.
Lists can be appended and inserted into a list just like any other object, e.g:
outer_list = []
print(outer_list)
inner_list1 = [1, 2, 3]
outer_list.append(inner_list1)
print(outer_list)
inner_list2 = [4, 5, 6]
outer_list.append(inner_list2)
print(outer_list)
I am not sure if you already went over list comprehension. However, one nice way of doing what you are doing is:
>>> global_list = [[1,2,3]]
>>> global_list.append([i + 3 for i in global_list[0]])
>>> print global_list
[[1, 2, 4], [4, 5, 6]]
The question was "How do you make a 2-dimensional (2D) list?". The answer given was "That's a list in a list like this: [[1,2,3],[4,5,6]]". Literally, that's the answer:
>>> a = [[1, 2, 3], [4, 5, 6]]
>>> print a
[[1, 2, 3], [4, 5, 6]]
You can also do this:
>>> a = [[1, 2, 3]]
>>> a.append([4, 5, 6])
>>> a
[[1, 2, 3], [4, 5, 6]]
You don't need a for loop to append a list inside another list:
>>> a = [[1, 2, 3]]
>>> a[0]
[1, 2, 3]
>>> a.append(a[0])
>>> a
[[1, 2, 3], [1, 2, 3]]
However, this makes the second element of the list of lists the same as the first, so if you change one you change the other:
>>> a[0] is a[1]
True
>>> a[0][0] = 4
>>> a
[[4, 2, 3], [4, 2, 3]]
What you can do to make a copy of the list is list(a[0]):
>>> a = [[1, 2, 3]]
>>> a[0]
[1, 2, 3]
>>> a[0] is a[0]
True
>>> list(a[0])
[1, 2, 3]
>>> a[0] is list(a[0])
False
>>> a.append(list(a[0]))
>>> a
[[1, 2, 3], [1, 2, 3]]
>>> a[0] is a[1]
False
>>> a[0][0] = 4
>>> a
[[4, 2, 3], [1, 2, 3]]

How to add an item at the last nested list of an outer list in python?

Is thery any nice compact way of adding an item to the last nested list of an outer list.
I.e:
a=[1,2,[3,4,[5,6]]]
and after the insertion of 7 i want my list to become
a=[1,2,[3,4,[5,6,7]]]
You can use indexing to reference the last inner list:
>>> a=[1,2,[3,4,[5,6]]]
>>> a[2][2].append(7)
>>> a
[1, 2, [3, 4, [5, 6, 7]]]
Or you can write a function to seek out the last inner list:
>>> def findlastlist(s):
while s and isinstance(s[-1], list):
s = s[-1]
return s
>>> a=[1,2,[3,4,[5,6]]]
>>> findlastlist(a).append(7)
>>> a
[1, 2, [3, 4, [5, 6, 7]]]
If you don't look for a general solution:
>>> a=[1,2,[3,4,[5,6]]]
>>> a[-1][-1].append(7)
>>> print a
[1, 2, [3, 4, [5, 6, 7]]]
If you do, here's a naive implementation (for usage, see the doctests):
A function, that returns the nesting level of a list:
def nesting(alist, level=0):
""" Get the nesting level of a list.
>>> nesting([])
0
>>> nesting([1, 2])
0
>>> nesting([1, [2]])
1
>>> nesting([1, 2, [3, 4, [5, 6]]])
2
>>> nesting([1, 2, [3, 4, [5, 6]], [33, 44, [55, 66]]])
2
"""
try:
alist[-1]
except IndexError:
return level
except TypeError:
return level - 1
else:
return nesting(alist[-1], level=level + 1)
A function, that appends an element to alist at a certain level:
def append_nested(alist, element, level):
"""
>>> x = []
>>> append_nested(x, 'hello', nesting(x))
['hello']
>>> x = [1, 2, 3]
>>> append_nested(x, 'hello', nesting(x))
[1, 2, 3, 'hello']
>>> x = [1, 2, 3, [4, 5]]
>>> append_nested(x, 'hello', nesting(x))
[1, 2, 3, [4, 5, 'hello']]
>>> x = [1, 2, 3, [4, 5], [7, 8]]
>>> append_nested(x, 'hello', nesting(x))
[1, 2, 3, [4, 5], [7, 8, 'hello']]
>>> x = [1,2,[3,4,[5,6]]]
>>> append_nested(x, 7, nesting(x))
[1, 2, [3, 4, [5, 6, 7]]]
>>> x = [1,2,[3,4,[5,6]]]
>>> append_nested(x, 7, 0) # append to the 'root' list
[1, 2, [3, 4, [5, 6]], 7]
"""
z = alist
for i in range(level):
z = z[-1]
z.append(element)
return alist
To test them, just run:
if __name__ == '__main__':
import doctest
doctest.testmod()
If you need a general solution try this:
def last_inner_append(x, y):
try:
if isinstance(x[-1], list):
last_inner_append(x[-1], y)
return x
except IndexError:
pass
x.append(y)
return x
>>> x = [1,2,[3,4,[5,6]]]
>>> y = 7
>>> last_inner_append(x, y)
[1,2,[3,4,[5,6,7]]]
It recursively works through the final element of nested lists until it reaches something that is not a list. At this point it sticks your value there. The try/except block allows it to handle empty lists.

Categories

Resources