본문 바로가기

개발하자

Python에서의 슬라이싱 작동 방식

반응형

Python에서의 슬라이싱 작동 방식

파이썬은 어떻게 작동하나요? 즉, 내가 , 등과 같은 코드를 쓸 때, 어떤 요소가 슬라이스에 포함되는지 어떻게 이해할 수 있는가? 해당되는 경우 참조를 포함하십시오.


표기법 뒤에 있는 설계 결정에 대한 자세한 내용은 을 참조하십시오.

슬라이싱의 가장 일반적인 실용적인 사용법(및 문제 해결을 위한 다른 방법)은 목록의 모든 N번째 요소를 가져오는 것을 참조하십시오. 해당되는 경우 해당 질문을 중복 대상으로 사용하십시오.

에 대한 자세한 답변은 를 참조하십시오(여기서도 설명하지만).




구문은 다음과 같습니다:

a[start:stop]  # items start through stop-1
a[start:]      # items start through the rest of the array
a[:stop]       # items from the beginning through stop-1
a[:]           # a copy of the whole array

위의 값과 함께 사용할 수 있는 값도 있습니다:

a[start:stop:step] # start through not past stop, by step

중요한 점은 이 값이 선택한 슬라이스의 첫 번째 값을 나타낸다는 것입니다. 따라서 및 사이의 차이는 선택한 요소의 수입니다(기본값이 1인 경우).

또 다른 특징은 또는 숫자일 수 있으며, 이는 숫자가 시작이 아닌 배열의 끝에서 계산된다는 것을 의미합니다. 그래서:

a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items

마찬가지로, 은 음수일 수 있다:

a[::-1]    # all items in the array, reversed
a[1::-1]   # the first two items, reversed
a[:-3:-1]  # the last two items, reversed
a[-3::-1]  # everything except the last two items, reversed

파이썬은 당신이 요구하는 것보다 적은 아이템이 있다면 프로그래머에게 친절하다. 예를 들어, 하나의 요소만 포함하도록 요청하면 오류 대신 빈 목록이 표시됩니다. 오류를 선호하는 경우가 있으므로 이러한 문제가 발생할 수 있음을 알아야 합니다.

대상과의 관계

A는 슬라이싱 작업을 나타낼 수 있다.

a[start:stop:step]

는 다음과 같습니다:

a[slice(start, stop, step)]

또한 슬라이스 개체는 변수의 수에 따라 약간 다르게 동작합니다. 즉, 둘 다 및 지원됩니다. 지정된 인수 지정을 건너뛰려면 를 사용하여 예를 들어 와 같거나 같습니다.

기반 표기법은 간단한 슬라이싱에 매우 유용하지만, 객체의 명시적인 사용은 슬라이싱의 프로그래밍 생성을 단순화한다.




그것에 대한 이야기(슬라이징에 대한 부분에 도달할 때까지 약간 아래로 스크롤).

ASCII 아트 다이어그램은 슬라이스가 작동하는 방식을 기억하는 데도 유용합니다:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6
-6  -5  -4  -3  -2  -1

슬라이스가 작동하는 방식을 기억하는 한 가지 방법은 인덱스를 첫 번째 문자의 왼쪽 가장자리에 0 번호가 붙은 포인팅 문자로 생각하는 것입니다. 그런 다음 일련의 문자의 마지막 문자의 오른쪽 가장자리에 색인이 있습니다.




시퀀스에 대한 문법에 의해 허용되는 가능성 열거:

>>> x[:]                # [x[0],   x[1],          ..., x[-1]    ]
>>> x[low:]             # [x[low], x[low+1],      ..., x[-1]    ]
>>> x[:high]            # [x[0],   x[1],          ..., x[high-1]]
>>> x[low:high]         # [x[low], x[low+1],      ..., x[high-1]]
>>> x[::stride]         # [x[0],   x[stride],     ..., x[-1]    ]
>>> x[low::stride]      # [x[low], x[low+stride], ..., x[-1]    ]
>>> x[:high:stride]     # [x[0],   x[stride],     ..., x[high-1]]
>>> x[low:high:stride]  # [x[low], x[low+stride], ..., x[high-1]]

물론, 만약 그렇다면, 종점은 보다 조금 더 낮을 것이다.

음수일 경우 카운트다운 중이므로 순서가 약간 변경됩니다:

>>> x[::-stride]        # [x[-1],   x[-1-stride],   ..., x[0]    ]
>>> x[high::-stride]    # [x[high], x[high-stride], ..., x[0]    ]
>>> x[:low:-stride]     # [x[-1],   x[-1-stride],   ..., x[low+1]]
>>> x[high:low:-stride] # [x[high], x[high-stride], ..., x[low+1]]

확장 슬라이싱(콤마 및 타원 포함)은 대부분 특수 데이터 구조(NumPy 등)에서만 사용되며 기본 시퀀스는 이를 지원하지 않습니다.

>>> class slicee:
...     def __getitem__(self, item):
...         return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'



그리고 슬라이싱 구문을 처음 봤을 때는 바로 알 수 없었던 몇 가지 사항이 있습니다:

>>> x = [1,2,3,4,5,6]
>>> x[::-1]
[6,5,4,3,2,1]

시퀀스를 쉽게 되돌릴 수 있습니다!

그리고 어떤 이유에서인지 거꾸로 된 순서의 모든 두 번째 항목을 원한다면:

>>> x = [1,2,3,4,5,6]
>>> x[::-2]
[6,4,2]



나는 그것에 대해 생각하는 "요소들 사이의 인덱스 포인트" 방법을 사용하지만, 때때로 다른 사람들이 그것을 얻는 것을 돕는 한 가지 방법은 다음과 같다:

mylist[X:Y]

X는 원하는 첫 번째 요소의 인덱스입니다. Y는 원하는 첫 번째 요소의 인덱스입니다.




그것을 조금 사용한 후에 나는 그것이 루프의 주장과 정확히 같다는 것을 깨달았다...

(from:to:step)

다음 중 하나를 선택할 수 있습니다:

(:to:step)
(from::step)
(from:to)

그런 다음 네거티브 인덱스는 문자열의 길이를 네거티브 인덱스에 추가하면 이해할 수 있습니다.

어쨌든 이건 나한테 효과가 있어...




위의 답변은 슬라이스 할당에 대해 논의하지 않습니다. 슬라이스 할당을 이해하려면 ASCII 아트에 다른 개념을 추가하는 것이 좋습니다:

                +---+---+---+---+---+---+
                | P | y | t | h | o | n |
                +---+---+---+---+---+---+
Slice position: 0   1   2   3   4   5   6
Index position:   0   1   2   3   4   5

>>> p = ['P','y','t','h','o','n']
# Why the two sets of numbers:
# indexing gives items, not lists
>>> p[0]
 'P'
>>> p[5]
 'n'

# Slicing gives lists
>>> p[0:1]
 ['P']
>>> p[0:2]
 ['P','y']

하나의 휴리스틱은 0에서 n까지의 슬라이스에 대해 "0은 시작이고, 시작에서 시작하고 목록에서 항목을 가져간다"고 생각하는 것이다.

>>> p[5] # the last of six items, indexed from zero
 'n'
>>> p[0:5] # does NOT include the last item!
 ['P','y','t','h','o']
>>> p[0:6] # not p[0:5]!!!
 ['P','y','t','h','o','n']

또 다른 경험적 접근법은 "모든 조각에 대해 시작을 0으로 바꾸고, 이전 경험적 접근법을 적용하여 목록의 끝을 얻은 다음, 첫 번째 숫자를 다시 세어 시작에서 항목을 잘라내는 것"이다

