Автор, ты несёшь бред. Ты прочитал статью, но ничего из неё не понял. Вот тебе
доки, просвящайся.
Давай ещё на русском продублирую, раз с английским туго
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 будут разные, хотя имя одно, но после присвоения переменная потеряла связь с внешней
И вот пока ты внутренней переменной не присвоил что-то новое, изменяя её ты изменишь и внешнюю переменную.
И после "смерти" объекта ничего не изменится, как ты писал. Объект удаляется из памяти не сразу, это делает сборщик мусора, и только в том случае, если ссылок на объект в программе нет. Но тогда ты и не сможешь своё значение получить.
И список - это такой же мутабельный тип, как и произвольный класс, так что "передача по ссылке" для него работает (в доках об этом русским по белому написано, можешь почитать), так что создавать отдельный класс было глупо, в проблеме ты всё равно не разобрался.