Неизменяемые против Mutable типов

Я смущен тем, что такое неизменный тип. Я знаю, что объект float считается неизменным, с этим типом примера из моей книги:

class RoundFloat(float):
    def __new__(cls, val):
        return float.__new__(cls, round(val, 2))

Является ли это неизменным из-за структуры/иерархии классов?, значение float находится в верхней части класса и является его собственным вызовом метода. Подобно этому типу примера (хотя моя книга говорит, что dict изменчива):

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

В то время как что-то mutable имеет методы внутри класса, с этим типом примера:

class SortedKeyDict_a(dict):
    def example(self):
        return self.keys()

Кроме того, для последнего class(SortedKeyDict_a), если я передаю ему этот тип набора:

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

без вызова метода example, он возвращает словарь. SortedKeyDict с __new__ обозначает его как ошибку. Я попытался передать целые числа в класс RoundFloat с __new__ и не указал ошибок.

Ответ 1

Что? Поплавки неизменны? Но я не могу сделать

x = 5.0
x += 7.0
print x # 12.0

Разве этот "мут" x?

Хорошо, вы согласны, что строки неизменны правильно? Но вы можете сделать то же самое.

s = 'foo'
s += 'bar'
print s # foobar

Значение переменной изменяется, но изменяется, изменяя значение переменной. Изменчивый тип может измениться таким образом, и он также может изменить "на месте".

Вот разница.

x = something # immutable type
print x
func(x)
print x # prints the same thing

x = something # mutable type
print x
func(x)
print x # might print something different

x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing

x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different

Конкретные примеры

x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo

x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]

def func(val):
    val += 'bar'

x = 'foo'
print x # foo
func(x)
print x # foo

def func(val):
    val += [3, 2, 1]

x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]

Ответ 2

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

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

>>> s = "abc"
>>>id(s)
4702124
>>> s[0] 
'a'
>>> s[0] = "o"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = "xyz"
>>>id(s)
4800100
>>> s += "uvw"
>>>id(s)
4800500

Вы можете сделать это со списком, и он не изменит идентификатор объектов

>>> i = [1,2,3]
>>>id(i)
2146718700
>>> i[0] 
1
>>> i[0] = 7
>>> id(i)
2146718700

Чтобы узнать больше о модели данных Python, вы можете посмотреть ссылку на язык Python:

Ответ 3

Обычный неизменяемый тип:

  • numbers: int(), float(), complex()
  • неизменяемые последовательности: str(), tuple(), frozenset(), bytes()

Обычный изменяемый тип (почти все остальное):

  • изменяемые последовательности: list(), bytearray()
  • тип набора: set()
  • тип отображения: dict()
  • классы, экземпляры классов
  • и др.

Один трюк, чтобы быстро проверить, является ли тип изменчивым или нет, заключается в использовании встроенной функции id().

Примеры, использующие целое число,

>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)

используя список,

>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)

Ответ 4

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

int и float являются неизменяемыми. Если я делаю

a = 1
a += 5

Он указывает имя a на a 1 где-то в памяти в первой строке. Во второй строке он выглядит так: 1, добавляет 5, получает 6, затем указывает a на то, что 6 в памяти - он не менял 1 на a 6 в любом случае. Такая же логика применима к следующим примерам, используя другие неизменные типы:

b = 'some string'
b += 'some other string'
c = ('some', 'tuple')
c += ('some', 'other', 'tuple')

Для изменяемых типов я могу делать то, что в действительности изменяет значение, которое оно хранит в памяти. С:

d = [1, 2, 3]

Я создал список местоположений 1, 2 и 3 в памяти. Если я тогда сделаю

e = d

Я просто указываю e на те же list d точки в. Тогда я могу:

e += [4, 5]

И список, в котором будут отображаться как теги e, так и d, будет обновлен, чтобы также иметь места в 4 и 5 в памяти.

Если я вернусь к неизменяемому типу и сделаю это с помощью tuple:

f = (1, 2, 3)
g = f
g += (4, 5)

