2013-07-28 3 views
3

itertools 설명서의 요리법 섹션에 consume과 같은 유용한 코드가 있지만 문제를 발견했습니다.itertools.islice의 Python 버전이 작동하지 않는 이유는 무엇입니까?

from itertools import islice 
numbers = iter(range(10)) 
for i in numbers: 
    print i 
    next(islice(numbers, 3, 3), None) 

위의 코드는 next(islice(numbers, 3, 3), None) 트릭이 consume 조리법에서 가져온 것입니다, 그것은 [0,4,8]를 인쇄, 잘 작동합니다.

여기서 중요한 역할을하고있는 islice을 볼 수 있습니다. 내가 그 기능에 대한 파이썬 문서에서 제공하는 코드로 itertools.islice를 교체 할 때 내가 기대하지만, 코드가 작동하지 않습니다 :

#from itertools import islice 

def islice(iterable, *args): 
    # islice('ABCDEFG', 2) --> A B 
    # islice('ABCDEFG', 2, 4) --> C D 
    # islice('ABCDEFG', 2, None) --> C D E F G 
    # islice('ABCDEFG', 0, None, 2) --> A C E G 
    s = slice(*args) 
    it = iter(xrange(s.start or 0, s.stop or 200, s.step or 1)) 
    nexti = next(it) 
    for i, element in enumerate(iterable): 
     if i == nexti: 
      yield element 
      nexti = next(it) 


numbers = iter(range(10)) 
for i in numbers: 
    print i 
    next(islice(numbers, 3, 3), None) 

출력 위의 출력 다르다; 모두 범위의 숫자가 인쇄되고 islice() 줄을 사용하면 아무 것도 수행되지 않습니다.

아무도 이유가 설명 될 수 있습니까? 여기에 islice의 작동 방식을 설명 할 수 있습니까?

+0

오류를 제공합니까? –

+0

@MichaelVayvala : 아니요, 오류는 인쇄되지 않습니다. 출력은 다릅니다. 나는 그것을 명확하게하기 위해 질문을 편집했다. –

+0

예, 이제 알 수 있습니다. –

답변

2

슬라이스 시작 및 인자가 동일하므로 xrange에 next(it) 호출 StopIteration 제기 중지 :

>>> s = slice(3, 3, None) 
>>> it = iter(xrange(s.start or 0, s.stop or 200, s.step or 1)) 
>>> next(it) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
StopIteration 

slice() 발생기 기능을 즉시 종료한다. 이것은 논리적 인 것일 뿐이므로 xrange() 함수는이 경우 숫자를 생성하지 않습니다.

itertools 설명서는 이며 설명은이며 완전한 것은 아닙니다. 샘플 Python 코드가 C 구현과 동일한 방식으로 작동하지 않는 edgecase를 발견했습니다.

은 물론이 경우의 파이썬 코드가 작동을 만드는 StopIteration 예외를 잡을 명시 적으로 루프에서 edgecase에 대한 테스트하려면 :

def islice(iterable, *args): 
    s = slice(*args) 
    it = iter(xrange(s.start or 0, s.stop or 200, s.step or 1)) 
    try: 
     nexti = next(it) 
     consume = False 
    except StopIteration: 
     if s.start < 1: 
      return 
     nexti = s.start - 1 
     consume = True 
    for i, element in enumerate(iterable): 
     if i == nexti: 
      if consume: 
       return 
      yield element 
      nexti = next(it) 

파이썬 예제 코드는 코드를 만들기 위해 slice() 객체를 사용 더 작고 읽기 쉬운; C 코드는 자체 시작, 중지 및 단계 구문 분석을 수행하고 xrange()에 위임하지 않고 개수 자체를 처리하므로이 edgecase로 실행되지 않습니다.

+0

Thx, 귀하의 islice 버전이 작동 중입니다. 하지만 왜 당신이 수익을 사용하는지 물어봐도 될까요? 아무 것도 돌려주지 않니? None을 반환하면 next() 프로세스는 어떻게 수행 되었습니까? – michaelyin

+0

@michaelyin : 이것은 생성자 함수입니다. 'return'은 생성자를 끝내고 ('StopIteration'을 발생시킵니다) –

+0

반환 지점은 값을 반환하지 않고 생성기를 끝내는 것입니다. 어쨌든 당신은 할 수 없습니다. 생성기 함수는 'yield'로 결과를 생성합니다. –