#python #python_faq
>>> a = [1,2,3,4,5,] >>> for x in a: x=1 >>> >>> a [1, 2, 3, 4, 5] >>> Вопрос - почему а не равен [1,1,1,1,1]?? Неужели в цикле создается копия для каждого х? как сделать "правильно" я понимаю. Просто интересен механизм цикла ...ведь для списка работает: >>> a = [[1],[2],[3]] >>> for x in a: x.append(33) >>> >>> a [[1, 33], [2, 33], [3, 33]] а присваивание тоже "не работает" со ссылкой ... >>> a [[1, 33], [2, 33], [3, 33]] >>> for x in a: x = [22] >>> >>> a [[1, 33], [2, 33], [3, 33]] >>> Ладно - последние два примера, которые мне вообще кажутся странными: >>> a = [[1],[2],[3]] >>> a [[1], [2], [3]] >>> for x in a: x = x +[11] >>> a [[1], [2], [3]] >>> for x in a: x+=[11] >>> a [[1, 11], [2, 11], [3, 11]] >>> где это и как объясняется?
Ответы
Ответ 1
Q: Как реализован цикл for? for x in a: x = 1 можно себе представить как: it = iter(a) while True: try: x = next(it) except StopIteration: break else: x = 1 то есть: получить итератор из переданной коллекции и по одному обойти все элементы, присваивая каждое значение переменной x (это описывает поведение, не фактическую реализацию). Q: почему а не равен [1,1,1,1,1]? a список содержит ссылки на самые обычные объекты. Объект никак не меняется от того, что какие-то списки могут ссылки на него содержать. Поэтому a не равен [1,1,1,1,1] по той же причине что и x = 2 x = 1 не делает 2 == 1. И даже если бы мы изменяемые объекты здесь использовали, такие как списки, из: x = [1] x = [2] не следует, что [1] == [2]. Подробнее: на первой итерации x = next(it) заставляет x имя ссылаться на тот же объект, что и a[0], то есть x is a[0] в этот момент. Затем x = 1 заставляет x имя ссылаться на единичку. Можно себе это представлять, как ярлык с одного предмета на другой переместили: c одного объекта a=1 на другой объект ярлык перевесили a=2 . От этого изначальный объект никак не меняется, иначе даже после for x in L: pass у вас стал бы весь список последнему элементу равен L = [5,5,5,5,5]. два примера, которые мне вообще кажутся странными: Для примера a = [[1],[2],[3]] циклы с x = x + [1] против x += [1] неравнозначны. Первый случай аналогичен предыдущим (простое присваивание): сумма x + [1] создаёт новый список, к которому прикрепляется имя x — ярлык со старого списка снимается и переносится на новый. Второй пример использует += операцию, которая для списков по месту изменение проводит — это равнозначно x.extend([1]), что не создаёт новый список, а добавляет элементы из аргумента к старому списку. В последнем случае и до и после x имя продолжает ссылаться на один и тот же объект (тот же список). где это и как объясняется? Чтобы узнать, что += делает, просто вызовите help('+=') в Питон REPL. Конкретно для изменяемых последовательностей (таких как списки) поведение s += t равнозначно s.extend(t).Ответ 2
Каждый раз в переменную записывается значение из списка (не копия), но при записи другого значения в эту же переменную, значение записывается именно в переменную, а не в список. Правильно делать так: a = [1,2,3,4,5] for i, x in enumerate(a): a[i] = 1 print(a) # [1, 1, 1, 1, 1] Можно удостовериться, что в переменную попадает не копия, а сам объект из списка. Только список лучше брать из чисел больше 256, т.к. для Python каждое число от -5 до 256 является одним и тем же объектом, т.е. >>> a = 256 >>> b = 256 >>> a == b True >>> a is b True # один и тот же объект >>> a = 257 >>> b = 257 >>> a == b True >>> a is b False # разные объекты Внимание: данное поведение не гарантируется, в разных реализациях и версиях языка внутренняя реализация объектов целых чисел может различаться. Для примера берем числа от 1000 до 1004: >>> a = list(range(1000, 1005)) >>> a [1000, 1001, 1002, 1003, 1004] >>> for i, x in enumerate(a): ... print(a[i] is x) True True True True True Видим, что в переменной x каждый раз оказывается тот же объект, что и в списке по индексу. Обновление. По поводу append. В самом первом примере вы изменяете значение в переменной, никак не связанной со списком, по сути просто в переменную записываете новое значение (переменная сначала "указывала" на объект внутри списка, а после присваивания начала указывать на другой объект, список при этом не изменяется). В случае с append, вы изменяете сам объект (его "внутренности"), полученный из списка (как я уже показал выше, при итерации по списку в переменную попадает сам объект, а не его копия). Т.к. это тот же самый объект, что лежит в списке, то видим, что и в самом списке объект меняется. Обновление 2 = и += это разные операторы, работающие по-разному. += может изменять исходный объект (если объект изменяемый, например если это список, но не число, ну и в зависимости от реализации метода __iadd__, если это какой-то свой объект), а = явно заменяет значение переменной, но не изменяет объект, который лежал в переменной до присваивания.
Комментариев нет:
Отправить комментарий