В python 3.{2-7} странно клонируются сложносочиненные субъекты классов
Итак мы клонируем субъекты класса smthng в список instlst, после чего видим, что список глубины 1 lst1 склонировался как надо в то время как список большей глубины lst2 перезаписывает себя.
При сравнении lst2 из разных субъектов в instlst через is всегда дает False , но lst2 при этом меняется вместе с "предшественниками", хотя по моей логике так быть не должно.
Сердечно прошу объяснить мне как работает эта техномагия, является ли багом, и как мне клонировать списки глубиной 3 внутри класса, так чтоб они сохранялись
P.S deepcopy и @classmethod пробовал, не помогают
class smthng:
def __init__(self, ):
self.lst1=['a ' for a in range(0,5)]
self.lst2=[['a ' for a in range(0,5)],['appendix']]#,['a ' for a in range(0,2)]]
self.mn=5
self.junk='SomeRandomJunk'
def cln(self,scnd):
self.lst1=[]
self.lst1=self.lst1+scnd.lst1
self.lst2=[]
self.lst2=self.lst2+scnd.lst2
self.mn=scnd.mn
self.junk=''
self.junk=self.junk+scnd.junk
#same for other junk
mninst=smthng()
instlst=[]
for i in range(0,3):
for u in range(0,3):
instlst=instlst+[smthng()]
instlst[len(instlst)-1].cln(mninst)
instlst[len(instlst)-1].lst1[1]=i
instlst[len(instlst)-1].lst1[2]=u
instlst[len(instlst)-1].lst2[0][1]=i
instlst[len(instlst)-1].lst2[0][2]=u
print('depth 1 all goes well')
for u in range(0,len(instlst)):
print('instlst main is',instlst[u].lst1,'i=',i)
print('depth 2 previous instances get overwritten')
for u in range(0,len(instlst)):
print('instlst main is',instlst[u].lst2,'i=',i)
#mninst.cln1(instlst[len(instlst)-1])
а вот и вывод на Python 3.7 смотрите сами #Same code, different result
depth 1 all goes well
instlst main is ['a ', 0, 0, 'a ', 'a '] i= 0
instlst main is ['a ', 0, 1, 'a ', 'a '] i= 0
instlst main is ['a ', 0, 2, 'a ', 'a '] i= 0
depth 2 previous instances get overwritten
instlst main is [['a ', 0, 2, 'a ', 'a '], ['appendix']] i= 0
instlst main is [['a ', 0, 2, 'a ', 'a '], ['appendix']] i= 0
instlst main is [['a ', 0, 2, 'a ', 'a '], ['appendix']] i= 0
depth 1 all goes well
instlst main is ['a ', 0, 0, 'a ', 'a '] i= 1
instlst main is ['a ', 0, 1, 'a ', 'a '] i= 1
instlst main is ['a ', 0, 2, 'a ', 'a '] i= 1
instlst main is ['a ', 1, 0, 'a ', 'a '] i= 1
instlst main is ['a ', 1, 1, 'a ', 'a '] i= 1
instlst main is ['a ', 1, 2, 'a ', 'a '] i= 1
depth 2 previous instances get overwritten
instlst main is [['a ', 1, 2, 'a ', 'a '], ['appendix']] i= 1
instlst main is [['a ', 1, 2, 'a ', 'a '], ['appendix']] i= 1
instlst main is [['a ', 1, 2, 'a ', 'a '], ['appendix']] i= 1
instlst main is [['a ', 1, 2, 'a ', 'a '], ['appendix']] i= 1
instlst main is [['a ', 1, 2, 'a ', 'a '], ['appendix']] i= 1
instlst main is [['a ', 1, 2, 'a ', 'a '], ['appendix']] i= 1
depth 1 all goes well
instlst main is ['a ', 0, 0, 'a ', 'a '] i= 2
instlst main is ['a ', 0, 1, 'a ', 'a '] i= 2
instlst main is ['a ', 0, 2, 'a ', 'a '] i= 2
instlst main is ['a ', 1, 0, 'a ', 'a '] i= 2
instlst main is ['a ', 1, 1, 'a ', 'a '] i= 2
instlst main is ['a ', 1, 2, 'a ', 'a '] i= 2
instlst main is ['a ', 2, 0, 'a ', 'a '] i= 2
instlst main is ['a ', 2, 1, 'a ', 'a '] i= 2
instlst main is ['a ', 2, 2, 'a ', 'a '] i= 2
depth 2 previous instances get overwritten
instlst main is [['a ', 2, 2, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 2, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 2, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 2, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 2, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 2, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 2, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 2, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 2, 'a ', 'a '], ['appendix']] i= 2
Update В принципе решением данной проблемы является код написанный ниже, но все еще не понимаю почему при копировании списка глубины 2 способом для глубины 1 при сравнении instlst[a].lst2[b] is instlst[c].lst2[b] при a,c >0 and a,c <len(instlst)-1 and a!=c выдавало False хотя по факту это были ссылки на 1 и тот объект, что меня и запутало.
И еще мне любопытно существует ли реализация этой функции на питоне для произвольной глубины и можно ли как-то менять глубину клонирования в deepcopy потому что у меня оно клонировало правильно лишь до глубины 1 внутри класса на Python 3.{2-7}
P.s ответ на комментарий append вместо сложения списков породит лишние квадратные скобки ; += не использовал во избежании появления еще каких-нибудь глитчей
#глубина 1
self.lst1=[]
self.lst1=self.lst1+scnd.lst1
#глубина 2
for i in range(0,len(self.lst2)):
self.lst2[i]=[]
self.lst2[i]=self.lst2[i]+scnd.lst2[i]
#глубина 3
for i in range(0,len(self.lst3)):
for u in range(0,len(self.lst3[i])):
self.lst3[i][u]=[]
self.lst3[i][u]=self.lst3[i][u]+scnd.lst3[i][u]
Ответы (1 шт):
У вас не происходит копирования списка.
Вы создаете список, складывайте его с другим списком, в результате создается новый список с элементами первого и второго списков.
Заметьте, тут не будет копирования, просто элементы одного списка будут перенесены в другой. А для изменяемых объектов (таких как списки) это означает, что объект при таком раскладе будет в обоих списках одновременно, т.к. там нет копирования объекта, там будет копия ссылки на объект.
Суть проблемы:
self.lst1=[]
self.lst1=self.lst1+scnd.lst1
self.lst2=[]
self.lst2=self.lst2+scnd.lst2
Наглядный пример:
l2 = [['FOO']]
print(id(l2[0]), l2[0])
# 2421659886216 ['FOO']
l1 = []
l1 = l1 + l2
print(id(l1[0]), l1[0])
# 2421659886216 ['FOO']
Как тут видно, хоть объект ['FOO'] попал в другой список, он остался тем же объектом. Это видно по его id. И если вы, например, в l1 поменяете список ['FOO'], то он поменяется в l2, т.к. там тоже ссылка на объект.
l1[0].append('BAR')
print(l1, l2)
# [['FOO', 'BAR']] [['FOO', 'BAR']]
print(l1[0] is l2[0])
# True
Решением будет сделать копию списков
- Используйте модуль
copy, у него есть отличный методdeepcopy - Для добавления в список используйте
append
Пример:
import copy
class smthng:
def __init__(self, ):
self.lst1 = ['a ' for _ in range(5)]
self.lst2 = [
['a ' for _ in range(5)],
['appendix']
]
self.mn = 5
self.junk = 'SomeRandomJunk'
def cln(self, scnd):
self.lst1 = copy.deepcopy(scnd.lst1)
self.lst2 = copy.deepcopy(scnd.lst2)
self.mn = scnd.mn
self.junk = scnd.junk
mninst = smthng()
instlst = []
for i in range(3):
for u in range(3):
obj = smthng()
obj.cln(mninst)
obj.lst1[1] = i
obj.lst1[2] = u
obj.lst2[0][1] = i
obj.lst2[0][2] = u
instlst.append(obj)
print('depth 1 all goes well')
for x in instlst:
print('instlst main is', x.lst1, 'i=', i)
print('depth 2 previous instances get overwritten')
for x in instlst:
print('instlst main is', x.lst2, 'i=', i)
print('\n' + '-' * 100 + '\n')
Результат:
depth 1 all goes well
instlst main is ['a ', 0, 0, 'a ', 'a '] i= 0
instlst main is ['a ', 0, 1, 'a ', 'a '] i= 0
instlst main is ['a ', 0, 2, 'a ', 'a '] i= 0
depth 2 previous instances get overwritten
instlst main is [['a ', 0, 0, 'a ', 'a '], ['appendix']] i= 0
instlst main is [['a ', 0, 1, 'a ', 'a '], ['appendix']] i= 0
instlst main is [['a ', 0, 2, 'a ', 'a '], ['appendix']] i= 0
----------------------------------------------------------------------------------------------------
depth 1 all goes well
instlst main is ['a ', 0, 0, 'a ', 'a '] i= 1
instlst main is ['a ', 0, 1, 'a ', 'a '] i= 1
instlst main is ['a ', 0, 2, 'a ', 'a '] i= 1
instlst main is ['a ', 1, 0, 'a ', 'a '] i= 1
instlst main is ['a ', 1, 1, 'a ', 'a '] i= 1
instlst main is ['a ', 1, 2, 'a ', 'a '] i= 1
depth 2 previous instances get overwritten
instlst main is [['a ', 0, 0, 'a ', 'a '], ['appendix']] i= 1
instlst main is [['a ', 0, 1, 'a ', 'a '], ['appendix']] i= 1
instlst main is [['a ', 0, 2, 'a ', 'a '], ['appendix']] i= 1
instlst main is [['a ', 1, 0, 'a ', 'a '], ['appendix']] i= 1
instlst main is [['a ', 1, 1, 'a ', 'a '], ['appendix']] i= 1
instlst main is [['a ', 1, 2, 'a ', 'a '], ['appendix']] i= 1
----------------------------------------------------------------------------------------------------
depth 1 all goes well
instlst main is ['a ', 0, 0, 'a ', 'a '] i= 2
instlst main is ['a ', 0, 1, 'a ', 'a '] i= 2
instlst main is ['a ', 0, 2, 'a ', 'a '] i= 2
instlst main is ['a ', 1, 0, 'a ', 'a '] i= 2
instlst main is ['a ', 1, 1, 'a ', 'a '] i= 2
instlst main is ['a ', 1, 2, 'a ', 'a '] i= 2
instlst main is ['a ', 2, 0, 'a ', 'a '] i= 2
instlst main is ['a ', 2, 1, 'a ', 'a '] i= 2
instlst main is ['a ', 2, 2, 'a ', 'a '] i= 2
depth 2 previous instances get overwritten
instlst main is [['a ', 0, 0, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 0, 1, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 0, 2, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 1, 0, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 1, 1, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 1, 2, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 0, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 1, 'a ', 'a '], ['appendix']] i= 2
instlst main is [['a ', 2, 2, 'a ', 'a '], ['appendix']] i= 2
UPD. deepcopy копирует и объекты, и поля в копируемых объектах, даже если те объекты сами находятся в списках:
import copy
class Foo:
def __init__(self):
self.a = 1
self.b = 2
self.c = [1, 2, 3, [4, 5, 6]]
def __repr__(self):
return f'Foo[{self.a}, {self.b}, {self.c}, {id(self.c)}, {id(self.c[-1])}]'
foo = Foo()
print(foo)
# Foo[1, 2, [1, 2, 3, [4, 5, 6]], 2432577922504, 2432577922568]
foo2 = copy.deepcopy(foo)
print(foo2)
# Foo[1, 2, [1, 2, 3, [4, 5, 6]], 2432577922376, 2432577922312]
print()
items = [foo, foo, foo]
print(items)
# [Foo[1, 2, [1, 2, 3, [4, 5, 6]], 2432577922504, 2432577922568], Foo[1, 2, [1, 2, 3, [4, 5, 6]], 2432577922504, 2432577922568], Foo[1, 2, [1, 2, 3, [4, 5, 6]], 2432577922504, 2432577922568]]
items2 = copy.deepcopy(items)
print(items2)
# [Foo[1, 2, [1, 2, 3, [4, 5, 6]], 2432577922120, 2432577922056], Foo[1, 2, [1, 2, 3, [4, 5, 6]], 2432577922120, 2432577922056], Foo[1, 2, [1, 2, 3, [4, 5, 6]], 2432577922120, 2432577922056]]
Но есть интересная вещь, в списке, имеющий один объект в нескольких элементах (items = [foo, foo, foo]) в копии списка будет тоже одна копия объекта, вместо ожидаемых нескольких копий от того объекта.
Это не проблема, если там будут разные объекты, для проверки сделаем разные объекты через копии:
items = [copy.deepcopy(foo), copy.deepcopy(foo), copy.deepcopy(foo)]