This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 9 years ago.
I was working with Python deep copy trying to create a total copy of original object, but the deep copy didn't seem to create a copy. It still shares the same reference with original object, which is not desired
Here's the code. I have a class Board, the instance of which I want to deep copy.
import copy
class Board:
xpos = None
opos = None
count = None
status = []
def __init__(self, size):
self.xpos=[0,0]
self.opos=[size-1,size-1]
self.count = size*size-2
for i in range(size):
tmp = ['-']*size
self.status.append(tmp)
self.status[0][0] = 'X'
self.status[size-1][size-1]= 'O'
Somewhere in another function I want to call
board=Board()
localboard=copy.deepcopy(board)
# then do modification to local board....
# but it seems the old board is also affected. This is so weird since
# I am already using deep copy.
So how can I create a deep copy of the old board? I don't want to share any reference, since I will do modification on the local one and want to keep the old intact..
You should remove the following from the Board definition:
xpos = None
opos = None
count = None
status = []
and add the following to its __init__():
self.status = []
Otherwise all your instances share the same status, which is an attribute of the class, not of the instance.
Related
This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 3 years ago.
I'm new to Python and I'm making my own chess game to help learn it. I know this topic has been covered a lot, but I don't understand how to apply the answers I've found to my specific situation.
Here's a very basic overview of the problem:
class blank:
def __init__(self,row,col):
self.row = row
self.col = col
def __str__(self):
return '.'
class pawn:
def __init__(self,row,col):
self.row = row
self.col = col
def __str__(self):
return 'p'
def addpiece(brd,tr,tc):
nb = brd
nb[tr][tc] = pawn(tr,tc)
return nb
original_board = [[blank(rr,cc) for rr in range(8)] for cc in range(8)]
for row1 in original_board: print(' '.join([str(elem) for elem in row1]))
print('=====\n')
new_board = addpiece(original_board,1,1)
for row2 in original_board: print(' '.join([str(elem) for elem in row2]))
Each piece type (and a blank square) is a class; the board is a list of lists. If I want to see if a potential move will put the player in check, I made duplicate board (new_board), so the original_board variable is supposed to stay the same for the moment. I change one of the squares in the new_board (e.g., put a pawn in the middle), but the original_board variable changes anyway.
I know the problem has something to do with the variable ID following original_board into the function and assigning it to the dummy variable nb. I suppose I could change the original_board to a tuple of tuples and then change it back afterwards, but that doesn't seem very elegant. Any suggestions? Thanks.
The problem is nb = brd doesn’t create a copy, it just creates a new reference to the same object. You can try using deepcopy instead:
from copy import deepcopy
nb = deepcopy(brd)
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 3 years ago.
I am implementing a basic node object in python. Basically, I implemented a node class with the attribute f_pointers and set it to the default value []. When ever I try to change f_pointers of (lets say) node_a, I will end up changing f_pointers of node_b, which are programmed to be completely unrelated.
I have already solved the problem by instead changing the default value to None and setting up the forward_pointers in __init__. However, I would still like to know how to avoid this problem in the future and possibly learn something new about Python.
For the sake of simplicity, I removed some unnecessary parts of the code.
class Node:
def __init__(self, f_pointers = []):
self.f_pointers = f_pointers
def get_pointers(self):
return self.f_pointers
def add_pointers(self, new_pointer):
self.f_pointers.append(new_pointer)
a = Node()
b = Node()
print(a.get_pointers, b.get_pointers)
>>> [] []
a.add_pointers("a")
print(a.get_pointers, b.get_pointers)
>> ["a"] ["a"]
a.add_pointers("b")
print(a.get_pointers, b.get_pointers)
>> ["a","b"] ["a","b"]
As can be seen, a and b are completely unrelated objects (other than the fact that they are of the same type Node) but will affect each other. Why does this happen?
It's because you are referencing to the same list (the one instantiated in the __init__ default params list definition like __init__(self, f_pointers=[]). What happens is that when you say in the __init__ method code block that self.f_points = f_pointers you are basically referencing the same list every time you instantiate a new Node object.
The reasons are explained further here
What you do want to do instead is instantiate a new list for every init like:
def __init__(self, f_pointers=None):
self.f_pointers = []
You should do it like this.
class Node:
def __init__(self, f_pointers=None):
if f_pointers:
self.f_pointers = f_pointers
else:
self.f_pointers = []
def get_pointers(self):
return self.f_pointers
def add_pointers(self, new_pointer):
self.f_pointers.append(new_pointer)
a = Node()
b = Node()
print(a.get_pointers(), b.get_pointers())
a.add_pointers("a")
print(a.get_pointers(), b.get_pointers())
You get this kind of behavior because in your case a.f_pointers and b.f_pointers is the same list, which was generated, when you described your class Node.
So a.f_pointers is b.f_pointers == True in your case
This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 4 years ago.
I created the class Sorter which sets the variable self.list in the __init__ equal to a given argument. I then created a function selectionSort which should copy the value of self.list into a new variable unsortedList. That worked out, but when I then change unsortedList, the self.list variable changes as well.
Here's my code:
class Sorter:
def __init__(self, list):
self.list = list
def selectionSort(self):
unsortedList = self.list
sortedList = []
indexSmallest = 0
while len(unsortedList)>0:
for i in range(len(unsortedList)):
if unsortedList[i] <= unsortedList[indexSmallest]:
indexSmallest = i
sortedList.append(unsortedList[indexSmallest])
unsortedList.pop(indexSmallest)
indexSmallest = 0
return sortedList
sorter = Sorter([2,6,1,8,5])
print(sorter.selectionSort())
I expect self.list to be the same as before calling the selectionSort() function but the result I get is an empty self.list variable.
Use either:
#1
unsortedList = self.list.copy()
Or
#2
unsortedList = self.list[:]
Or
#3
import copy
unsortedList = copy.deepcopy(self.list)
Explanation:
When you do an assignment via =, it really is referring to the same list just that now that list has 2 different names.
To circumvent this, use #1 or #2 methods -> you would require the .copy() inbuilt function or using [:].
As for #3, this is used when shallow copying isn't enough because you might have mutable objects within the list itself.
For a greater understanding on copy vs deepcopy, visit and read here
What's happening is that when you set unsortedList = self.list, Python doesn't want to copy all the values over, because that could be expensive. Instead, it just makes both variables point to the same region of memory, so when you change one it changes the other.
To make a copy, you can do unsortedList = self.list[:]
EDIT see this thread for more information.
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
I have this simple class:
class revs:
def __init__(self, rev, us, accs = []):
self.rev = rev
self.us = us
self.accs = accs
And i have this piece of code to asign values to the list and is inside of a loop
rev, usu = cada_l.split("|")
acct = each_l[:2].strip()
list_acct.append(acct)
and last, i create a dict, to manage a list of revs like this:
drevs = {}
cada = revs(rev, us, list_acct)
drevs[cada.rev] = cada
And it Works correctly with rev and us, but with list_acct is ever updating all the instances:
drevs['1'].rev
'1'
drevs['2'].rev
'2'
drevs['1'].us
'user1'
drevs['2'].us
'user2'
drevs['1'].accs
'["Doc1","Doc2"]'
drevs['2'].accs
'["Doc1","Doc2"]'
And if i change list_acct.clear(), the values in all the instances is clear, I'm still fairly new to Python and this confuses me.
Thanks
This looks like it's happening because you're passing the same list to every object. As a result, all the objects maintain references to the same list, and since list is mutable, it appears to change "all" of them at once.
To fix this, either pass in a new empty list each time you create a revs object, or else clone the list you're passing in:
cada = revs(rev, us, list_acct[:])
Note that if list_acct contains mutable objects, you could still get into the same problem again, but one level deeper!
If you're not passing lists to the revs objects when you create them at all (I can't tell, since you're not showing your full code!), then you have the same problem, but for a different reason: in Python, default arguments are all evaluated once, at the time of the function definition. Therefore, you can get this behavior:
r1 = revs(1, 1)
r2 = revs(2, 2)
r1.accs.append("Hi!")
print(r1.accs) # prints ['Hi!']
print(r2.accs) # prints ['Hi!']
Because the default argument for the revs constructor is always pointing to the same list. See this question for an explanation as to why, but to get around it, just use None as your default instead of [].
class revs:
def __init__(self, rev, us, accs=None):
self.rev = rev
self.us = us
if accs is None:
accs = []
self.accs = accs
I'm trying to create a Python script that opens several databases and compares their contents. In the process of creating that script, I've run into a problem in creating a list whose contents are objects that I've created.
I've simplified the program to its bare bones for this posting. First I create a new class, create a new instance of it, assign it an attribute and then write it to a list. Then I assign a new value to the instance and again write it to a list... and again and again...
Problem is, it's always the same object so I'm really just changing the base object. When I read the list, I get a repeat of the same object over and over.
So how do you write objects to a list within a loop?
Here's my simplified code
class SimpleClass(object):
pass
x = SimpleClass
# Then create an empty list
simpleList = []
#Then loop through from 0 to 3 adding an attribute to the instance 'x' of SimpleClass
for count in range(0,4):
# each iteration creates a slightly different attribute value, and then prints it to
# prove that step is working
# but the problem is, I'm always updating a reference to 'x' and what I want to add to
# simplelist is a new instance of x that contains the updated attribute
x.attr1= '*Bob* '* count
print "Loop Count: %s Attribute Value %s" % (count, x.attr1)
simpleList.append(x)
print '-'*20
# And here I print out each instance of the object stored in the list 'simpleList'
# and the problem surfaces. Every element of 'simpleList' contains the same attribute value
y = SimpleClass
print "Reading the attributes from the objects in the list"
for count in range(0,4):
y = simpleList[count]
print y.attr1
So how do I (append, extend, copy or whatever) the elements of simpleList so that each entry contains a different instance of the object instead of all pointing to the same one?
You demonstrate a fundamental misunderstanding.
You never created an instance of SimpleClass at all, because you didn't call it.
for count in xrange(4):
x = SimpleClass()
x.attr = count
simplelist.append(x)
Or, if you let the class take parameters, instead, you can use a list comprehension.
simplelist = [SimpleClass(count) for count in xrange(4)]
A list comprehension can be used to fill a list with separate instances of a class, like so:
instancelist = [MyClass() for i in range(29)]
This avoids the problem with multiplying a list of one element with *, which re-uses the same object.
It shouldn't be necessary to recreate the SimpleClass object each time, as some are suggesting, if you're simply using it to output data based on its attributes. However, you're not actually creating an instance of the class; you're simply creating a reference to the class object itself. Therefore, you're adding a reference to the same class attribute to the list (instead of instance attribute), over and over.
Instead of:
x = SimpleClass
you need:
x = SimpleClass()
Create a new instance each time, where each new instance has the correct state, rather than continually modifying the state of the same instance.
Alternately, store an explicitly-made copy of the object (using the hint at this page) at each step, rather than the original.
If I understand correctly your question, you ask a way to execute a deep copy of an object.
What about using copy.deepcopy?
import copy
x = SimpleClass()
for count in range(0,4):
y = copy.deepcopy(x)
(...)
y.attr1= '*Bob* '* count
A deepcopy is a recursive copy of the entire object. For more reference, you can have a look at the python documentation: https://docs.python.org/2/library/copy.html
I think this simply demonstrates what you are trying to achieve:
# coding: utf-8
class Class():
count = 0
names = []
def __init__(self,name):
self.number = Class.count
self.name = name
Class.count += 1
Class.names.append(name)
l=[]
l.append(Class("uno"))
l.append(Class("duo"))
print l
print l[0].number, l[0].name
print l[1].number, l[1].name
print Class.count, Class.names
Run the code above and you get:-
[<__main__.Class instance at 0x6311b2c>,
<__main__.Class instance at 0x63117ec>]
0 uno
1 duo
2 ['uno', 'duo']