Maybe I've been drinking too much of the functional programming Kool Aid, but this behavior of list comprehensions seems like a bad design choice:
>>> d = [1, 2, 3, 4, 5]
>>> [d.pop() for _ in range(len(d))]
[5, 4, 3, 2, 1]
Python never does copy unless you specifically ask it to do a copy. This is a perfectly simple, clear, and totally understandable rule. Putting exceptions and distinguos on it, such as "except under the following circumstances within a list comprehension...", would be utter folly: if Python's design had ever been under the management of somebody with such crazy ideas, Python would be a sick, contorted, half-broken language not worth learning. Thanks for making me happy all over again in the realization that is is definitely not the case!
You want a copy? Make a copy! That's always the solution in Python when you prefer a copy's overhead because you need to perform some changes that must not be reflected in the original. That is, in a clean approach, you'd do
dcopy = list(d) [dcopy.pop() for _ in range(len(d))]
If you're super-keen to have everything within a single expression, you can, though it's possibly not code one would call exactly "clean":
[dcopy.pop() for dcopy in [list(d)] for _ in range(len(d))]
i.e., the usual trick one uses when one would really like to fold an assignment into a list comprehension (add a
for clause, with the "control variable" being the name you want to assign to, and the "loop" is over a single-item sequence of the value you want to assign).
Functional languages never mutate data, therefore they don't make copies either (nor do they need to). Python is not a functional language, but of course there's a lot of things you can do in Python "the functional way", and often it's a better way. For example, a much better replacement for your list comprehension (guaranteed to have identical results and not affect
d, and vastly faster, more concise, and cleaner):
(AKA "the Martian Smiley", per my wife Anna;-). Slicing (not slice assignment, which is a different operation) always perform a copy in core Python (language and standard library), though of course not necessarily in independently developed third party modules like the popular
numpy (which prefers to see a slice as a "view" on the original