@andrey_ado

Передача списка в объект класса по ссылке, как сделать?

Передаю в качестве параметра в объект класса список. Ожидалось, что он будет меняться в объекте, но этого не происходит.
Получается список передается по значению? Код весь не буду вставлять.
Начало:
my_object = m_table(root,
                                 mheader,
                                 item_list                                 
                                 )

m_table- таблица на Tkiner, в которую предается список item_list. в обекте ссылка на список отдается self.item_list.
def __init__(self, parent=None,
                 header_list,
                 item_list
                 ):
self.header_list = header_list
self.item_list = item_list

В таблице список изменяется, окно закрывается.
root.mainloop()
print(item_list)

Список каким был, таким и остался
  • Вопрос задан
  • 722 просмотра
Решения вопроса 1
@throughtheether
human after all
Код покажите, скорее всего вы что-то неправильно делаете.
Вот пример.
>>> def f1(l):
	l=l+[0]
	
>>> def f2(l):
	l+=[0]
	
>>> def f3(l):
	l.append(0)

>>> mylist=[1,2,3]
>>> f1(mylist)
>>> mylist
[1, 2, 3]
>>> f2(mylist)
>>> mylist
[1, 2, 3, 0]
>>> f3(mylist)
>>> mylist
[1, 2, 3, 0, 0]

В первой функции выражение l=l+[0] создает локальную переменную l, которая ссылается на некий новый список, состоящий из элементов списка, переданного в функцию, с добавленным элементом (0). То есть l слева и l справа - это разные переменные.

Во второй функции происходит (неявно) вызов метода объекта-списка, переданного в функцию, поэтому список в результате ("снаружи") изменяется. В третьей функции происходит явный вызов метода объекта, переданного в функцию.

Хотел бы отметить, специалистом по CS (в части теории языков программирования) не являюсь, мог напутать с терминологией.

Есть на эту тему хорошая статья.

UPD:
Первый пример подробнее:
>>> def f1(l):
	print "Value at function start: {}, id: {}".format(l,id(l))
	l=l+[0]
	print "Value at function end: {}, id: {}".format(l,id(l))

	
>>> mylist=[1,2,3]
>>> f1(mylist)
Value at function start: [1, 2, 3], id: 44215272
Value at function end: [1, 2, 3, 0], id: 5440176
>>> mylist
[1, 2, 3]

Видно по разным id, что происходит обращение к разным переменным.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@bromzh
Drugs-driven development
Автор, ты несёшь бред. Ты прочитал статью, но ничего из неё не понял. Вот тебе доки, просвящайся.
Давай ещё на русском продублирую, раз с английским туго
1) Когда ты присваиваешь одну переменную другой, на самом деле происходит лишь копирование ссылок на объект. В итоге, 2 переменные ссылаются на 1 объект. Вот тебе простейшее доказательство, если ты мне не веришь. Пожалуйста, вбей этот текст в интерпретатор питона и убедись сам:
class A(object):
    def __init__(self, item):
        self.field = item

c = [1, 2]
a = A(c)
id(a.field) == id(c) # => True. Где тут разные id, покажи? 
c[0] = 100
a.field # => [100, 2] Копия создаётся, говоришь? Как ты тогда объяснишь, что изменяли мы c, а a.field вдруг тоже изменилось?
a.field[1] = 200
c #=> [100, 200] Ты всё ещё уверен, что это копия?


Автор в статье как раз и пытается донести мысль, что переменная - это не сам объект (объект хранится в памяти), а только лишь его адрес. И когда ты пишешь знак равенства, на деле переменной слева всего лишь присваивается тот адрес, на который указывает переменная справа. После присваивания 2 переменные стали ссылаться на 1 объект в памяти.
Если этот объект неизменяемый (строка, кортеж, число, и т.д.), то ты никак его изменить не сможешь. И когда ты переменной, указывающей на этот объект присваиваешь новое значение, на деле, переменная просто начинает указывать на другой объект, а тот который был до этого не меняется. Даже когда пишешь i += 1, i будет указывать уже на другой объект
Если же переменная указывает на мутабельный объект (экземпляр класса, список, словарь и т.д.), то над ней можно совершить некие действия, который изменят состояние объекта. И это изменённое состояние и будут отображать другие переменные, которые ссылаются на этот объект. Таким образом, можно изменить 1 переменную, а изменения прочитать из другой.

2) В питоне нет понятия "объявить переменную". Сам факт присваивания переменной чего-нибудь либо затирает старую ссылку (но не объект!) на объект, либо создаёт новую связь.
Так вот, есть такое понятие, как область видимости. Если ты создашь новую переменную внутри функции, извне она будет не видна. А вот если ты возьмёшь в качестве имени внутренней переменной уже использовавшееся ранее, то эта внутренняя переменная затенит внешнюю. Внутри функции не будет доступа к внешней переменной с тем же именем, вместо неё будет внутренняя.

Так вот автор (и доки) так и говорят: если ты внутри функции попытаешься присвоить переменной с именем одного из аргумента какое-то новое значение, то это значение будет локальным внутри данной функции, а извне её значение переменной, которую передали в качестве аргумента будет старым. Более того, даже если ты сперва присвоил внутренней переменной внешнюю, но потом ещё раз присвоишь что-то другой, то во внутренней будет другая ссылка. Поясняю:
def foo(a):
    print(id(a))
    a = [1] # Теперь a затеняет внешнюю переменную, т.е. указывает на другой объект
    print(id(a))

b = [0, 1]
foo(b) # Id будут разные, хотя имя одно, но после присвоения переменная потеряла связь с внешней


И вот пока ты внутренней переменной не присвоил что-то новое, изменяя её ты изменишь и внешнюю переменную.
И после "смерти" объекта ничего не изменится, как ты писал. Объект удаляется из памяти не сразу, это делает сборщик мусора, и только в том случае, если ссылок на объект в программе нет. Но тогда ты и не сможешь своё значение получить.

И список - это такой же мутабельный тип, как и произвольный класс, так что "передача по ссылке" для него работает (в доках об этом русским по белому написано, можешь почитать), так что создавать отдельный класс было глупо, в проблеме ты всё равно не разобрался.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы