Неизменяемые и изменяемые типы
Я не понимаю, что такое неизменяемый тип. Я знаю, что 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())
В то время как что-то изменяемое имеет методы внутри класса, в примере этого типа:
class SortedKeyDict_a(dict):
def example(self):
return self.keys()
Также, в качестве последнего class(SortedKeyDict_a)
, если я передам ему этот тип set:
d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))
без вызова example
метода он возвращает словарь. SortedKeyDict
With __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 представляет все свои данные в виде объектов. Некоторые из этих объектов, такие как списки и словари, изменяемы, что означает, что вы можете изменять их содержимое, не меняя их идентификатора. Другие объекты, такие как целые числа, числа с плавающей запятой, строки и кортежи, являются объектами, которые нельзя изменить. Простой способ понять это, взглянув на идентификатор объекта.
Ниже вы видите строку, которая является неизменяемой. Вы не можете изменить ее содержимое. При попытке изменить ее будет вызвано 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
Общий неизменяемый тип:
- числа:
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)
использование списка on,
>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)
Ответ 4
Прежде всего, имеет ли класс методы или какова его структура класса, не имеет ничего общего с изменчивостью.
int
s и float
s являются неизменяемыми. Если я сделаю
a = 1
a += 5
Он указывает имя a
на 1
где-то в памяти в первой строке. Во второй строке он ищет это 1
, добавляет 5
, получает 6
, затем указывает a
на это 6
в памяти - это никоим образом не изменило 1
на 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
из tuples
) как val
, вы получаете ошибку, потому что у tuple
s нет .clear()
метода - вам нужно было бы передать dict(d)
как val
, чтобы он заработал, и в этом случае вы получите пустое значение SortedKeyDict
в результате.