Подтвердить что ты не робот

Проверить на изменчивость в Python?

Рассмотрим этот код:

a = {...} # a is an dict with arbitrary contents
b = a.copy()
  • Какую роль играет изменчивость в ключах и значениях dicts?
  • Как обеспечить, чтобы изменения ключей или значений одного dict не отражались в другом?
  • Как это относится к hashable ограничению ключей dict?
  • Существуют ли различия в поведении между Python 2.x и Python 3.x?

Как проверить, является ли тип изменяемым в Python?

4b9b3361

Ответ 1

1) Ключи не должны быть изменяемыми, , если не существует пользовательского класса, который является хешируемым, но также изменяемым. Это все, что на тебя навязывает. Однако использование хешируемого, изменяемого объекта в качестве ключа dict может быть плохой идеей.

2) Не разделяя значения между двумя dicts. Хорошо делиться ключами, потому что они должны быть неизменными. Копирование словаря, в смысле модуля copy, безусловно, безопасно. Здесь также работает вызов конструктора dict: b = dict(a). Вы также можете использовать неизменяемые значения.

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

4) Не то, чтобы я знал; Я описываю 2.x.

Тип является изменяемым, если он не является неизменным. Тип является неизменным, если он является встроенным неизменяемым типом: str, int, long, bool, float, tuple и, возможно, пара других, которых я забываю. Пользовательские типы всегда изменяемы.

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

Ответ 2

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

Неизменяемость на самом деле не имеет отношения к диктофонам; отлично использовать переменные значения в качестве ключей. Важно иметь в виду сравнение и хеширование: объект должен всегда оставаться равным себе. Например:

class example(object):
    def __init__(self, a):
        self.value = a
    def __eq__(self, rhs):
        return self.value == rhs.value
    def __hash__(self):
        return hash(self.value)

a = example(1)
d = {a: "first"}
a.data = 2
print d[example(1)]

Здесь example не является неизменным; мы изменяем его с помощью a.data = 2. Тем не менее, мы используем его как ключ от хэша без каких-либо проблем. Зачем? Измененное свойство не влияет на равенство: хэш не изменяется, а example(1) всегда равен example(1), игнорируя любые другие свойства.

Чаще всего это кэширование и memoization: наличие кэширования или не логически не изменяет объект и обычно не влияет на равенство.

(Я остановлюсь здесь, пожалуйста, не сразу задавайте пять вопросов.)

Ответ 3

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

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

Если вы ищете что-то похожее на dict, но также неизменным, то namedtuple может быть лучшим выбором из того, что в стандартной библиотеке. По общему признанию, это не очень хорошее приближение, но это начало.

Ответ 4

  • dict ключи должны быть хешируемыми, что означает, что они имеют неизменяемое значение хэш-функции. dict значения могут быть или не быть изменяемыми; однако, если они изменяемы, это влияет на ваш второй вопрос.

  • "Изменения в клавишах" не будут отражаться между двумя диктофонами. Изменения неизменяемых значений, например строк, также не отражаются. Изменения в изменяемые объекты, такие как определяемые пользователем классы, будут отражаться, потому что объект хранится по id (например, ссылка).

    class T(object):
      def __init__(self, v):
        self.v = v
    
    
    t1 = T(5)
    
    
    d1 = {'a': t1}
    d2 = d1.copy()
    
    
    d2['a'].v = 7
    d1['a'].v   # = 7
    
    
    d2['a'] = T(2)
    d2['a'].v   # = 2
    d1['a'].v   # = 7
    
    
    import copy
    d3 = copy.deepcopy(d2) # perform a "deep copy"
    d3['a'].v = 12
    d3['a'].v   # = 12
    d2['a'].v   # = 2
    
  • Я думаю, что это объясняется двумя первыми ответами.

  • Не знаю, что я знаю в этом отношении.

некоторые дополнительные мысли:

Для понимания поведения ключей есть две основные вещи: ключи должны быть hashable (что означает, что они реализуют object.__hash__(self)), и они также должны быть "сопоставимыми" (что означает, что они реализуют что-то вроде object.__cmp__(self)). Один важный вывод из документов: по умолчанию хеш-функции пользовательских объектов возвращают id().

Рассмотрим следующий пример:

class K(object):
  def __init__(self, x, y):
     self.x = x
     self.y = y
  def __hash__(self):
     return self.x + self.y

k1 = K(1, 2)
d1 = {k1: 3}
d1[k1] # outputs 3
k1.x = 5
d1[k1] # KeyError!  The key hash has changed!
k2 = K(2, 1)
d1[k2] # KeyError!  The key hash is right, but the keys aren't equal.
k1.x = 1
d1[k1] # outputs 3

class NewK(object):
  def __init__(self, x, y):
     self.x = x
     self.y = y
  def __hash__(self):
     return self.x + self.y
  def __cmp__(self, other):
     return self.x - other.x

nk1 = NewK(3, 4)
nd1 = {nk1: 5}
nd1[nk1] # outputs 5
nk2 = NewK(3, 7)
nk1 == nk2 # True!
nd1[nk2] # KeyError! The keys' hashes differ.
hash(nk1) == hash(nk2) # False
nk2.y = 4
nd1[nk2] # outputs 5

# Where this can cause issues:
nd1.keys()[0].x = 5
nd1[nk1] # KeyError! nk1 is no longer in the dict!
id(nd1.keys()[0]) == id(nk1)  # Yikes. True?!
nd1.keys()[0].x = 3
nd1[nk1]  # outputs 5
id(nd1.keys()[0]) == id(nk1)  # True!

Значения намного легче понять, в дикторе хранятся ссылки на объекты. Прочтите разделы по hashable. Такие вещи, как строки, неизменяемы, если вы "меняете" их, то вы изменили его, теперь ссылается на новый объект. Объекты, которые изменяются, могут быть "изменены на месте", поэтому значение обоих dicts изменится.

d1 = {1: 'a'}
d2 = d1.copy()
id(d1[1]) == id(d2[1]) # True
d2[1] = 'z'
id(d1[1]) == id(d2[1]) # False

# the examples in section 2 above have more examples of this.

В любом случае, вот основные моменты всего этого:

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

Я не думаю, что есть общий способ проверить любой из этих точек. Тесты на пригодность будут зависеть от вашего прецедента. Например, может быть достаточно проверить, что объект выполняет или не реализует функции __hash__ и сравнения (__eq__ или __cmp__). Похожим образом вы можете каким-то образом "проверить" метод объекта __setattr__, чтобы определить, является ли он изменчивым.

Ответ 5

Есть MutableSequence, MutableSet, MutableMapping в модуле collections. Что можно использовать для проверки изменчивости готовых типов.

issubclass(TYPE, (MutableSequence, MutableSet, MutableMapping))

Если вы хотите использовать это в пользовательских типах, тип должен быть либо унаследован от одного из них, либо зарегистрирован как виртуальный подкласс.

class x(MutableSequence):
    ...

или

class x:
    ...

abc.ABCMeta.register(MutableSequence,x)

Ответ 6

Dicts - неупорядоченные наборы пар ключ: значение. Ключи должны быть неизменными и, следовательно, хешируемыми. Чтобы определить, является ли объект хешируемым, вы можете использовать функцию hash():

>>> hash(1)
1
>>> hash('a')
12416037344
>>> hash([1,2,3])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> hash((1,2,3))
2528502973977326415
>>> hash({1: 1})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

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