Затем f по-прежнему указывает только на исходный tuple - вы указали g на совершенно новый tuple.

Теперь, с вашим примером

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Где вы проходите

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

(который является tuple of tuples) как val, вы получаете сообщение об ошибке, потому что tuple не имеет метода .clear() - вам нужно пройти dict(d) как val, чтобы он работал, и в этом случае вы получите пустой SortedKeyDict.

Ответ 5

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

Пользовательские типы (то есть классы), как правило, изменяемы. Существуют некоторые исключения, такие как простые подклассы неизменяемого типа. Другие неизменяемые типы включают некоторые встроенные типы, такие как int, float, tuple и str, а также некоторые классы Python, реализованные в C.

Общее описание главы "Модель данных" в справочнике по языку Python":

Значение некоторых объектов может измениться. Объекты, значение которых может измениться считаются изменчивыми; объекты, значение которых неизменно, если они создаются называются неизменяемыми.

(Значение неизменяемого контейнера объект, который содержит ссылку на изменяемый объект, может измениться, когда значение последних изменяется; однако контейнер все еще считается неизменяемым, поскольку коллекция объектов, которые она содержит не может быть изменено. Таким образом, неизменность не является абсолютно такой же, как наличие неизменное значение, оно более тонкое.)

Изменчивость объектов определяется его типом; например, числа, строки и кортежи неизменяемый, в то время как словари и списки изменяемы.

Ответ 6

Если вы приходите на Python с другого языка (за исключением того, что очень похоже на Python, например Ruby), и настаивайте на том, чтобы понимать его с точки зрения этого другого языка, здесь, где люди обычно путаются:

>>> a = 1
>>> a = 2 # I thought int was immutable, but I just changed it?!

В Python назначение не является мутацией в Python.

В С++, если вы пишете a = 2, вы вызываете a.operator=(2), который будет мутировать объект, хранящийся в a. (И если в a не был сохранен объект, это ошибка.)

В Python a = 2 ничего не делает для того, что было сохранено в a; это просто означает, что 2 теперь сохраняется в a. (И если в a не было объекта, то это прекрасно.)


В конечном счете, это является частью еще более глубокого различия.

Переменная на языке С++ - это типизированное место в памяти. Если a является int, это означает, что 4 байта где-то, что знает компилятор, предполагается интерпретировать как int. Итак, когда вы делаете a = 2, это изменяет то, что хранится в этих 4 байтах памяти от 0, 0, 0, 1 до 0, 0, 0, 2. Если там другая переменная int в другом месте, она имеет свои 4 байта.

Переменная на языке Python - это имя для объекта, у которого есть своя жизнь. Там есть объект для числа 1 и еще один объект для числа 2. И a не 4 байта памяти, которые представлены как int, это просто имя, которое указывает на объект 1. Для a = 2 не имеет смысла превращать число 1 в число 2 (что дало бы любому программисту на Python слишком большую силу, чтобы изменить фундаментальные работы Вселенной); вместо этого вместо a следует забыть объект 1 и указать вместо него объект 2.


Итак, если назначение не является мутацией, что такое мутация?

  • Вызов метода, который задокументирован для мутаций, например a.append(b). (Обратите внимание, что эти методы почти всегда возвращают None). Неизменяемые типы не имеют таких методов, обычно изменяются типы.
  • Назначение части объекта, например a.spam = b или a[0] = b. Неизменяемые типы не позволяют назначать атрибуты или элементы, изменяемые типы обычно допускают тот или иной.
  • Иногда с использованием расширенного назначения, например a += b, иногда нет. Переменные типы обычно изменяют значение; неизменяемые типы никогда не делают, и вместо этого дают вам копию (они вычисляют a + b, а затем присваивают результат a).

Но если назначение не является мутацией, как назначить часть мутации объекта? То, где это становится сложно. a[0] = b не мутирует a[0] (опять же, в отличие от С++), но он мутирует a (в отличие от С++, кроме косвенно).

