0 Изменчивый против неизменного

В Python есть два типа типов. Неизменяемые типы и изменяемые типы.

Immutables

Объект неизменяемого типа не может быть изменен. Любая попытка изменить объект приведет к созданию копии.

Эта категория включает в себя: целые числа, числа с плавающей запятой, сложные, строки, байты, кортежи, диапазоны и фрозенцеты.

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

 >>> a = 1
>>> id(a)
140128142243264
>>> a += 2
>>> a
3
>>> id(a)
140128142243328

 

Хорошо, 1 не 3 ... Последние новости ... Может быть, нет. Однако об этом поведении часто забывают, когда речь идет о более сложных типах, особенно строках.

 >>> stack = "Overflow"
>>> stack
'Overflow'
>>> id(stack)
140128123955504
>>> stack += " rocks!"
>>> stack
'Overflow rocks!'

 

Ага! Увидеть? Мы можем изменить это!

 >>> id(stack)
140128123911472

 

Нет . Хотя, кажется , мы можем изменить строку с именем переменной stack , что мы на самом деле делаем, создает новый объект , чтобы содержать результат конкатенации. Мы одурачены, потому что при этом старый объект никуда не уходит, поэтому он уничтожается. В другой ситуации это было бы более очевидно:

 >>> stack = "Stack"
>>> stackoverflow = stack + "Overflow"
>>> id(stack)
140128069348184
>>> id(stackoverflow)
140128123911480

 

В этом случае ясно, что если мы хотим сохранить первую строку, нам нужна копия. Но так ли это очевидно для других типов?

Упражнение

Теперь, зная, как работают неизменяемые типы, что бы вы сказали с приведенным ниже фрагментом кода? Это мудро?

 s = ""
for i in range(1, 1000):
    s += str(i)
    s += ","


 

Mutables

Объект изменяемом типа может быть изменен, и он изменяется на месте. Неявные копии не делаются.

В эту категорию входят: списки, словари, байтовые массивы и наборы.

Давайте продолжать играть с нашим маленьким id функции.

 >>> b = bytearray(b'Stack')
>>> b
bytearray(b'Stack')
>>> b = bytearray(b'Stack')
>>> id(b)
140128030688288
>>> b += b'Overflow'
>>> b
bytearray(b'StackOverflow')
>>> id(b)
140128030688288

 

(В качестве примечания я использую байты, содержащие данные ascii, чтобы прояснить мою точку зрения, но помните, что байты не предназначены для хранения текстовых данных. Пусть сила извинит меня.)

Что у нас есть? Мы создаем ByteArray, изменить его и используя id , мы можем гарантировать , что это тот же объект, модифицирована. Не копия этого.

Конечно, если объект будет часто изменяться, изменяемый тип делает работу намного лучше, чем неизменяемый. К сожалению, реальность этого свойства часто забывают, когда он причиняет боль больше всего.

 >>> c = b
>>> c += b' rocks!'
>>> c
bytearray(b'StackOverflow rocks!')

 

Хорошо...

 >>> b
bytearray(b'StackOverflow rocks!')

 

Подожди секунду ...

 >>> id(c) == id(b)
True

 

В самом деле. c не копия b . c является b .

Упражнение

Теперь вы лучше понимаете, какой побочный эффект подразумевает изменчивый тип. Можете ли вы объяснить, что происходит в этом примере?

 >>> ll = [ [] ]*4 # Create a list of 4 lists to contain our results
>>> ll
[[], [], [], []]
>>> ll[0].append(23) # Add result 23 to first list
>>> ll
[[23], [23], [23], [23]]
>>> # Oops...