#python #python_3x
Задача показать сходство и разницу между a += b и a = a + b. Я только учу Python 3, и не знаю ещё даже верхушку айсберга, но всё никогда не замечал каких то отличий. Чтобы было нагляднее, вот задача
Ответы
Ответ 1
a + b вызывает метод __add__ типа, а a += b вызывает метод __iadd__. Если метод __iadd__ реализован, то ожидается, что он изменит текущий объект. Если __iadd__ не реализован, то используется __add__. Это можно найти в документации. Логично получается, что __iadd__ имеет смысл для изменяемых типов данных, а для неизменяемых a += b всегда будет выполняться как a = a + b. Среди стандартных типов данных я нашёл метод __iadd__ только у типа list: In [38]: int.__iadd__ --------------------------------------------------------------------------- AttributeError Traceback (most recent call last)in ----> 1 int.__iadd__ AttributeError: type object 'int' has no attribute '__iadd__' In [39]: str.__iadd__ --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) in ----> 1 str.__iadd__ AttributeError: type object 'str' has no attribute '__iadd__' In [40]: list.__iadd__ Out[40]: Попробуем убедиться, что для списков a + b и a += b ведут себя по-разному. Отличие 1. __iadd__ изменяет текущий объект, а __add__ создаёт новый. В питоне есть функция id(), которая возвращает идентификатор объекта. Гарантируется, что идентификатор не будет меняться в течение жизни объекта (документация). Посмотрим идентификаторы списков, которые мы сложим по-разному: In [43]: l = [1, 2, 3] In [44]: id(l) Out[44]: 2209362559304 In [45]: l += [4, 5, 6] In [46]: id(l) Out[46]: 2209362559304 In [47]: l = l + [7, 8, 9] In [48]: id(l) Out[48]: 2209364596040 Видно, что после l += [4, 5, 6] объект не изменил свой id (то есть изменился исходный объект), а после l = l + [7, 8, 9] имеем новый id, потому что питон создал новый объект. Такое поведение описано документацией как рекомендуемое, но не гарантированное (These methods should attempt to do the operation in-place). Тем не менее стоит ожидать такого поведения при работе с изменяемыми типами данных. При реализации собственных классов тоже нужно учитывать указания документации. Отличие 2. Реализация. Можно посмотреть, как реализованы для списков методы add и iadd в исходном коде. Для add вызывается list_concat, для iadd — list_inplace_concat. Оказывается, что list_inplace_concat (который вызывается оператором +=) является обёрткой для extend, который принимает любой итерируемый объект (то есть не только списки, но и кортежи, строки, словари, множества и т. д.). А вот list_concat строго требует список. Отсюда понятно, почему такое сработает: In [49]: l = [1, 2, 3] In [50]: l += 'abc' In [51]: l Out[51]: [1, 2, 3, 'a', 'b', 'c'] А вот такое выдаст ошибку: In [52]: l = [1, 2, 3] In [53]: l = l + 'abc' --------------------------------------------------------------------------- TypeError Traceback (most recent call last) in ----> 1 l = l + 'abc' TypeError: can only concatenate list (not "str") to list Вот вам пример, когда при одинаковых операндах мы получаем разный результат. Но у каждого типа данных могут быть свои реализации этих методов. Конечно, в каком-то типе они будут реализованы так, что всегда будут приходить к одному результату. Но конкретно у списков эти операторы реализованы по-разному.
Комментариев нет:
Отправить комментарий