>>> p[0:4] # Start at the beginning and count out 4 items
 ['P','y','t','h']
>>> p[1:4] # Take one item off the front
 ['y','t','h']
>>> p[2:4] # Take two items off the front
 ['t','h']
# etc.

슬라이스 할당의 첫 번째 규칙은 리스트를 슬라이스한 이후에 리스트를 슬라이스 할당(또는 다른 반복 가능한 리스트를 슬라이스 할당하는 것이다:

>>> p[2:3]
 ['t']
>>> p[2:3] = ['T']
>>> p
 ['P','y','T','h','o','n']
>>> p[2:3] = 't'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable

위에서 볼 수 있는 슬라이스 할당의 두 번째 규칙은 슬라이스 인덱싱에 의해 반환되는 목록의 어떤 부분이든 슬라이스 할당에 의해 변경되는 것과 동일한 부분이라는 것입니다:

>>> p[2:4]
 ['T','h']
>>> p[2:4] = ['t','r']
>>> p
 ['P','y','t','r','o','n']

슬라이스 할당의 세 번째 규칙은 할당된 목록(편집 가능)의 길이가 같을 필요가 없다는 것이다:

>>> p = ['P','y','t','h','o','n'] # Start over
>>> p[2:4] = ['s','p','a','m']
>>> p
 ['P','y','s','p','a','m','o','n']

가장 익숙해지기 쉬운 부분은 빈 조각에 할당하는 것이다. 휴리스틱 1과 2를 사용하면 빈 조각을 쉽게 이해할 수 있습니다:

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []

그런 다음 빈 슬라이스에 슬라이스를 할당하면 다음과 같은 이점도 마찬가지입니다:

>>> p = ['P','y','t','h','o','n']
>>> p[2:4] = ['x','y'] # Assigned list is same length as slice
>>> p
 ['P','y','x','y','o','n'] # Result is same length
>>> p = ['P','y','t','h','o','n']
>>> p[3:4] = ['x','y'] # Assigned list is longer than slice
>>> p
 ['P','y','t','x','y','o','n'] # The result is longer
>>> p = ['P','y','t','h','o','n']
>>> p[4:4] = ['x','y']
>>> p
 ['P','y','t','h','x','y','o','n'] # The result is longer still

슬라이스의 두 번째 숫자(4)는 변경하지 않으므로 삽입된 항목은 빈 슬라이스에 할당할 때에도 항상 'o'에 바로 쌓입니다. 따라서 빈 슬라이스 할당의 위치는 비어 있지 않은 슬라이스 할당의 위치를 논리적으로 확장한 것입니다.

조금만 뒤로 물러서서, 슬라이스를 시작하는 우리의 행렬을 계속 진행하면 어떻게 될까요?

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []

슬라이싱을 사용하면, 일단 완료되면, 그것은 뒤로 자르기 시작하지 않습니다. 파이썬에서는 음수를 사용하여 명시적으로 요청하지 않는 한 음의 약진을 얻지 못합니다.

>>> p[5:3:-1]
 ['n','o']

"한 번 끝나면 끝"이라는 규칙에는 이상한 결과가 있습니다:

>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []
>>> p[6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

사실 인덱싱에 비해 파이썬 슬라이싱은 이상할 정도로 오류가 발생하지 않습니다:

>>> p[100:200]
 []
>>> p[int(2e99):int(1e99)]
 []

이것은 때때로 유용할 수 있지만, 약간 이상한 행동으로 이어질 수도 있습니다:

>>> p
 ['P', 'y', 't', 'h', 'o', 'n']
>>> p[int(2e99):int(1e99)] = ['p','o','w','e','r']
>>> p
 ['P', 'y', 't', 'h', 'o', 'n', 'p', 'o', 'w', 'e', 'r']

응용 프로그램에 따라 다음과(와) 다를 수 있습니다... 안 그러면... 네가 거기서 바라던 것이 되어라!


아래는 제 원래 답변의 본문입니다. 그것은 많은 사람들에게 유용했기 때문에 삭제하고 싶지 않았다.

>>> r=[1,2,3,4]
>>> r[1:1]
[]
>>> r[1:1]=[9,8]
>>> r
[1, 9, 8, 2, 3, 4]
>>> r[1:1]=['blah']
>>> r
[1, 'blah', 9, 8, 2, 3, 4]

이는 또한 슬라이싱과 인덱싱의 차이를 명확히 할 수 있다.




이 훌륭한 테이블을 발견했습니다

Python indexes and slices for a six-element list.
Indexes enumerate the elements, slices enumerate the spaces between the elements.

Index from rear:    -6  -5  -4  -3  -2  -1      a=[0,1,2,3,4,5]    a[1:]==[1,2,3,4,5]
Index from front:    0   1   2   3   4   5      len(a)==6          a[:5]==[0,1,2,3,4]
                   +---+---+---+---+---+---+    a[0]==0            a[:-2]==[0,1,2,3]
                   | a | b | c | d | e | f |    a[5]==5            a[1:2]==[1]
                   +---+---+---+---+---+---+    a[-1]==5           a[1:-1]==[1,2,3,4]
Slice from front:  :   1   2   3   4   5   :    a[-2]==4
Slice from rear:   :  -5  -4  -3  -2  -1   :
                                                b=a[:]
                                                b==[0,1,2,3,4,5] (shallow copy of a)



이것은 단지 추가 정보를 위한 것입니다... 아래 목록을 고려하십시오

>>> l=[12,23,345,456,67,7,945,467]

목록을 뒤집기 위한 몇 가지 다른 요령:

>>> l[len(l):-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[len(l)::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[-1:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]



어떻게 작동하는지 기억하는 것이 더 쉽다는 것을 알게 되고, 그러면 구체적인 시작/중지/단계 조합을 알아낼 수 있습니다.

먼저 이해하는 것이 좋습니다:

def range(start=0, stop, step=1):  # Illegal syntax, but that's the effect
    i = start
    while (i < stop if step > 0 else i > stop):
        yield i
        i += step

에서 시작하여, 단위로 증가하고, 도달하지 않습니다. 아주 간단합니다.

네거티브 스텝에 대해 기억해야 할 것은 더 높든 낮든 항상 배제된 끝이라는 것입니다. 반대 순서로 동일한 조각을 원할 경우, 반대 순서를 개별적으로 수행하는 것이 훨씬 더 깨끗합니다. 예를 들어 왼쪽에서 한 문자, 오른쪽에서 두 문자를 잘라낸 다음 반대로 진행합니다. (도 참조)

시퀀스 슬라이싱은 음의 인덱스를 먼저 정규화하고 시퀀스 밖으로 나갈 수 없다는 점을 제외하면 동일합니다:

: 아래 코드는 abs(step)>1에서 "시퀀스 밖으로 절대 나가지 마세요"라는 버그가 있었습니다; 제가 정확하게 패치를 했는데, 이해하기 어렵습니다.

def this_is_how_slicing_works(seq, start=None, stop=None, step=1):
    if start is None:
        start = (0 if step > 0 else len(seq)-1)
    elif start < 0:
        start += len(seq)
    if not 0 <= start < len(seq):  # clip if still outside bounds
        start = (0 if step > 0 else len(seq)-1)
    if stop is None:
        stop = (len(seq) if step > 0 else -1)  # really -1, not last element
    elif stop < 0:
        stop += len(seq)
    for i in range(start, stop, step):
        if 0 <= i < len(seq):
            yield seq[i]

세부 사항에 대해 걱정하지 마십시오. 생략하거나 항상 전체 시퀀스를 제공하는 올바른 작업을 수행한다는 것을 기억하십시오.

음의 인덱스를 먼저 정규화하면 시작 및/또는 중지를 끝부터 독립적으로 계산할 수 있습니다. 정규화는 때때로 "modulo the length"로 생각되지만, 길이를 단 한 번 추가한다: 예를 들어 전체 문자열일 뿐이다.




Python 2.7에서

파이썬에서 슬라이싱

[a:b:c]

len = length of string, tuple or list

c -- default is +1. The sign of c indicates forward or backward, absolute value of c indicates steps. Default is forward with step size 1. Positive means forward, negative means backward.

a --  When c is positive or blank, default is 0. When c is negative, default is -1.

b --  When c is positive or blank, default is len. When c is negative, default is -(len+1).

인덱스 할당을 이해하는 것은 매우 중요합니다.

In forward direction, starts at 0 and ends at len-1

In backward direction, starts at -1 and ends at -len

[a:b:c]라고 말할 때, 당신은 c의 부호(앞으로 또는 뒤로)에 따라 a에서 시작하여 b에서 끝난다(b번째 인덱스의 요소 제외)라고 말한다. 위의 인덱싱 규칙을 사용하여 이 범위의 요소만 찾을 수 있습니다:

-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1

그러나 이 범위는 양방향으로 무한히 계속됩니다:

...,-len -2 ,-len-1,-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1, len, len +1, len+2 , ....

예:

             0    1    2   3    4   5   6   7   8   9   10   11
             a    s    t   r    i   n   g
    -9  -8  -7   -6   -5  -4   -3  -2  -1

위의 a,b,c에 대한 규칙을 사용하여 횡단할 때 a,b,c를 선택하여 위의 범위와 겹칠 수 있는 경우 요소가 포함된 목록(횡단 중 터치됨)이 표시되거나 빈 목록이 표시됩니다.

마지막으로, 만약 a와 b가 같다면, 빈 목록도 얻을 수 있다:

>>> l1
[2, 3, 4]

>>> l1[:]
[2, 3, 4]

>>> l1[::-1] # a default is -1 , b default is -(len+1)
[4, 3, 2]

>>> l1[:-4:-1] # a default is -1
[4, 3, 2]

>>> l1[:-3:-1] # a default is -1
[4, 3]

>>> l1[::] # c default is +1, so a default is 0, b default is len
[2, 3, 4]

>>> l1[::-1] # c is -1 , so a default is -1 and b default is -(len+1)
[4, 3, 2]


>>> l1[-100:-200:-1] # Interesting
[]

>>> l1[-1:-200:-1] # Interesting
[4, 3, 2]


>>> l1[-1:-1:1]
[]


>>> l1[-1:5:1] # Interesting
[4]


>>> l1[1:-7:1]
[]

>>> l1[1:-7:-1] # Interesting
[3, 2]

>>> l1[:-2:-2] # a default is -1, stop(b) at -2 , step(c) by 2 in reverse direction
[4]



Index:
      ------------>
  0   1   2   3   4
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
  0  -4  -3  -2  -1
      <------------

Slice:
    <---------------|
|--------------->
:   1   2   3   4   :
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
:  -4  -3  -2  -1   :
|--------------->
    <---------------|

이것이 파이썬에서 목록을 모델링하는 데 도움이 되기를 바랍니다.

참조:




슬라이스 할당을 사용하여 목록에서 하나 이상의 요소를 제거할 수도 있습니다:

r = [1, 'blah', 9, 8, 2, 3, 4]
>>> r[1:4] = []
>>> r
[1, 2, 3, 4]



파이썬 슬라이싱 표기법:

a[start:end:step]
  • 및 의 경우 음수 값은 시퀀스의 끝에 상대적인 값으로 해석됩니다.
  • 에 대한 양의 지수는 포함할 마지막 요소의 위치를 나타냅니다.
  • 공백 값은 다음과 같이 기본값입니다.
  • 음의 단계를 사용하면 및 의 해석이 반대가 됩니다

표기법은 (숫자) 행렬과 다차원 배열로 확장된다. 예를 들어, 전체 열을 슬라이스하려면 다음을 사용할 수 있습니다:

m[::,0:2:] ## slice the first two columns

슬라이스에는 배열 요소의 복사본이 아닌 참조가 저장됩니다. 별도의 복사본을 배열로 만들려면 를 사용할 수 있습니다.




일반적으로 하드코딩된 인덱스 값이 많은 코드를 작성하면 가독성 및 유지관리가 엉망이 됩니다. 예를 들어, 1년 후에 코드로 돌아온다면, 당신은 코드를 보고 그것을 쓸 때 무슨 생각을 하고 있었는지 궁금해 할 것이다. 표시된 솔루션은 코드가 실제로 무엇을 하고 있는지 보다 명확하게 설명하는 방법일 뿐입니다. 일반적으로 기본 제공 슬라이스()는 슬라이스가 허용되는 모든 위치에서 사용할 수 있는 슬라이스 개체를 만듭니다. 예:

>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[2:4]
[2, 3]
>>> items[a]
[2, 3]
>>> items[a] = [10,11]
>>> items
[0, 1, 10, 11, 4, 5, 6]
>>> del items[a]
>>> items
[0, 1, 4, 5, 6]

슬라이스 인스턴스가 있는 경우 s.start, s.stop 및 s.step 속성을 각각 살펴봄으로써 해당 인스턴스에 대한 자세한 정보를 얻을 수 있습니다. 예:

>>> a = slice(10, 50, 2)
>>> a.start
10
>>> a.stop
50
>>> a.step
2
>>>



Python의 슬라이스 표기법 설명

간단히 말해서, 첨자 표기법()의 콜론()은 선택적 인수 , , 및 를 갖는 슬라이스 표기법을 만든다:

sliceable[start:stop:step]

파이썬 슬라이싱은 데이터의 일부에 체계적으로 액세스하는 계산적으로 빠른 방법입니다. 내 생각에, 중간 파이썬 프로그래머가 되기 위해서는 언어의 한 측면에 익숙해질 필요가 있다.

중요한 정의

우선 몇 가지 용어를 정의해 보겠습니다:

슬라이스의 시작 인덱스로, , 기본값이 0(즉, 첫 번째 인덱스)인 경우가 아니면 이 인덱스에 요소를 포함합니다. 부정적이면 끝부터 항목을 시작하라는 뜻이다.

슬라이스의 끝 인덱스(이 인덱스에 요소가 포함됨). 기본적으로 슬라이스되는 시퀀스의 길이(끝까지의 길이 및 끝까지의 길이 포함).

인덱스가 증가하는 양(기본값은 1)입니다. 음성이면 역으로 가회성 부분을 잘라내는 것입니다.

인덱싱 작동 방식

이러한 양수 또는 음수를 만들 수 있습니다. 양수의 의미는 간단하지만 음수의 경우 Python의 인덱스와 마찬가지로 끝에서 뒤로 세고, 의 경우 인덱스를 줄입니다. 다음 예제는 입니다. 그러나 각 인덱스가 참조하는 항목을 순서대로 나타내도록 약간 수정했습니다:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
   0   1   2   3   4   5 
  -6  -5  -4  -3  -2  -1

How Slicing Works

To use slice notation with a sequence that supports it, you must include at least one colon in the square brackets that follow the sequence (which actually implement the __getitem__ method of the sequence, according to the Python data model.)

Slice notation works like this:

sequence[start:stop:step]

And recall that there are defaults for start, stop, and step, so to access the defaults, simply leave out the argument.

Slice notation to get the last nine elements from a list (or any other sequence that supports it, like a string) would look like this:

my_list[-9:]

When I see this, I read the part in the brackets as "9th from the end, to the end." (Actually, I abbreviate it mentally as "-9, on")

Explanation:

The full notation is

my_list[-9:None:None]

and to substitute the defaults (actually when step is negative, stop's default is -len(my_list) - 1, so None for stop really just means it goes to whichever end step takes it to):

my_list[-9:len(my_list):1]

The colon, :, is what tells Python you're giving it a slice and not a regular index. That's why the idiomatic way of making a shallow copy of lists in Python 2 is

list_copy = sequence[:]

And clearing them is with:

del my_list[:]

(Python 3 gets a list.copy and list.clear method.)

When step is negative, the defaults for start and stop change

By default, when the step argument is empty (or None), it is assigned to +1.

But you can pass in a negative integer, and the list (or most other standard sliceables) will be sliced from the end to the beginning.

Thus a negative slice will change the defaults for start and stop!

Confirming this in the source

I like to encourage users to read the source as well as the documentation. The source code for slice objects and this logic is found here. First we determine if step is negative:

step_is_negative = step_sign < 0;

If so, the lower bound is -1 meaning we slice all the way up to and including the beginning, and the upper bound is the length minus 1, meaning we start at the end. (Note that the semantics of this -1 is different from a -1 that users may pass indexes in Python indicating the last item.)

if (step_is_negative) {
    lower = PyLong_FromLong(-1L);
    if (lower == NULL)
        goto error;

    upper = PyNumber_Add(length, lower);
    if (upper == NULL)
        goto error;
}

Otherwise step is positive, and the lower bound will be zero and the upper bound (which we go up to but not including) the length of the sliced list.

else {
    lower = _PyLong_Zero;
    Py_INCREF(lower);
    upper = length;
    Py_INCREF(upper);
}

Then, we may need to apply the defaults for start and stop—the default then for start is calculated as the upper bound when step is negative:

if (self->start == Py_None) {
    start = step_is_negative ? upper : lower;
    Py_INCREF(start);
}

and stop, the lower bound:

if (self->stop == Py_None) {
    stop = step_is_negative ? lower : upper;
    Py_INCREF(stop);
}

Give your slices a descriptive name!

You may find it useful to separate forming the slice from passing it to the list.__getitem__ method (that's what the square brackets do). Even if you're not new to it, it keeps your code more readable so that others that may have to read your code can more readily understand what you're doing.

However, you can't just assign some integers separated by colons to a variable. You need to use the slice object:

last_nine_slice = slice(-9, None)

The second argument, None, is required, so that the first argument is interpreted as the start argument otherwise it would be the stop argument.

You can then pass the slice object to your sequence:

>>> list(range(100))[last_nine_slice]
[91, 92, 93, 94, 95, 96, 97, 98, 99]

It's interesting that ranges also take slices:

>>> range(100)[last_nine_slice]
range(91, 100)

Memory Considerations:

Since slices of Python lists create new objects in memory, another important function to be aware of is itertools.islice. Typically you'll want to iterate over a slice, not just have it created statically in memory. islice is perfect for this. A caveat, it doesn't support negative arguments to start, stop, or step, so if that's an issue you may need to calculate indices or reverse the iterable in advance.

length = 100
last_nine_iter = itertools.islice(list(range(length)), length-9, None, 1)
list_last_nine = list(last_nine_iter)

and now:

>>> list_last_nine
[91, 92, 93, 94, 95, 96, 97, 98, 99]

The fact that list slices make a copy is a feature of lists themselves. If you're slicing advanced objects like a Pandas DataFrame, it may return a view on the original, and not a copy.




#!/usr/bin/env python

def slicegraphical(s, lista):

    if len(s) > 9:
        print """Enter a string of maximum 9 characters,
    so the printig would looki nice"""
        return 0;
    # print " ",
    print '  '+'+---' * len(s) +'+'
    print ' ',
    for letter in s:
        print '| {}'.format(letter),
    print '|'
    print " ",; print '+---' * len(s) +'+'

    print " ",
    for letter in range(len(s) +1):
        print '{}  '.format(letter),
    print ""
    for letter in range(-1*(len(s)), 0):
        print ' {}'.format(letter),
    print ''
    print ''


    for triada in lista:
        if len(triada) == 3:
            if triada[0]==None and triada[1] == None and triada[2] == None:
                # 000
                print s+'[   :   :   ]' +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] == None and triada[2] != None:
                # 001
                print s+'[   :   :{0:2d} ]'.format(triada[2], '','') +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] == None:
                # 010
                print s+'[   :{0:2d} :   ]'.format(triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] != None:
                # 011
                print s+'[   :{0:2d} :{1:2d} ]'.format(triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] == None:
                # 100
                print s+'[{0:2d} :   :   ]'.format(triada[0]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] != None:
                # 101
                print s+'[{0:2d} :   :{1:2d} ]'.format(triada[0], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] == None:
                # 110
                print s+'[{0:2d} :{1:2d} :   ]'.format(triada[0], triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] != None:
                # 111
                print s+'[{0:2d} :{1:2d} :{2:2d} ]'.format(triada[0], triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]

        elif len(triada) == 2:
            if triada[0] == None and triada[1] == None:
                # 00
                print s+'[   :   ]    ' + ' = ', s[triada[0]:triada[1]]
            elif triada[0] == None and triada[1] != None:
                # 01
                print s+'[   :{0:2d} ]    '.format(triada[1]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] == None:
                # 10
                print s+'[{0:2d} :   ]    '.format(triada[0]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] != None:
                # 11
                print s+'[{0:2d} :{1:2d} ]    '.format(triada[0],triada[1]) + ' = ', s[triada[0]:triada[1]]

        elif len(triada) == 1:
            print s+'[{0:2d} ]        '.format(triada[0]) + ' = ', s[triada[0]]


if __name__ == '__main__':
    # Change "s" to what ever string you like, make it 9 characters for
    # better representation.
    s = 'COMPUTERS'

    # add to this list different lists to experement with indexes
    # to represent ex. s[::], use s[None, None,None], otherwise you get an error
    # for s[2:] use s[2:None]

    lista = [[4,7],[2,5,2],[-5,1,-1],[4],[-4,-6,-1], [2,-3,1],[2,-3,-1], [None,None,-1],[-5,None],[-5,0,-1],[-5,None,-1],[-1,1,-2]]

    slicegraphical(s, lista)

You can run this script and experiment with it, below is some samples that I got from the script.

  +---+---+---+---+---+---+---+---+---+
  | C | O | M | P | U | T | E | R | S |
  +---+---+---+---+---+---+---+---+---+
  0   1   2   3   4   5   6   7   8   9   
 -9  -8  -7  -6  -5  -4  -3  -2  -1 

COMPUTERS[ 4 : 7 ]     =  UTE
COMPUTERS[ 2 : 5 : 2 ] =  MU
COMPUTERS[-5 : 1 :-1 ] =  UPM
COMPUTERS[ 4 ]         =  U
COMPUTERS[-4 :-6 :-1 ] =  TU
COMPUTERS[ 2 :-3 : 1 ] =  MPUT
COMPUTERS[ 2 :-3 :-1 ] =  
COMPUTERS[   :   :-1 ] =  SRETUPMOC
COMPUTERS[-5 :   ]     =  UTERS
COMPUTERS[-5 : 0 :-1 ] =  UPMO
COMPUTERS[-5 :   :-1 ] =  UPMOC
COMPUTERS[-1 : 1 :-2 ] =  SEUM
[Finished in 0.9s]

When using a negative step, notice that the answer is shifted to the right by 1.




This is how I teach slices to newbies:

Understanding the difference between indexing and slicing:

Wiki Python has this amazing picture which clearly distinguishes indexing and slicing.

Enter image description here

It is a list with six elements in it. To understand slicing better, consider that list as a set of six boxes placed together. Each box has an alphabet in it.

Indexing is like dealing with the contents of box. You can check contents of any box. But you can't check the contents of multiple boxes at once. You can even replace the contents of the box. But you can't place two balls in one box or replace two balls at a time.

In [122]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [123]: alpha
Out[123]: ['a', 'b', 'c', 'd', 'e', 'f']

In [124]: alpha[0]
Out[124]: 'a'

In [127]: alpha[0] = 'A'

In [128]: alpha
Out[128]: ['A', 'b', 'c', 'd', 'e', 'f']

In [129]: alpha[0,1]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-129-c7eb16585371> in <module>()
----> 1 alpha[0,1]

TypeError: list indices must be integers, not tuple

Slicing is like dealing with boxes themselves. You can pick up the first box and place it on another table. To pick up the box, all you need to know is the position of beginning and ending of the box.

You can even pick up the first three boxes or the last two boxes or all boxes between 1 and 4. So, you can pick any set of boxes if you know the beginning and ending. These positions are called start and stop positions.

The interesting thing is that you can replace multiple boxes at once. Also you can place multiple boxes wherever you like.

In [130]: alpha[0:1]
Out[130]: ['A']

In [131]: alpha[0:1] = 'a'

In [132]: alpha
Out[132]: ['a', 'b', 'c', 'd', 'e', 'f']

In [133]: alpha[0:2] = ['A', 'B']

In [134]: alpha
Out[134]: ['A', 'B', 'c', 'd', 'e', 'f']

In [135]: alpha[2:2] = ['x', 'xx']

In [136]: alpha
Out[136]: ['A', 'B', 'x', 'xx', 'c', 'd', 'e', 'f']

Slicing With Step:

Till now you have picked boxes continuously. But sometimes you need to pick up discretely. For example, you can pick up every second box. You can even pick up every third box from the end. This value is called step size. This represents the gap between your successive pickups. The step size should be positive if You are picking boxes from the beginning to end and vice versa.

In [137]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [142]: alpha[1:5:2]
Out[142]: ['b', 'd']

In [143]: alpha[-1:-5:-2]
Out[143]: ['f', 'd']

In [144]: alpha[1:5:-2]
Out[144]: []

In [145]: alpha[-1:-5:2]
Out[145]: []

How Python Figures Out Missing Parameters:

When slicing, if you leave out any parameter, Python tries to figure it out automatically.

If you check the source code of CPython, you will find a function called PySlice_GetIndicesEx() which figures out indices to a slice for any given parameters. Here is the logical equivalent code in Python.

This function takes a Python object and optional parameters for slicing and returns the start, stop, step, and slice length for the requested slice.

def py_slice_get_indices_ex(obj, start=None, stop=None, step=None):

    length = len(obj)

    if step is None:
        step = 1
    if step == 0:
        raise Exception("Step cannot be zero.")

    if start is None:
        start = 0 if step > 0 else length - 1
    else:
        if start < 0:
            start += length
        if start < 0:
            start = 0 if step > 0 else -1
        if start >= length:
            start = length if step > 0 else length - 1

    if stop is None:
        stop = length if step > 0 else -1
    else:
        if stop < 0:
            stop += length
        if stop < 0:
            stop = 0 if step > 0 else -1
        if stop >= length:
            stop = length if step > 0 else length - 1

    if (step < 0 and stop >= start) or (step > 0 and start >= stop):
        slice_length = 0
    elif step < 0:
        slice_length = (stop - start + 1)/(step) + 1
    else:
        slice_length = (stop - start - 1)/(step) + 1

    return (start, stop, step, slice_length)

This is the intelligence that is present behind slices. Since Python has an built-in function called slice, you can pass some parameters and check how smartly it calculates missing parameters.

In [21]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [22]: s = slice(None, None, None)

In [23]: s
Out[23]: slice(None, None, None)

In [24]: s.indices(len(alpha))
Out[24]: (0, 6, 1)

In [25]: range(*s.indices(len(alpha)))
Out[25]: [0, 1, 2, 3, 4, 5]

In [26]: s = slice(None, None, -1)

In [27]: range(*s.indices(len(alpha)))
Out[27]: [5, 4, 3, 2, 1, 0]

In [28]: s = slice(None, 3, -1)

In [29]: range(*s.indices(len(alpha)))
Out[29]: [5, 4]

Note: This post was originally written in my blog, The Intelligence Behind Python Slices.




My brain seems happy to accept that lst[start:end] contains the start-th item. I might even say that it is a 'natural assumption'.

But occasionally a doubt creeps in and my brain asks for reassurance that it does not contain the end-th element.

In these moments I rely on this simple theorem:

for any n,    lst = lst[:n] + lst[n:]

This pretty property tells me that lst[start:end] does not contain the end-th item because it is in lst[end:].

Note that this theorem is true for any n at all. For example, you can check that

lst = range(10)
lst[:-42] + lst[-42:] == lst

returns True.




1. Slice Notation

To make it simple, remember slice has only one form:

s[start:end:step]

and here is how it works:

  • s: an object that can be sliced
  • start: first index to start iteration
  • end: last index, NOTE that end index will not be included in the resulted slice
  • step: pick element every step index

Another import thing: all start,end, step can be omitted! And if they are omitted, their default value will be used: 0,len(s),1 accordingly.

So possible variations are:

# Mostly used variations
s[start:end]
s[start:]
s[:end]

# Step-related variations
s[:end:step]
s[start::step]
s[::step]

# Make a copy
s[:]

NOTE: If start >= end (considering only when step>0), Python will return a empty slice [].

2. Pitfalls

The above part explains the core features on how slice works, and it will work on most occasions. However, there can be pitfalls you should watch out, and this part explains them.

Negative indexes

The very first thing that confuses Python learners is that an index can be negative! Don't panic: a negative index means count backwards.

For example:

s[-5:]    # Start at the 5th index from the end of array,
          # thus returning the last 5 elements.
s[:-5]    # Start at index 0, and end until the 5th index from end of array,
          # thus returning s[0:len(s)-5].

Negative step

Making things more confusing is that step can be negative too!

A negative step means iterate the array backwards: from the end to start, with the end index included, and the start index excluded from the result.

NOTE: when step is negative, the default value for start is len(s) (while end does not equal to 0, because s[::-1] contains s[0]). For example:

s[::-1]            # Reversed slice
s[len(s)::-1]      # The same as above, reversed slice
s[0:len(s):-1]     # Empty list

Out of range error?

Be surprised: slice does not raise an IndexError when the index is out of range!

If the index is out of range, Python will try its best to set the index to 0 or len(s) according to the situation. For example:

s[:len(s)+5]      # The same as s[:len(s)]
s[-len(s)-5::]    # The same as s[0:]
s[len(s)+5::-1]   # The same as s[len(s)::-1], and the same as s[::-1]

3. Examples

Let's finish this answer with examples, explaining everything we have discussed:

# Create our array for demonstration
In [1]: s = [i for i in range(10)]

In [2]: s
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [3]: s[2:]   # From index 2 to last index
Out[3]: [2, 3, 4, 5, 6, 7, 8, 9]

In [4]: s[:8]   # From index 0 up to index 8
Out[4]: [0, 1, 2, 3, 4, 5, 6, 7]

In [5]: s[4:7]  # From index 4 (included) up to index 7(excluded)
Out[5]: [4, 5, 6]

In [6]: s[:-2]  # Up to second last index (negative index)
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7]

In [7]: s[-2:]  # From second last index (negative index)
Out[7]: [8, 9]

In [8]: s[::-1] # From last to first in reverse order (negative step)
Out[8]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

In [9]: s[::-2] # All odd numbers in reversed order
Out[9]: [9, 7, 5, 3, 1]

In [11]: s[-2::-2] # All even numbers in reversed order
Out[11]: [8, 6, 4, 2, 0]

In [12]: s[3:15]   # End is out of range, and Python will set it to len(s).
Out[12]: [3, 4, 5, 6, 7, 8, 9]

In [14]: s[5:1]    # Start > end; return empty list
Out[14]: []

In [15]: s[11]     # Access index 11 (greater than len(s)) will raise an IndexError
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-15-79ffc22473a3> in <module>()
----> 1 s[11]

IndexError: list index out of range



The previous answers don't discuss multi-dimensional array slicing which is possible using the famous NumPy package:

Slicing can also be applied to multi-dimensional arrays.

# Here, a is a NumPy array

>>> a
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
>>> a[:2, 0:3:2]
array([[1, 3],
       [5, 7]])

The ":2" before the comma operates on the first dimension and the "0:3:2" after the comma operates on the second dimension.




The below is the example of an index of a string:

 +---+---+---+---+---+
 | H | e | l | p | A |
 +---+---+---+---+---+
 0   1   2   3   4   5
-5  -4  -3  -2  -1

str="Name string"

Slicing example: [start:end:step]

str[start:end] # Items start through end-1
str[start:]    # Items start through the rest of the array
str[:end]      # Items from the beginning through end-1
str[:]         # A copy of the whole array

Below is the example usage:

print str[0] = N
print str[0:2] = Na
print str[0:7] = Name st
print str[0:7:2] = Nm t
print str[0:-1:2] = Nm ti



In Python, the most basic form for slicing is the following:

l[start:end]

where l is some collection, start is an inclusive index, and end is an exclusive index.

In [1]: l = list(range(10))

In [2]: l[:5] # First five elements
Out[2]: [0, 1, 2, 3, 4]

In [3]: l[-5:] # Last five elements
Out[3]: [5, 6, 7, 8, 9]

When slicing from the start, you can omit the zero index, and when slicing to the end, you can omit the final index since it is redundant, so do not be verbose:

In [5]: l[:3] == l[0:3]
Out[5]: True

In [6]: l[7:] == l[7:len(l)]
Out[6]: True

Negative integers are useful when doing offsets relative to the end of a collection:

In [7]: l[:-1] # Include all elements but the last one
Out[7]: [0, 1, 2, 3, 4, 5, 6, 7, 8]

In [8]: l[-3:] # Take the last three elements
Out[8]: [7, 8, 9]

It is possible to provide indices that are out of bounds when slicing such as:

In [9]: l[:20] # 20 is out of index bounds, and l[20] will raise an IndexError exception
Out[9]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [11]: l[-20:] # -20 is out of index bounds, and l[-20] will raise an IndexError exception
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Keep in mind that the result of slicing a collection is a whole new collection. In addition, when using slice notation in assignments, the length of the slice assignments do not need to be the same. The values before and after the assigned slice will be kept, and the collection will shrink or grow to contain the new values:

In [16]: l[2:6] = list('abc') # Assigning fewer elements than the ones contained in the sliced collection l[2:6]

In [17]: l
Out[17]: [0, 1, 'a', 'b', 'c', 6, 7, 8, 9]

In [18]: l[2:5] = list('hello') # Assigning more elements than the ones contained in the sliced collection l [2:5]

In [19]: l
Out[19]: [0, 1, 'h', 'e', 'l', 'l', 'o', 6, 7, 8, 9]

If you omit the start and end index, you will make a copy of the collection:

In [14]: l_copy = l[:]

In [15]: l == l_copy and l is not l_copy
Out[15]: True

If the start and end indexes are omitted when performing an assignment operation, the entire content of the collection will be replaced with a copy of what is referenced:

In [20]: l[:] = list('hello...')

In [21]: l
Out[21]: ['h', 'e', 'l', 'l', 'o', '.', '.', '.']

Besides basic slicing, it is also possible to apply the following notation:

l[start:end:step]

where l is a collection, start is an inclusive index, end is an exclusive index, and step is a stride that can be used to take every nth item in l.

In [22]: l = list(range(10))

In [23]: l[::2] # Take the elements which indexes are even
Out[23]: [0, 2, 4, 6, 8]

In [24]: l[1::2] # Take the elements which indexes are odd
Out[24]: [1, 3, 5, 7, 9]

Using step provides a useful trick to reverse a collection in Python:

In [25]: l[::-1]
Out[25]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

It is also possible to use negative integers for step as the following example:

In[28]:  l[::-2]
Out[28]: [9, 7, 5, 3, 1]

However, using a negative value for step could become very confusing. Moreover, in order to be Pythonic, you should avoid using start, end, and step in a single slice. In case this is required, consider doing this in two assignments (one to slice, and the other to stride).

In [29]: l = l[::2] # This step is for striding

In [30]: l
Out[30]: [0, 2, 4, 6, 8]

In [31]: l = l[1:-1] # This step is for slicing

In [32]: l
Out[32]: [2, 4, 6]



Most of the previous answers clears up questions about slice notation.

The extended indexing syntax used for slicing is aList[start:stop:step], and basic examples are:

Enter image description here:

More slicing examples: 15 Extended Slices




In my opinion, you will understand and memorize better the Python string slicing notation if you look at it the following way (read on).

Let's work with the following string ...

azString = "abcdefghijklmnopqrstuvwxyz"

For those who don't know, you can create any substring from azString using the notation azString[x:y]

Coming from other programming languages, that's when the common sense gets compromised. What are x and y?

I had to sit down and run several scenarios in my quest for a memorization technique that will help me remember what x and y are and help me slice strings properly at the first attempt.

My conclusion is that x and y should be seen as the boundary indexes that are surrounding the strings that we want to extra. So we should see the expression as azString[index1, index2] or even more clearer as azString[index_of_first_character, index_after_the_last_character].

Here is an example visualization of that ...

Letters   a b c d e f g h i j ...
         ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
             ┊           ┊
Indexes  0 1 2 3 4 5 6 7 8 9 ...
             ┊           ┊
cdefgh    index1       index2

So all you have to do is setting index1 and index2 to the values that will surround the desired substring. For instance, to get the substring "cdefgh", you can use azString[2:8], because the index on the left side of "c" is 2 and the one on the right size of "h" is 8.

Remember that we are setting the boundaries. And those boundaries are the positions where you could place some brackets that will be wrapped around the substring like this ...

a b [ c d e f g h ] i j

That trick works all the time and is easy to memorize.




If you feel negative indices in slicing is confusing, here's a very easy way to think about it: just replace the negative index with len - index. So for example, replace -3 with len(list) - 3.

The best way to illustrate what slicing does internally is just show it in code that implements this operation:

def slice(list, start = None, end = None, step = 1):
  # Take care of missing start/end parameters
  start = 0 if start is None else start
  end = len(list) if end is None else end

  # Take care of negative start/end parameters
  start = len(list) + start if start < 0 else start
  end = len(list) + end if end < 0 else end

  # Now just execute a for-loop with start, end and step
  return [list[i] for i in range(start, end, step)]



I want to add one Hello, World! example that explains the basics of slices for the very beginners. It helped me a lot.

Let's have a list with six values ['P', 'Y', 'T', 'H', 'O', 'N']:

+---+---+---+---+---+---+
| P | Y | T | H | O | N |
+---+---+---+---+---+---+
  0   1   2   3   4   5

Now the simplest slices of that list are its sublists. The notation is [<index>:<index>] and the key is to read it like this:

[ start cutting before this index : end cutting before this index ]

Now if you make a slice [2:5] of the list above, this will happen:

        |           |
+---+---|---+---+---|---+
| P | Y | T | H | O | N |
+---+---|---+---+---|---+
  0   1 | 2   3   4 | 5

You made a cut before the element with index 2 and another cut before the element with index 5. So the result will be a slice between those two cuts, a list ['T', 'H', 'O'].




The basic slicing technique is to define the starting point, the stopping point, and the step size—also known as stride.

First, we will create a list of values to use in our slicing.

Create two lists to slice. The first is a numeric list from 1 to 9 (List A). The second is also a numeric list, from 0 to 9 (List B):

A = list(range(1, 10, 1)) # Start, stop, and step
B = list(range(9))

print("This is List A:", A)
print("This is List B:", B)

Index the number 3 from A and the number 6 from B.

print(A[2])
print(B[6])

Basic Slicing

Extended indexing syntax used for slicing is aList[start:stop:step]. The start argument and the step argument both default to None—the only required argument is stop. Did you notice this is similar to how range was used to define lists A and B? This is because the slice object represents the set of indices specified by range(start, stop, step).

As you can see, defining only stop returns one element. Since the start defaults to none, this translates into retrieving only one element.

It is important to note, the first element is index 0, not index 1. This is why we are using 2 lists for this exercise. List A's elements are numbered according to the ordinal position (the first element is 1, the second element is 2, etc.) while List B's elements are the numbers that would be used to index them ([0] for the first element, 0, etc.).

With extended indexing syntax, we retrieve a range of values. For example, all values are retrieved with a colon.

A[:]

To retrieve a subset of elements, the start and stop positions need to be defined.

Given the pattern aList[start:stop], retrieve the first two elements from List A.




It is easy to understand if we could relate slicing to range, which gives the indexes. We can categorize slicing into the following two categories:


1. No step or step > 0. For example, [i:j] or [i:j:k] (k>0)

Suppose the sequence is s=[1,2,3,4,5].

  • if 0<i<len(s) and 0<j<len(s), then [i:j:k] -> range(i,j,k)

For example, [0:3:2] -> range(0,3,2) -> 0, 2

  • if i>len(s) or j>len(s), then i=len(s) or j=len(s)

For example, [0:100:2] -> range(0,len(s),2) -> range(0,5,2) -> 0, 2, 4

  • if i<0 or j<0, then i=max(0,len(s)+i) or j=max(0,len(s)+j)

For example, [0:-3:2] -> range(0,len(s)-3,2) -> range(0,2,2) -> 0

For another example, [0:-1:2] -> range(0,len(s)-1,2) -> range(0,4,2) -> 0, 2

  • if i is not specified, then i=0

For example, [:4:2] -> range(0,4,2) -> range(0,4,2) -> 0, 2

  • if j is not specified, then j=len(s)

For example, [0::2] -> range(0,len(s),2) -> range(0,5,2) -> 0, 2, 4


2. Step < 0. For example, [i:j:k] (k<0)

Suppose the sequence is s=[1,2,3,4,5].

  • if 0<i<len(s) and 0<j<len(s), then [i:j:k] -> range(i,j,k)

For example, [5:0:-2] -> range(5,0,-2) -> 5, 3, 1

  • if i>len(s) or j>len(s), then i=len(s)-1 or j=len(s)-1

For example, [100:0:-2] -> range(len(s)-1,0,-2) -> range(4,0,-2) -> 4, 2

  • if i<0 or j<0, then i=max(-1,len(s)+i) or j=max(-1,len(s)+j)

For example, [-2:-10:-2] -> range(len(s)-2,-1,-2) -> range(3,-1,-2) -> 3, 1

  • if i is not specified, then i=len(s)-1

For example, [:0:-2] -> range(len(s)-1,0,-2) -> range(4,0,-2) -> 4, 2

  • if j is not specified, then j=-1

For example, [2::-2] -> range(2,-1,-2) -> 2, 0

For another example, [::-1] -> range(len(s)-1,-1,-1) -> range(4,-1,-1) -> 4, 3, 2, 1, 0


In summary

enter image description here




I don't think that the Python tutorial diagram (cited in various other answers) is good as this suggestion works for positive stride, but does not for a negative stride.

This is the diagram:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6
-6  -5  -4  -3  -2  -1

From the diagram, I expect a[-4,-6,-1] to be yP but it is ty.

>>> a = "Python"
>>> a[2:4:1] # as expected
'th'
>>> a[-4:-6:-1] # off by 1
'ty'

What always work is to think in characters or slots and use indexing as a half-open interval -- right-open if positive stride, left-open if negative stride.

This way, I can think of a[-4:-6:-1] as a(-6,-4] in interval terminology.

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
   0   1   2   3   4   5  
  -6  -5  -4  -3  -2  -1

 +---+---+---+---+---+---+---+---+---+---+---+---+
 | P | y | t | h | o | n | P | y | t | h | o | n |
 +---+---+---+---+---+---+---+---+---+---+---+---+
  -6  -5  -4  -3  -2  -1   0   1   2   3   4   5  



I got a little frustrated in not finding an online source, or Python documentation that describes precisely what slicing does.

I took Aaron Hall's suggestion, read the relevant parts of the CPython source code, and wrote some Python code that performs slicing similarly to how it's done in CPython. I've tested my code in Python 3 on millions of random tests on integer lists.

You may find the references in my code to the relevant functions in CPython helpful.

def slicer(x, start=None, stop=None, step=None):
    """ Return the result of slicing list x.  

    See the part of list_subscript() in listobject.c that pertains 
    to when the indexing item is a PySliceObject.
    """

    # Handle slicing index values of None, and a step value of 0.
    # See PySlice_Unpack() in sliceobject.c, which
    # extracts start, stop, step from a PySliceObject.
    maxint = 10000000       # A hack to simulate PY_SSIZE_T_MAX
    if step is None:
        step = 1
    elif step == 0:
        raise ValueError('slice step cannot be zero')

    if start is None:
        start = maxint if step < 0 else 0
    if stop is None:
        stop = -maxint if step < 0 else maxint

    # Handle negative slice indexes and bad slice indexes.
    # Compute number of elements in the slice as slice_length.
    # See PySlice_AdjustIndices() in sliceobject.c
    length = len(x)
    slice_length = 0

    if start < 0:
        start += length
        if start < 0:
            start = -1 if step < 0 else 0
    elif start >= length:
        start = length - 1 if step < 0 else length

    if stop < 0:
        stop += length
        if stop < 0:
            stop = -1 if step < 0 else 0
    elif stop > length:
        stop = length - 1 if step < 0 else length

    if step < 0:
        if stop < start:
            slice_length = (start - stop - 1) // (-step) + 1
    else:
        if start < stop:
            slice_length = (stop - start - 1) // step + 1

    # Cases of step = 1 and step != 1 are treated separately
    if slice_length <= 0:
        return []
    elif step == 1:
        # See list_slice() in listobject.c
        result = []
        for i in range(stop - start):
            result.append(x[i+start])
        return result
    else:
        result = []
        cur = start
        for i in range(slice_length):
            result.append(x[cur])
            cur += step
        return result



I personally think about it like a for loop:

a[start:end:step]
# for(i = start; i < end; i += step)

Also, note that negative values for start and end are relative to the end of the list and computed in the example above by given_index + a.shape[0].




The important idea to remember about indices of a sequence is that

  • nonnegative indices begin at the first item in the sequence;
  • negative indices begin at the last item in the sequence (so only apply to finite sequences).

In other words, negative indices are shifted right by the length of the sequence:

              0   1   2   3   4   5   6   7   ...
            -------------------------
            | a | b | c | d | e | f |
            -------------------------
...  -8  -7  -6  -5  -4  -3  -2  -1

With that in mind, subscription and slicing are straightforward.

Subscription

Subscription uses the following syntax:*

sequence[index]

Subscription selects a single item in the sequence at index:

>>> 'abcdef'[0]
'a'
>>> 'abcdef'[-6]
'a'

Subscription raises an IndexError if index is out of range:

>>> 'abcdef'[100]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range

Slicing

Slicing uses the following syntax:**

sequence[start:stop:step]

Slicing selects a range of items in the sequence, beginning at start inclusive and ending at stop exclusive:

>>> 'abcdef'[0:2:1]
'ab'
>>> 'abcdef'[0:-4:1]
'ab'
>>> 'abcdef'[-6:-4:1]
'ab'
>>> 'abcdef'[-6:2:1]
'ab'
>>> 'abcdef'[1:-7:-1]
'ba'
>>> 'abcdef'[-5:-7:-1]
'ba'

Slicing defaults to the fullest range of items in the sequence, so it uses the following default values if start, stop, or step is omitted or None:***

  • step defaults to 1;
  • if step is positive
    • start defaults to 0 (first item index),
    • stop defaults to start + len(sequence) (last item index plus one);
  • if step is negative
    • start defaults to -1 (last item index),
    • stop defaults to start - len(sequence) (first item index minus one).
>>> 'abcdef'[0:6:1]
'abcdef'
>>> 'abcdef'[::]
'abcdef'
>>> 'abcdef'[-1:-7:-1]
'fedcba'
>>> 'abcdef'[::-1]
'fedcba'

Slicing raises a ValueError if step is 0:

>>> 'abcdef'[::0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: slice step cannot be zero

Slicing does not raise an IndexError if start or stop is out of range (contrary to subscription):

>>> 'abcdef'[-100:100]
'abcdef'

* The expressions sequence[index] and sequence.__getitem__(index) are equivalent.

** The expressions sequence[start:stop:step], sequence[slice(start, stop, step)], and sequence.__getitem__(slice(start, stop, step)) are equivalent, where the built-in class slice instance packs start, stop, and step.

*** The expressions sequence[:], sequence[::], and sequence[None:None:None] use default values for start, stop, and step.




There are lots of answers already, but I wanted to add a performance comparison:

python3.8 -m timeit -s 'fun = "this is fun;slicer = slice(0, 3)"' "fun_slice = fun[slicer]"
10000000 loops, best of 5: 29.8 nsec per loop

python3.8 -m timeit -s 'fun = "this is fun"' "fun_slice = fun[0:3]"
10000000 loops, best of 5: 37.9 nsec per loop

python3.8 -m timeit -s 'fun = "this is fun"' "fun_slice = fun[slice(0, 3)]"
5000000 loops, best of 5: 68.7 nsec per loop

python3.8 -m timeit -s 'fun = "this is fun"' "slicer = slice(0, 3)"
5000000 loops, best of 5: 42.8 nsec per loop

So, if you are using the same slice repeatedly, it would be beneficial and improve readability to use a slice object. However, if you are slicing only a handful of times, the [:] notation should be preferred.




Here's a simple mnemonic for remembering how it works:

  • S L *I* C *E*
  • the 'i' of slice comes first and stands for inclusive,
  • the 'e' comes last and stands for exclusive.

So array[j:k] will include the jth element and exclude the kth element.




You can use slice syntax to return a sequence of characters.

Specify a start and end index, separated by colons, to return part of the string.

Example:

Get the characters from position 2 to position 5 (not included):

b = "Hello, World!"
print(b[2:5])

Slice From the Start

By omitting the starting index, the range will start from the first character:

Example:

Get the characters from the start to position 5 (not included):

b = "Hello, World!"
print(b[:5])

Slice To the End

By omitting the end index, the range will end:

Example:

Get the characters from position 2, and all the way to the end:

b = "Hello, World!"
print(b[2:])

Negative Indexing

Use a negative index to start slicing from the end of the string: example.

Get the characters:

From: "o" in "World!" (position -5)

To, but not included: "d" in "World!" (position -2):

b = "Hello, World!"
print(b[-5:-2])



For simple way and simple understandable:

In Python, the slice notation a[start:stop:step] can be used to select a range of elements from a sequence (such as a list, tuple, or string).

The start index is the first element that is included in the slice,

The stop index is the first element that is excluded from the slice, and last one

The step value is the number of indices between slice elements.

For example, consider the following list:

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

If we want to select all elements of a, we can use the slice notation a[:]:

>>> a[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

If we want to select all elements of a, but skip every other element, we can use the slice notation a[::2]:

>>> a[::2]
[0, 2, 4, 6, 8]

If we want to select all elements from the third element (index 2) to the seventh element (index 6), we can use the slice notation a[2:7]:

>>> a[2:7]
[2, 3, 4, 5, 6]

If we want to select all elements from the third element (index 2) to the seventh element (index 6), but skip every other element, we can use the slice notation a[2:7:2]:

>>> a[2:7:2]
[2, 4, 6]

If we want to select all elements from the third element (index 2) to the end of the list, we can use the slice notation a[2:]:

>>> a[2:]
[2, 3, 4, 5, 6, 7, 8, 9]

If we want to select all elements from the beginning of the list to the seventh element (index 6), we can use the slice notation a[:7]:

>>> a[:7]
[0, 1, 2, 3, 4, 5, 6]

If you want to learn more about slice notation, you can refer to the official Python documentation: Link 1 Link 2




The rules of slicing are as follows:

[lower bound : upper bound : step size]

I- Convert upper bound and lower bound into common signs.

II- Then check if the step size is a positive or a negative value.

(i) If the step size is a positive value, upper bound should be greater than lower bound, otherwise empty string is printed. For example:

s="Welcome"
s1=s[0:3:1]
print(s1)

The output:

Wel

However if we run the following code:

s="Welcome"
s1=s[3:0:1]
print(s1)

It will return an empty string.

(ii) If the step size if a negative value, upper bound should be lesser than lower bound, otherwise empty string will be printed. For example:

s="Welcome"
s1=s[3:0:-1]
print(s1)

The output:

cle

But if we run the following code:

s="Welcome"
s1=s[0:5:-1]
print(s1)

The output will be an empty string.

Thus in the code:

str = 'abcd'
l = len(str)
str2 = str[l-1:0:-1]    #str[3:0:-1] 
print(str2)
str2 = str[l-1:-1:-1]    #str[3:-1:-1]
print(str2)

In the first str2=str[l-1:0:-1], the upper bound is lesser than the lower bound, thus dcb is printed.

However in str2=str[l-1:-1:-1], the upper bound is not less than the lower bound (upon converting lower bound into negative value which is -1: since index of last element is -1 as well as 3).


반응형