2015-02-02 6 views
26

을 사용하여 값을 추출하고 StopIteration을 생성했기 때문에 오늘 버그가 발생했습니다.next()는 파이썬에서 any/all과 잘 맞지 않습니다.

프로그램을 중단 할

일반적으로,하지만 next를 사용하여 함수가 all() 반복 내에서 호출되고 있었다, 그래서 all 단지 조기 종료 및 True를 반환했습니다.

이것은 예상되는 동작입니까? 이런 종류의 것을 피하는 데 도움이되는 스타일 가이드가 있습니까?

간단한 예 :

def error(): return next(i for i in range(3) if i==10) 
error() # fails with StopIteration 
all(error() for i in range(2)) # returns True 
+0

같은 일이 파이썬에서 3 – khelwood

+0

@khelwood 덕분에 일이, 나는 물론 py2.7 태그 – amwinter

+9

을 제거 할 수 있습니다. [** all ** * (iterable) * iterable의 모든 요소가 true이면 true를 반환하고 iterable이 비어 있으면 true를 반환합니다. ***.] (https://docs.python.org/2/) library/functions.html # all) –

답변

24

이것은 3.6 이하의 Python 버전에서 기본 동작이지만 언어에서 실수로 간주되며 Python 3.7에서 변경되어 예외가 발생합니다. PEP 479으로

는 말한다 :

발전기의 상호 작용과 StopIteration 현재 다소 놀라운 일이며, 모호한 버그를 은폐 할 수 있습니다. 예기치 않은 예외가 미묘하게 변경된 동작을 초래해서는 안되지만 시끄럽고 쉽게 디버그 된 추적을 유발해야합니다. 현재 생성자 함수 내에서 실수로 발생한 StopIteration은 생성기를 구동하는 루프 구문에 의해 반복의 끝으로 해석됩니다.

파이썬 3.5 이상에서는 기본 동작을 3.7로 예약 된 동작으로 변경할 수 있습니다. 이 코드 : 경고가 발생,

Traceback (most recent call last): 
    File "gs_exc.py", line 8, in <genexpr> 
    all(error() for i in range(2)) 
    File "gs_exc.py", line 6, in error 
    return next(i for i in range(3) if i==10) 
StopIteration 

The above exception was the direct cause of the following exception: 

Traceback (most recent call last): 
    File "gs_exc.py", line 8, in <module> 
    all(error() for i in range(2)) 
RuntimeError: generator raised StopIteration 
파이썬 3.5에서

및없이 3.6 __future__ 수입 :

# gs_exc.py 

from __future__ import generator_stop 

def error(): 
    return next(i for i in range(3) if i==10) 

all(error() for i in range(2)) 

은 ... 다음과 같은 예외를 발생시킵니다.예를 들어 :

# gs_warn.py 

def error(): 
    return next(i for i in range(3) if i==10) 

all(error() for i in range(2)) 

$ python3.5 -Wd gs_warn.py 
gs_warn.py:6: PendingDeprecationWarning: generator '<genexpr>' raised StopIteration 
    all(error() for i in range(2)) 

$ python3.6 -Wd gs_warn.py 
gs_warn.py:6: DeprecationWarning: generator '<genexpr>' raised StopIteration 
    all(error() for i in range(2)) 
+0

감사합니다 - StopIteration에 깜짝 놀랐다 손가락이 엇갈 렸습니다. 거품이 울려서 나를 미치게 만들지 않았습니다. – amwinter

+0

'모든 (some_iterator)'와'mlist = [i for i some_iterator]; all (mlist)'는 다르게? – tdelaney

+0

@tdelaney 예,'some_iterator'가'StopIteration'을 발생시키는 생성자 라면요. –

9

문제는 all를 사용하지 않을, 그것은 당신이 all에 매개 변수로 발전기 식을 가지고 있습니다. StopIteration은 생성자 표현식으로 전달되며, 원래 생성 된 위치를 실제로 알지 못하므로 일반적인 작업을 수행하고 반복을 종료합니다.

>>> all([]) 
True 
:

def error2(): raise StopIteration 

>>> all(error2() for i in range(2)) 
True 

퍼즐의 마지막 조각이 all 빈 순서로 무엇을 알고있다 :

사용자가 직접 오류가 발생 무언가로 error 기능을 대체하여이 문제를 볼 수 있습니다

next을 직접 사용하려는 경우 직접 StopIteration을 잡을 준비가되어 있어야합니다.

편집 : 파이썬 개발자가 버그라고 생각하고 3.7에서 변경하는 단계를 수행하고 있습니다.

+0

왜 'all'을 사용하여 오류가 전파되지 않습니까? –

+1

ok - 해결책은 'StopIteration'을 발생시킬 수있는 모든 것에 대해 '모든'/ '모두'를 사용하지 말라는 것입니다. – amwinter

+0

@PadraicCunningham 발전기 표현 자체에 의해 감지됩니다. –