Присвоение другой переменной строке приводит к копированию или увеличению количества ссылок

На стр .35 "Основы Python Essential Reference" Дэвида Бизли он сначала заявляет:

Для неизменяемых данных, таких как строки, интерпретатор агрессивно разделяет объекты между различными частями программы.

Однако позже на той же странице он заявляет

Для неизменяемых объектов, таких как числа и строки, это назначение эффективно создает копию.

Но разве это не противоречие? С одной стороны, он говорит, что они разделены, но затем он говорит, что они скопированы.

Ответ 1

Назначение в python никогда не создает копию (это технически возможно, только если назначение для члена класса переопределено, например, с помощью __setattr__, свойств или дескрипторов).

Итак, после

a = foo()
b = a

все, что было возвращено из foo, не было скопировано, и вместо этого у вас есть две переменные a и b, указывающие на один и тот же объект. Независимо от того, является ли объект неизменным или нет.

С неизменяемыми объектами, однако трудно сказать, так ли это (потому что вы не можете мутировать объект с помощью одной переменной и проверить, видно ли изменение с помощью другого), поэтому вы можете думать, что действительно a и b не могут влиять друг на друга.

Для некоторых неизменяемых объектов Python может повторно использовать старые объекты вместо создания новых и после

a = x + y
b = x + y

где оба x и y являются числами (поэтому сумма является числом и является неизменной) может заключаться в том, что оба a и b будут указывать на один и тот же объект. Обратите внимание, что такой гарантии нет... может быть также, что вместо этого они будут указывать на разные объекты с одинаковым значением.

Важно помнить, что Python никогда не делает копию, если специально не указано, что нужно использовать, например, copy или deepcopy. Это важно с изменяемыми объектами, чтобы избежать сюрпризов.

Например, вы можете увидеть одну общую идиому:

class Polygon:
    def __init__(self, pts):
        self.pts = pts[:]
    ...

В этом случае вместо self.pts = pts используется self.pts = pts[:], чтобы сделать копию всего массива точек, чтобы убедиться, что список точек не изменится неожиданно, если после создания изменений объекта применяются к списку, который был передается конструктору.

Ответ 2

Он эффективно создает копию. На самом деле это не создает копию. Основное различие между наличием двух копий и наличием двух имен одинакового значения состоит в том, что в последнем случае модификации с помощью одного имени влияют на значение другого имени. Если значение не может быть мутировано, это отличие исчезает, поэтому для неизменяемых объектов мало практических последствий для копирования или отсутствия значения.

Есть некоторые угловые случаи, где вы можете указать разницу между копиями и разными объектами даже для неизменяемых типов (например, с помощью функции id или оператора is), но это не полезно для встроенного встроенного Python типы (например, строки и числа).