2

What is the difference between

def delete_head1(t):
    #t[:] = t[1:]
    t = t[1:]

and

def delete_head2(t):
    t[:] = t[1:]
    #t = t[1:]

Why does the second one modify the input but not the first? That is, if I do

k=[1,2,3]
delete_head1(k)
print(k)

versus

k=[1,2,3]
delete_head2(k)
print(k)

The results are different. As I understand, the first one, in its body, creates and refers to a local variable t. Why isn't the second one also referring to a local variable t?

If in the body of the function I have:

t = t[1:]

the interpreter recognizes the second t (the one after the equals sign) but not the first t (the one before the equals sign)?

But this is not the case if I write

t[:] = t[1:]

In this last statement it recognizes both t? What is the guiding principle here for Python?

tzaman
  • 46,925
  • 11
  • 90
  • 115
onyourmark
  • 65
  • 1
  • 7
  • 1
    If you do not edit your post then both functions give the same output which is an error. – codester_09 Dec 17 '22 at 11:38
  • `t[:] = t[1:]` creates a shallow copy – roganjosh Dec 17 '22 at 11:40
  • why doesn't t=t[1:] create a shallow copy? – onyourmark Dec 17 '22 at 12:04
  • Does this answer your question? [Slicing a list in Python without generating a copy](https://stackoverflow.com/questions/5131538/slicing-a-list-in-python-without-generating-a-copy) – roganjosh Dec 17 '22 at 12:13
  • Roganjosh, the link you sent may be good. The short answer is: Slicing lists does not generate copies of the objects in the list; it just copies the references to them. But what is the principle? Are there other cases other than slicing that don't generate copies? Seems a bit ad hoc. – onyourmark Dec 17 '22 at 12:26
  • You've missed the point. A shallow copy would also give references to the items in the list, but the list itself is a different object. In this case, you're still referring to the same list itself - the outer container, unless you take the shallow copy – roganjosh Dec 17 '22 at 12:34
  • 1
    [This](https://nedbatchelder.com/text/names.html) may help in the general case, but I'm not sure it covers this case – roganjosh Dec 17 '22 at 12:36
  • Relevant: https://stackoverflow.com/a/10623383/1964317 – Elia Dec 17 '22 at 13:13
  • `t[1:]` creates a shallow copy (of part of the list). The assignment does not (or at least, only as an implementation detail). `t[:] = ...`, rather, *transfers* values from some iterable *into* the current list `t`. – chepner Dec 17 '22 at 13:39

1 Answers1

0

Assignment statements behave differently depending on what the target it. You are correct that assignment to a name in a function create a local variable, but only one of your examples actually assigns to a name.


When the target is a simple name,

t = ...

this simply makes t refer to whatever object ... produces, and leaves the old value of t with one less reference.


When the target is a slice,

t[:] = ...

this is really syntactic sugar for

t = t.__setitem__(slice(None, None, None), ...)

t might refer to a new object afterwards, depending on what t.__setitem__ returns. But usually, this method returns the same object, and as a side effect alters the object in some way.

For lists, t[:] = some_list is basically equivalent to

t.clear()
t.extend(some_list)

t refers to the same list before and after, but the contents of that list change.

The full details of the syntax are in section 7.2, Assignment Statements of the language documentation. The semantics of list.__setitem__ in particular aren't spelled out in great detail, but can be inferred from the description of the syntax and the description of s[i:j:k] = t.

chepner
  • 497,756
  • 71
  • 530
  • 681