Как inplace add работает в кортежах, если кортежи не включают __iadd__, но все еще используют инструкцию INPLACE_ADD? - PullRequest
0 голосов
/ 26 февраля 2020

Мы знаем, tuple s не поддерживают назначение элементов, но мы можем выполнить inplace add с кортежами, однако этот создает новый объект , так как кортежи являются неизменяемыми. например:

>>> t1 = (1, 2)
>>> id(t1)
154311048
>>> t1 += (3, 4)
>>> t1
(1, 2, 3, 4)
>>> id(t1)
157955320

Это все хорошо. С другой стороны, list являются изменяемыми и не создают новые объекты при выполнении добавления на месте.

Теперь у меня сложилось впечатление, что добавление на месте в python было реализовано с помощью __iadd__ magi c, поэтому:

>>> l1 = [1,2,3]
>>> l1 += [4, 5]
# is same as
>>> l1 = l1.__iadd__([4,5])   # 1
# but, again for lists, this is also same as, simply
>>> l1.__iadd__([4,5])        # 2

Я предполагаю, что, поскольку операция на месте не гарантируется для неизменяемых объектов, python присваивает l1.__iadd__([4,5]) l1, иначе для списков, просто вызывая l1.__iadd__([4,5]) без присвоение его обратно l1 приведет к изменению l1 на месте.

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

>>> t1 = t1.__iadd__((3,4))

Но,

Traceback (most recent call last):

File "<ipython-input-12-e8ed2ace9f7f>", line 1, in <module> t1.__iadd__((1,2))

AttributeError: 'tuple' object has no attribute '__iadd__'

Действительно, '__iadd__' in dir(tuple) оценивается как False , Тогда я подумал, может быть, внутренне, Python составляет список из кортежей, выполняет __iadd__ и преобразует результат обратно в tuple (я не знаю, почему кто-то это сделал! Более того, это не будет объясните преимущества производительности для кортежей над списком) или для неизменяемых объектов, __iadd__ может просто вернуться к __add__ и вернуть значение, но после выполнения dis:

>>> from dis import dis
>>> dis('t1 += (3, 4)')
  1           0 LOAD_NAME                0 (t1)
              2 LOAD_CONST               0 ((3, 4))
              4 INPLACE_ADD
              6 STORE_NAME               0 (t1)
              8 LOAD_CONST               1 (None)
             10 RETURN_VALUE

Но байт-код имеет инструкция INPLACE_ADD! Для списка это выглядит так:

>>> dis('l1 += [1,2]')
  1           0 LOAD_NAME                0 (l1)
              2 LOAD_CONST               0 (1)
              4 LOAD_CONST               1 (2)
              6 BUILD_LIST               2
              8 INPLACE_ADD
             10 STORE_NAME               0 (l1)
             12 LOAD_CONST               2 (None)
             14 RETURN_VALUE

Кроме того, в случае list есть дополнительная инструкция BUILD_LIST, поэтому tuples также не создает список!

Как видите, меня это очень смущает. Может кто-нибудь объяснить, что происходит, как это работает?

1 Ответ

2 голосов
/ 26 февраля 2020

INPLACE_ADD возвращается к обычному добавлению с __add__ и __radd__, если __iadd__ не существует или возвращает NotImplemented.

...