Вы путаете объекты с именами . Все Объекты Python живут в куче, а имена - это просто ссылки на эти объекты. Список никогда не копируется, Python не обязан.
Вы можете думать об именах как о метках на фрагменте строки, и вы привязываете эти метки к объектам. Вы можете использовать столько меток, сколько хотите для ссылки на данный объект. Локальные имена в функции - это просто больше меток, и это метки , которые очищаются при выходе из функции. Объекты, с другой стороны, будут продолжать существовать до тех пор, пока к ним больше не будут прикреплены метки.
Для целей этой аналогии индексы списка - это просто больше меток, а ключи в словаре действуют как меткидля соответствующих значений. Фактически глобальные переменные модуля на самом деле являются просто парами ключ-значение в словаре!
Итак, в вашем конкретном примере вы создали объект списка, который дважды ссылается на None
:
[None] * 2
На данный момент к этому списку не прикреплены ярлыки, он только создан. Единственная причина, по которой он не загорается сразу, заключается в том, что стек исполнения Python ссылается на него. Следующая инструкция, которую затем выполняет Python, состоит в том, чтобы взять верх стека выполнения и назначить его локальному, потому что остальная часть этого оператора:
curr_row = ...
Таким образом, объект списка выше теперь имеет метку, прикрепленную кэто, локальный curr_now
.
Затем вы создаете еще несколько значений и присоединяете их к индексам в списке:
curr_row[0] = curr_value
curr_row[1] = curr_value * 2
Итак, с учетом curr_value
из 1
вытеперь получим следующее:
+---------+ +---------+ +---------+
| Globals | | | Heap | | | Locals |
+---------+ | +---------+ | +---------+
| |
| |
g_list ------------> {} |
| |
| [1, 2] <----------- curr_row
| |
(приведенная выше диаграмма несколько упрощает ситуацию, так как целые числа 1
и 2
тоже действительно отдельные объекты, но из-за этого приведенная выше диаграмма не читается).
Затем вы добавляете ключ к g_list
с новым пустым списком: row_key
is 1
здесь:
if row_key not in g_list:
g_list[row_key] = curr_row
Это создает ссылку между словарем и новымпустой объект списка:
+---------+ +---------+ +---------+
| Globals | | | Heap | | | Locals |
+---------+ | +---------+ | +---------+
| |
| |
g_list -------------> {1: <value>} |
| | |
| | |
| v |
| [] |
| |
| |
| |
| [1, 2] <------------- curr_row
| |
Наконец, вы изменяете этот пустой список, добавляя в него новый индекс, потому что добавляете к нему список, на который ссылается curr_row
:
g_list[row_key].append(curr_row)
который меняет ситуацию на:
+---------+ +---------+ +---------+
| Globals | | | Heap | | | Locals |
+---------+ | +---------+ | +---------+
| |
| |
g_list -------------> {1: <value>} |
| | |
| | |
| v |
| [<0>] |
| | |
| | |
| v |
| [1, 2] <------------- curr_row
| |
| |
Обратите внимание, как диктуетonary value ссылается на список, который в свою очередь ссылается на список с 1
и 2
в нем. Следующая строка изменяет последний список, присваивая индексу 1:
curr_row[1] = 100
, изменяя ситуацию следующим образом:
+---------+ +---------+ +---------+
| Globals | | | Heap | | | Locals |
+---------+ | +---------+ | +---------+
| |
| |
g_list -------------> {1: <value>} |
| | |
| | |
| v |
| [<0>] |
| | |
| | |
| v |
| [1, 100]<------------- curr_row
| |
| |
Наконец, функция завершается, удаляя все локальные имена:
+---------+ +---------+
| Globals | | | Heap |
+---------+ | +---------+
|
|
g_list -------------> {1: <value>}
| |
| |
| v
| [<0>]
| |
| |
| v
| [1, 100]
|
|
Список, который вы создали внутри функции и на который ссылается как curr_row
, не удаляется , поскольку на него все еще есть ссылка из индекса 0
списка, который g_list
словарь ссылается как значение.
Нигде Python не должен делать копию списка (глубокую или нет) .
В качестве примечания: Вы неВ ваших функциях нужно использовать операторы global g_list
. Это утверждение необходимо только в том случае, если вы хотите изменить то, с чем связано глобальное имя (метка). Вы устанавливаете g_list
только один раз , привязывая эту метку к объекту словаря. С этого момента этот ярлык остается связанным с тем же словарем. Вам нужно использовать global <name>
, только если вам нужно повторно привязать такое имя из функции, и только потому, что в противном случае Python обрабатывает имена, которые вы назначаете, как локальные. Поскольку в ваших функциях не делается попыток изменить то, на что ссылается g_list
, вы можете безопасно удалить операторы global g_list
из своего кода, не изменяя поведения кода.