As what I have understand on python, when you pass a variable on a function parameter it is already reference to the original variable. On my implementation when I try to equate a variable that I pass on the function it resulted empty list.
This is my code:
#on the main -------------
temp_obj = []
obj = [
{'name':'a1', 'level':0},
{'name':'a2', 'level':0},
{'name':'a3', 'level':1},
{'name':'a4', 'level':1},
{'name':'a5', 'level':2},
{'name':'a6', 'level':2},
]
the_result = myFunction(obj, temp_obj)
print(temp_obj)
#above print would result to an empty list
#this is my problem
#end of main body -------------
def myFunction(obj, new_temp_obj):
inside_list = []
for x in obj[:]:
if x['level'] == 0:
inside_list.append(x)
obj.remove(x) #removing the element that was added to the inside_list
new_temp_obj = obj[:] #copying the remaining element
print(new_temp_obj)
# the above print would result to
#[{'name': 'a3', 'level': 1}, {'name': 'a4', 'level': 1}, {'name': 'a5', 'level': 2}, {'name': 'a6', 'level': 2}]
return inside_list
Am I missing something or did I misunderstand the idea of python call by reference?
Python is not pass-by-reference. It is pass-by-object.
Consider the following two functions:
def f(mylist):
mylist = []
def g(mylist):
mylist.append(1)
Now let's say I call them.
mylist = [1]
f(mylist)
print(mylist)
mylist = [1] # reset the list
g(mylist)
print(mylist)
What would the output be?
If Python were pass-by-value, the functions would take a copy of the list, so modifying it would not affect the original list once you return out of the function. So in both cases, you would be printing the original list, [1].
If Python were pass-by-reference, the functions would accept a reference to the object and modifying it would modify the actual object that the reference references, so the first output would be [] and the second, [1,2].
If you run this example, you will find that the first output is [1] (the list if unaffected) and second output is [1,2] (the list is affected).
O_O
When you do new_temp_obj = obj[:], Python is constructing a new object obj[:] and giving it the name new_temp_obj.
If you were to append, Python would look for the thing called new_temp_obj and add elements to it. The argument you passed in tells it where to look for the list.
You are creating a totally new object at a totally new location in memory and simply giving it the same name,new_temp_obj
new_temp_obj = obj[:] #copying the remaining element
This would make new_temp_obj reference to another new list object. You could use id to see that its id changes with this assignment.
Change it to:
new_temp_obj[:] = obj[:]
You'll see your expected result.
Related
I was doing some exercises in python python and I bumped into a problem. I already printed a list reversed and I wanted to print the reversed elements in separate lines too but for some reason it only prints the first one and if I put the first print into comment it prints out the loop just fine. Why?
def main():
pass
if __name__ == '__main__':
main()
list1 = [10, 20, 30, 40, 50]
def reverse_list(l1):
l2 = []
for i in range(len(l1)):
l2.append(l1.pop())
return l2
print(reverse_list(list1))
pass
def reverse_print(l):
listr = reversed(l)
for j in listr:
print(j)
reverse_print(list1)
print("Complete...")
In python, data types are divided into mutable and immutable types:
Mutable object: the value in the memory pointed to by the object can be changed
Immutable object: The value in the memory pointed to by the object cannot be changed, so when the value pointed to by the variable changes, it is equivalent to copying the original value and storing it in a new address, and the variable points to this new address.
a = [1,2,3,4]
a_ref = a
print(a) # [1,2,3,4]
a_ref[0] = 5
print(a) # [5,2,3,4]
It can be seen from the above code
a, a_ref are exactly the same
a and a_ref have the same memory location
modification to a_ref, a will also be changed
for example:
a = [1,2,3,4]
def somefunc(a2):
a2.append(5)
print(a) # [1,2,3,4]
somefunc(a)
print(a) # [1,2,3,4,5]
Normally, it won't change a by calling somefunc(a)
BUT
Since a2 is a reference to a and points to the same memory location, when a2 is modified, it will also be modified to a
in your code:
when you calling reverse_list(list1), l1 is a reference to list1, when you pop the element from l1, it will also pop the element in list1
so after called reverse_list(list1), list1 is empty, that why reverse_print(list1) do not print anything
Sollution
def reverse_list(l1):
l1_copy = l1[:] # copy the whole list
l2 = []
for i in range(len(l1_copy)):
l2.append(l1_copy.pop())
return l2
when you call l1_copy = l1[:]
than l1_copy won't point to same memory location
so l1 and list1 won't modified when modifica l1_copy
PS: english is my second langage, so there maybe have some gramma mistake, please excuse me
at the second print list1 has been emptied out from using the pop() method in reverse_list.
to reverse a list perhaps you should use the reverse()method like the following example:
List1 = [1,2,3]
List1.reverse()
print(List1) # [3,2,1]
I have a class with attributes initialised based on a user-defined dictionary (read in using JSON):
class Knight(object):
def __init__(self, traits):
for k, v in traits.items():
self.__setattr__(k, v)
traitfile = json.load(open(input(), 'r'))
# Where the input file is e.g.
# {'helmet': 'horned',
# 'sword': 'big',
# 'words': ['Ni!', 'Peng', 'Neee-Wom!']}
When I instantiate the object, helmet, sword, and words become attributes as expected. But if I then change an instance attribute, it seems like it affects the original dictionary from which the object was initialised in the first place:
tall_knight = Knight(traitfile)
print(tall_knight.words) # prints ['Ni!', 'Peng', 'Neee-Wom!']
print(traitfile['words']) # also prints ['Ni!', 'Peng', 'Neee-Wom!']
tall_knight.words.append('Ekke ekke!')
print(tall_knight.words) # prints ['Ni!', 'Peng', 'Neee-Wom!', 'Ekke ekke!'] as expected
print(traitfile['words']) # also prints ['Ni!', 'Peng', 'Neee-Wom!', 'Ekke ekke!'] NOT EXPECTED
I did not expect the change to the object's attribute would affect the dictionary it was initialised from. I thought the whole point of instantiation is that the instance is, well, its own instance! What is going on here?! (And how can I stop it?)
Your problem is that traitfile['words'] is a list, and when you copy it to tall_knight.words, you are copying a reference to the list, not the values in it. So when you modify the list in tall_knight, you also modify the value in traitfile['words']. You can work around this by making a copy of the value in the object using copy.copy (or copy.deepcopy if the values may be nested):
import copy()
class Knight(object):
def __init__(self, traits):
for k, v in traits.items():
self.__setattr__(k, copy.copy(v))
As list is mutable object in python so when you create an object the reference would be the same behind the scene so you need to call the list.copy() which will create a copy with different reference then your changes will not reflect in the original one.
first_list = {"a":1, "b":[2,3,4]}
second_list = first_list
second_list["b"].append(34)
print("first one: ", first_list)
print("second one: ", second_list)
Output:
first one: {'a': 1, 'b': [2, 3, 4, 34]}
second one: {'a': 1, 'b': [2, 3, 4, 34]}
So the better to avoid the changes into the original one is to use the copy function: second_list = first_list.copy()
Including more as per your case you also need to create the copy before changing the object:
import copy
class Knight(object):
def __init__(self, traits):
for k, v in traits.items():
self.__setattr__(k, copy.deepcopy(v))
traitfile = json.load(open(input(), 'r'))
Here is the link for reference: Mutable and Immutable datatypes
I'm terribly sorry if this was already asked, but while I could find something similar I didn't find my specific issue. I have Python 3.7.4 - 64 bit. Basically I want to initialize a dictionary where each element is a list of empty lists. The problem is that in the way I'm doing it now I get that every single empty sub-list from the different items' list is the same object even though I am assigning a copy of the list to each item. As you can see in the code below, each sub-list in empty_list_of_lists is a different object. Then I assign the items to the dictionary as a copy of empty_list_of_lists. When I call my_dict['a'] is my_dict['b'] I get an expected False, but when I call my_dict['a'][0] is my_dict['b'][0] I get a True which puzzles me because empty_list_of_lists[0] is empty_list_of_lists[1] returns False and I don't get the logic. How should I go about that?
Here is my code:
empty_list_of_lists = [[] for i in range(5)]
print(empty_list_of_lists[0] is empty_list_of_lists[1]) # returns False --> expected
dict1 = {'a': empty_list_of_lists.copy(), 'b': empty_list_of_lists.copy()}
print(dict1['a'] is dict1['b']) # returns False --> expected
print(dict1['a'][0] is dict1['b'][0]) # returns True --> What?
you can use:
dict1 = {'a': [[] for _ in range(5)], 'b': [[] for _ in range(5)]}
or you can use copy.deepcopy
import copy
dict1 = {'a': copy.deepcopy(empty_list_of_lists), 'b': copy.deepcopy(empty_list_of_lists)}
you can read more about shallow and deep copy operations here
in your code you are using a shallow copy but what you need a deep copy, from the above docs:
A shallow copy constructs a new compound object and then (to the
extent possible) inserts references into it to the objects found in
the original.
A deep copy constructs a new compound object and then, recursively,
inserts copies into it of the objects found in the original.
If you have a list (Original) in python like:
class CustomID (object):
def __init__(self, *args):
self.ID = ''
self.manymore = float()
self.visited = False
self.isnoise = False
IDlist = ['1','2','3','4','5','6','7','8','9','10']
Original = list()
for IDs in IDlist:
NewObject = CustomID()
NewObject.ID = IDs
Original.append(NewObject)
and if you do a comprehension for a new list and a function to use over the comprehension sublist:
def Func(InputList=list()):
for objects in InputList:
objects.visited = True
return InputList
New_List = [member for member in Original if (int(member.ID)>5)]
ThirdList = Func(New_List)
Is this (New_List) resulting in a shallow or deep copy of the original list? The matter is relevant for me, if the original list contains objects, which attributes can change in the code to follow New_List creation (ThirdList). New_list is send to a function, which will change the attributes. The question is if you try to reuse the original list for the same function with different comprehension (lets say (members>4).
New_List = [member for member in Original if (int(member.ID)>4)]
Actually:
print New_List[3].visited
gives True.
You are creating a shallow, filtered copy.
Your loop doesn't create copies of member, it references them directly.
Not that you would need to create copies, all objects in your original list are immutable integers. Moreover, CPython interns small integers, creating copies would only result in the exact same objects being used for these.
To illustrate, try to create a copy of a list containing mutable objects instead. Here I used a dictionary:
>>> sample = [{'foo': 'bar'}]
>>> copy = [s for s in sample]
>>> copy[0]['spam'] = 'eggs'
>>> copy.append({'another': 'dictionary'})
>>> sample
[{'foo': 'bar', 'spam': 'eggs'}]
The copy list is a new list object containing a reference to the same dictionary contained in sample. Altering that dictionary is reflected in both copy and sample, but appending to copy doesn't alter the original list.
As for your updated loop code, your sample produces a New_List list that still shares objects, and New_List[3].visited is in fact True:
>>> New_List[3].ID
'8'
>>> New_List[3].visited
True
because it is still the same object found in Original at index 7:
>>> New_List[3] is Original[7]
True
which is the same object still found in ThirdList at index 2:
>>> ThirdList[2] is New_List[3]
True
Another idea, which worked for me is to implement a flagClear method in the class
class CustomID (object):
def __init__(self, *args):
self.ID = ''
self.manymore = float()
self.visited = False
self.isnoise = False
def flagClear(self):
self.visited = False
return self
And then, every time I construct a new list, simply to use the method:
New_List = [member.flagClear() for member in Original if (int(member.ID)>4)]
If the only thing I modify in the CustomID is the .visited flag, than this works. Obviously, it will not be perfect. If someone needs a complete solution, the suggestion of Martijn Pieters will work best (implementing .copy() method):
import copy
class CustomID (object):
def __init__(self, *args):
self.ID = ''
self.manymore = float()
self.visited = False
self.isnoise = False
def CustomCopy(self):
return copy.deepcopy(self)
New_List = [member.CustomCopy() for member in Original if (int(member.ID)>4)]
Thank you Martijn, this was really a learning experience for me.
Situation: After making a copy of the original list I use pop to modify said copy. As it turns out, the original list gets affected by the change.
I even after checking the original list and the copy are not the same object, poping an element of the copy will will pop the same element in the original.
See below for an example of the script. Thanks in advance for your help.
l = [['1412898', 'Jack', 'headache med', '8ET-500'],
['1423859', 'Sonny', 'prostate med', '8ET-800'],
['1413836', 'Paco', 'headache med', '8ET-500']]
class App(object):
def __init__(self, info):
self.fp_rows= info
def sortbyauditor(self):
self.fp_rows_copy = self.fp_rows[:]
print self.fp_rows is self.fp_rows_copy
for i in self.fp_rows_copy:
i.pop(1)
print self.fp_rows_copy
print self.fp_rows
app= App(l)
app.sortbyauditor()
some_list[:] is only a shallow copy. You seem to need a deep copy
from copy import deepcopy
copy = deepcopy(some_list)
Edit
To understand why "one objects affects the other" take a look at the id of each list:
original = [[1, 2], [3, 4]]
shallow = original[:]
deep = deepcopy(original)
print([id(l) for l in original])
# [2122937089096, 2122937087880]
print([id(l) for l in shallow])
# [2122937089096, 2122937087880]
print([id(l) for l in deep])
# [2122937088968, 2122937089672]
You can see that the ids of the lists in original are the same as the ids in shallow. That means the nested lists are the exact same objects. When you modify one nested list the changes are also in the other list.
The ids for deep are different. That are just copies. Changing them does not affect the original list.