Почему списки Python не создают копии аргументов, поэтому реальные объекты не могут быть видоизменены? - PullRequest
1 голос
/ 22 августа 2010

Может быть, я слишком много выпил за функциональное программирование Kool Aid, но такое поведение списочных представлений кажется плохим выбором дизайна:

>>> d = [1, 2, 3, 4, 5]
>>> [d.pop() for _ in range(len(d))]
[5, 4, 3, 2, 1]
>>> d
[]

Почему d не копируется, а затем скопированная версия с лексической областью не видоизменяется (а затем теряется)? Точка понимания списка выглядит так: возвращает нужный список, а не возвращает список и незаметно изменяет какой-то другой объект за кулисами. Разрушение d несколько неявно, что кажется пифоническим. Есть хороший пример использования для этого?

Почему выгодно, чтобы списочные операции вели себя как циклы, а не как функции (из функционального языка, с локальной областью видимости)?

Ответы [ 14 ]

2 голосов
/ 22 августа 2010

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

Как правило, человек, использующий понимание списка, может написать код, который достаточно прост для понимания и лишен побочных эффектов, насколько это возможно. Я считаю код, который вы разместили, плохим стилем программирования и глупым способом создания перевернутого списка, когда существует list.reverse.

Хотя, я полагаю, если список, из которого вы извлекаете в этом примере, является очередью, к которой можно добавить код обработки очереди (то есть что-то гораздо более сложное, чем d.pop()) или другой поток, тогда код это своего рода разумный способ делать вещи. Хотя я действительно думаю, что это должен быть цикл, а не понимание списка.

2 голосов
/ 22 августа 2010

Конечно, есть (например, обработка очереди).Но ясно, что вы показали, что это не одно.

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

1 голос
/ 22 августа 2010

dan04 имеет правильный ответ, и для сравнения, вот маленький Haskell ...

[print s | s <- ["foo", "bar", "baz"]]

Здесь у вас есть побочный эффект (печать) прямо в середине понимания списка в Haskell. Haskell ленив, поэтому вы должны явно запустить его с sequence_:

main = sequence_ [print s | s <- ["foo", "bar", "baz"]]

Но это практически то же самое, что и Python

_ = list(print(s) for s in ["foo", "baz", "baz"])

За исключением того, что Haskell включает идиому _ = list... в функцию с именем sequence_.

Понимание списка не имеет ничего общего с предотвращением побочных эффектов. Просто неожиданно увидеть их там. И вряд ли вы можете получить более «функциональный», чем Haskell, поэтому ответ «Python - это императивный язык» в этом контексте не совсем верен.

1 голос
/ 22 августа 2010

Я не уверен, что вы спрашиваете. Может быть, вы спрашиваете, должна ли d.pop () возвращать копию вместо того, чтобы изменять саму себя? (Это никак не связано с пониманием списков.) Ответ на этот вопрос - нет, конечно, нет: это превратит его из операции O (1) в O (n), что будет катастрофическим недостатком проекта. 1001 *

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

...