Все это, пожалуй, лучше не пытаться использовать семантику Python с точки зрения языка, к которому вы привыкли, и вместо этого изучать семантику Python на своих условиях.

Ответ 7

У изменяемого объекта должен быть хотя бы метод, способный мутировать объект. Например, объект list имеет метод append, который фактически будет мутировать объект:

>>> a = [1,2,3]
>>> a.append('hello') # `a` has mutated but is still the same object
>>> a
[1, 2, 3, 'hello']

но класс float не имеет метода для изменения объекта float. Вы можете сделать:

>>> b = 5.0 
>>> b = b + 0.1
>>> b
5.1

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

Когда вы выполняете b = b + 0.1 операнд = связывает переменную с новым поплавком, который создается с результатом te 5 + 0.1.

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

В любом случае = просто создайте привязку. Он не меняет и не создает объекты.

Когда вы выполняете a = 1.0, операнд = не создает float, а 1.0 часть строки. Фактически, когда вы пишете 1.0, это сокращение для float(1.0) вызова конструктора, возвращающего объект float. (Вот почему, если вы наберете 1.0 и нажмите enter, вы получите следующее "echo" 1.0, то есть возвращаемое значение вызываемой функции конструктора)

Теперь, если b - это float, и вы назначаете a = b, обе переменные указывают на один и тот же объект, но на самом деле переменные не могут взаимодействовать между собой, потому что объект является inmutable, и если вы выполняете b += 1, теперь b указывает на новый объект, а a все еще указывает на старое и не может знать, к чему указывает b.

но если c, скажем, a list, и вы назначаете a = c, теперь a и c могут "общаться", поскольку list является изменяемым, а если вы выполняете c.append('msg'), то просто проверяя a, вы получите сообщение.

(Кстати, у каждого объекта есть уникальный идентификационный номер, который вы можете получить с помощью id(x). Таким образом, вы можете проверить, является ли объект одинаковым или не проверяет, изменился ли его уникальный идентификатор.)

Ответ 8

Разница между Mutable и Immutable object

Определения

Mutable object: Объект, который можно изменить после его создания.
Неизменяемый объект: объект, который нельзя изменить после его создания.

В python попытается изменить значение неизменяемого объекта, он даст новый объект.

Мутируемые объекты

Ниже перечислены объекты списка в python, которые имеют изменяемый тип:

  1. list
  2. Dictionary
  3. Set
  4. bytearray
  5. user defined classes

Неизменяемые объекты

Ниже перечислены объекты списка в python, которые имеют неизменный тип:

  1. int
  2. float
  3. decimal
  4. complex
  5. bool
  6. string
  7. tuple
  8. range
  9. frozenset
  10. bytes

Некоторые неотвеченные вопросы

Вопросы: Является ли строка неизменяемым типом?
Ответ: да, но можете ли вы это объяснить: Доказательство 1:

a = "Hello"
a +=" World"
print a

Вывод

"Hello World"

В приведенном выше примере строка, полученная однажды, была создана как "Hello", наконец, изменена на "Hello World". Это означает, что строка имеет изменяемый тип. Но мы не можем проверить его личность и проверить, является ли он изменчивым типом или нет.

a = "Hello"
identity_a = id(a)
a += " World"
new_identity_a = id(a)
if identity_a != new_identity_a:
    print "String is Immutable"

Вывод

String is Immutable

Доказательство 2:

a = "Hello World"
a[0] = "M"

Вывод

TypeError 'str' object does not support item assignment

Вопросы: Является ли Tuple неизменным типом?
Ответ: да, это доказательство 1:

tuple_a = (1,)
tuple_a[0] = (2,)
print a

Вывод

'tuple' object does not support item assignment

Ответ 9

Класс неизменный, если каждый объект этого класса имеет фиксированное значение при создании экземпляра, который не может быть изменен ПОСЛЕДНИЕ

В другом слове измените все значение этой переменной (name) или оставьте ее в покое.

Пример:

