Updating a sliced list

后端 未结 4 1486
野性不改
野性不改 2021-02-19 04:12

I thought I understood Python slicing operations, but when I tried to update a sliced list, I got confused:

>>> foo = [1, 2, 3, 4]
>>> foo[:1]          


        
相关标签:
4条回答
  • 2021-02-19 04:53

    foo[:] is a copy of foo. You mutated the copy.

    0 讨论(0)
  • 2021-02-19 04:59

    This is because python does not have l-values that could be assigned. Instead, some expressions have an assignment form, which is different.

    A foo[something] is a syntactic sugar for:

    foo.__getitem__(something)
    

    but a foo[something] = bar is a syntactic sugar for rather different:

    foo.__setitem__(something, bar)
    

    Where a slice is just a special case of something, so that foo[x:y] expands to

    foo.__getitem__(slice(x, y, None))
    

    and foo[x:y] = bar expands to

    foo.__setitem__(slice(x, y, None), bar)
    

    Now a __getitem__ with slice returns a new list that is a copy of the specified range, so modifying it does not affect the original array. And assigning works by the virtue of __setitem__ being a different method, that can simply do something else.

    However the special assignment treatment applies only to the outermost operation. The constituents are normal expressions. So when you write

    foo[:][1] = 'two'
    

    it gets expanded to

    foo.__getitem__(slice(None, None, None)).__setitem__(1, 'two')
    

    the foo.__getitem__(slice(None, None, None)) part creates a copy and that copy is modified by the __setitem__. But not the original array.

    0 讨论(0)
  • 2021-02-19 05:07

    Use

    foo[1]  = 'two'
    

    and

    foo[2:] = ['three', 'four']
    

    and it works.

    The answer why is in the comment above (because you're using a copy)

    0 讨论(0)
  • 2021-02-19 05:16

    The main thing to notice here is that foo[:] will return a copy of itself and then the indexing [1] will be applied on the copied list that was returned

    # indexing is applied on copied list
    (foo[:])[1] = 'two'
        ^
    copied list
    

    You can view this if you retain a reference to the copied list. So, the foo[:][1] = 'two' operation can be re-written as:

    foo = [1, 2, 3, 4]
    
    # the following is similar to foo[:][1] = 'two'
    
    copy_foo = foo[:]  
    copy_foo[1] = 'two'
    

    Now, copy_foo has been altered:

    print(copy_foo)
    # [1, 'two', 3, 4]
    

    But, foo remains the same:

    print(foo)
    # [1, 2, 3, 4]
    

    In your case, you didn't name the intermediate result from copying the foo list with foo[:], that is, you didn't keep a reference to it. After the assignment to 'two' is perfomed with foo[:][1] = 'two', the intermediate copied list ceases to exist.

    0 讨论(0)
提交回复
热议问题