my_string = "Hello world" 
my_string[0] = "h"
print my_string 

вы ожидали, что это сработает и напечатает привет мир, но это вызовет следующую ошибку:

Traceback (most recent call last):
File "test.py", line 4, in <module>
my_string[0] = "h"
TypeError: 'str' object does not support item assignment

Интерпретатор говорит: я не могу изменить первый символ этой строки

вам нужно будет изменить весь string, чтобы он работал:

my_string = "Hello World" 
my_string = "hello world"
print my_string #hello world

проверьте эту таблицу:

введите описание изображения здесь

источник

Ответ 10

Мне кажется, что вы боретесь с вопросом, что mutable/immutable на самом деле означает. Итак, вот простое объяснение:

Сначала нам нужна основа для обоснования.

Итак, подумайте о том, что вы программируете как виртуальный объект, что-то, что сохраняется в памяти компьютеров как последовательность двоичных чисел. (Не пытайтесь представить это слишком сложно. ^^) Теперь на большинстве компьютерных языков вы не будете работать с этими двоичными числами напрямую, а скорее используете интерпретацию двоичных чисел.

например. вы не думаете о таких цифрах, как 0x110, 0xaf0278297319 или аналогичные, но вместо этого вы думаете о таких цифрах, как 6 или строки, такие как "Hello, world". Тем не менее, тезисы номеров или строк являются интерпретацией двоичного числа в памяти компьютеров. То же самое верно для любого значения переменной.

Короче: Мы не с фактическими значениями, а с интерпретациями фактических двоичных значений.

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

Теперь подумайте о целых числах: это также виртуальные объекты (последовательности двоичных чисел в памяти компьютеров). Поэтому, если мы изменим один из них, например, увеличивая значение шесть за другим, это еще шесть? Ну, конечно, нет. Таким образом, любое целое является неизменным.

Итак: Если любое изменение в виртуальном объекте означает, что оно фактически становится другим виртуальным объектом, то оно называется неизменным.

Заключительные замечания:

(1) Никогда не смешивайте свой реальный опыт изменения и неизменности с программированием на определенном языке:

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

Итак, пока вы можете понять разницу в значении, вам все равно нужно изучить фактическую реализацию для каждого языка программирования.... Действительно, может быть цель языка, где 6 может быть приглушенным, чтобы стать 7. Затем снова это будет довольно сумасшедший или интересный материал, например, моделирование параллельных вселенных. ^^

(2) Это объяснение, конечно, не является научным, оно призвано помочь вам понять разницу между изменчивым и неизменным.

Ответ 11

Один из способов понять разницу:

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

Ответ 12

Целью этого ответа является создание единственного места для поиска всех хороших идей о том, как определить, имеете ли вы дело с мутированием/немасштабированием (неизменяемым/изменяемым) и, где возможно, что с этим делать? Бывают случаи, когда мутация нежелательна, и поведение python в этом отношении может чувствовать себя интуитивно понятным для кодеров, входящих в нее с других языков.

Согласно полезному сообщению @mina-gabriel:

Анализ вышеизложенного и объединение w/a post by @arrakëën:

Что не может неожиданно измениться?

  • скаляры (типы переменных, сохраняющие одно значение) не изменяются неожиданно
    • числовые примеры: int(), float(), complex()
  • существуют некоторые "изменяемые последовательности":
    • str(), tuple(), frozenset(), bytes()

Что может?

  • список как объекты (списки, словари, наборы, bytearray())
  • здесь также говорится о классах и экземплярах классов, но это может зависеть от того, что наследует класс и/или как его построил.

"неожиданно" я имею в виду, что программисты с других языков могут не ожидать этого поведения (с исключением или Ruby, а может быть, и с несколькими другими языками, похожими на Python).

Добавление к этому обсуждению:

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

Со списками простое решение состоит в том, чтобы построить новый такой:

list2 = list (list1)

с другими структурами... решение может быть более сложным. Один из способов - перебрать элементы и добавить их в новую пустую структуру данных (одного типа).

функции могут мутировать оригинал при передаче в изменяемых структурах. Как сказать?

  • Есть несколько тестов, которые даны в других комментариях по этой теме, но есть комментарии, указывающие на то, что эти тесты не являются полным доказательством.
  • object.function() - это метод исходного объекта, но только некоторые из них мутируют. Если они ничего не вернут, они, вероятно, это сделают. Можно было бы ожидать .append(), чтобы мутировать без тестирования, учитывая его имя..union() возвращает объединение set1.union(set2) и не мутирует. Если вы сомневаетесь, функцию можно проверить на возвращаемое значение. Если return = None, он не мутирует.
  • sorted() может быть обходным путем в некоторых случаях. Поскольку он возвращает отсортированную версию оригинала, он может позволить вам хранить не мутированную копию, прежде чем вы начнете работать с оригиналом другими способами. Однако этот вариант предполагает, что вам не нужен порядок исходных элементов (если вы это делаете, вам нужно найти другой способ). В отличие от .sort() мутирует оригинал (как и следовало ожидать).

Нестандартные подходы (в случае полезности): Нашел это в github, опубликованном под лицензией MIT:

  • репозиторий github в: tobgu named: pyrsistent
  • Что это такое: код постоянной структуры Python, написанный для использования вместо основных структур данных, когда мутация нежелательна.

Для пользовательских классов @semicolon предлагает проверить, есть ли функция __hash__, потому что изменяемые объекты обычно не имеют функции __hash__().

Это все, что я собрал на эту тему. Другие идеи, исправления и т.д. Приветствуются. Спасибо.

Ответ 13

Самый простой ответ:

Измененная переменная - это переменная, значение которой может измениться на месте, тогда как в неизменяемой переменной изменение значения не произойдет. Изменение неизменяемой переменной приведет к восстановлению той же переменной.

Пример:

>>>x = 5

Создает значение 5, на которое ссылается x

x → 5

>>>y = x

Этот оператор заставит y ссылаться на 5 из x

x ------------- > 5 < ----------- y

>>>x = x + y

Поскольку x является целым (неизменяемым типом), был восстановлен.

В заявлении выражение на RHS приведет к значению 10, и когда это назначено LHS (x), x перестроит до 10. Итак, теперь

х --------- > 10

у --------- > 5

Ответ 14

Я не прочитал все ответы, но выбранный ответ неверен, и я думаю, что у автора есть идея, что возможность переназначить переменную означает, что любой тип данных изменен. Это не относится к делу. Mutability имеет отношение к передаче по ссылке, а не по значению.

Допустим, вы создали список

a = [1,2]

Если бы вы сказали:

b = a
b[1] = 3

Даже если вы переназначили значение на B, оно также переназначает значение на a. Потому что, когда вы назначаете "b = a". Вы передаете "Ссылка" на объект, а не на копию значения. Это не относится к строкам, поплавкам и т.д. Это делает список, словари и подобные mutable, но логические, плавающие и т.д. Неизменяемы.

Ответ 15

В Python есть простой способ узнать:

Неизменное:

    >>> s='asd'
    >>> s is 'asd'
    True
    >>> s=None
    >>> s is None
    True
    >>> s=123
    >>> s is 123
    True

Mutable:

>>> s={}
>>> s is {}
False
>>> {} is {}
Flase
>>> s=[1,2]
>>> s is [1,2]
False
>>> s=(1,2)
>>> s is (1,2)
False

и

>>> s=abs
>>> s is abs
True

Поэтому я считаю, что встроенная функция также неизменна в Python.

Но я действительно не понимаю, как работает float:

>>> s=12.3
>>> s is 12.3
False
>>> 12.3 is 12.3
True
>>> s == 12.3
True
>>> id(12.3)
140241478380112
>>> id(s)
140241478380256
>>> s=12.3
>>> id(s)
140241478380112
>>> id(12.3)
140241478380256
>>> id(12.3)
140241478380256

Это так странно.