yield
좋은 아이디어 와 같은 생성기 언어 기능이 있습니까?
나는 파이썬 관점에서 이것에 대답하고 싶습니다. 그렇습니다 . 좋은 생각 입니다.
먼저 귀하의 질문에 대한 몇 가지 질문과 가정을 해결 한 다음 생성기의 보급 성과 나중에 파이썬에서 불합리한 유용성을 보여 드리겠습니다.
일반 비 생성기 함수를 사용하면이를 호출 할 수 있으며 동일한 입력이 제공되면 동일한 출력을 리턴합니다. yield는 내부 상태에 따라 다른 출력을 반환합니다.
이것은 거짓입니다. 객체의 메소드는 자체 내부 상태를 가진 함수 자체로 생각할 수 있습니다. 파이썬에서는 모든 것이 객체이기 때문에 실제로 객체에서 메소드를 가져 와서 그 메소드를 전달할 수 있습니다 (이 메소드는 객체에서 온 객체에 바인딩되어 있으므로 상태를 기억합니다).
다른 예로는 의도적으로 임의의 기능뿐만 아니라 네트워크, 파일 시스템 및 터미널과 같은 입력 방법이 있습니다.
이와 같은 기능이 언어 패러다임에 어떻게 맞습니까?
언어 패러다임이 퍼스트 클래스 함수와 같은 것을 지원하고 생성기가 Iterable 프로토콜과 같은 다른 언어 기능을 지원하면 완벽하게 맞습니다.
실제로 어떤 규칙을 위반합니까?
아니요. 언어로 구워 졌기 때문에이 규칙은 기본적으로 생성되며 생성기 사용을 포함하거나 필요로합니다!
프로그래밍 언어 컴파일러 / 통역사는 이러한 기능을 구현하기 위해 모든 규칙을 위반해야합니다.
다른 기능과 마찬가지로 컴파일러는 기능을 지원하도록 설계되어야합니다. Python의 경우 함수는 이미 상태가있는 객체입니다 (예 : 기본 인수 및 함수 주석).
언어가이 기능이 작동하기 위해 멀티 스레딩을 구현해야합니까, 아니면 스레딩 기술없이 수행 할 수 있습니까?
재미있는 사실 : 기본 파이썬 구현은 스레딩을 전혀 지원하지 않습니다. 여기에는 GIL (Global Interpreter Lock) 기능이 있으므로 다른 Python 인스턴스를 실행하기 위해 두 번째 프로세스를 실행하지 않으면 실제로는 동시에 실행되는 것이 없습니다.
참고 : 예제는 Python 3에 있습니다.
수확량을 넘어서
yield
키워드는 어떤 함수에서도 키워드를 생성기로 바꿀 수 있지만 키워드를 만드는 유일한 방법은 아닙니다. 파이썬은 다른 반복자 (다른 생성기 포함)로 생성기를 명확하게 표현할 수있는 강력한 방법 인 생성기 표현식을 제공합니다.
>>> pairs = ((x,y) for x in range(10) for y in range(10) if y >= x)
>>> pairs
<generator object <genexpr> at 0x0311DC90>
>>> sum(x*y for x,y in pairs)
1155
보다시피, 구문은 깨끗하고 읽을 수있을뿐만 아니라 sum
수락 생성기 와 같은 내장 함수 입니다.
와
With 문에 대한 Python Enhancement Proposal을 확인하십시오 . 다른 언어의 With 문에서 기대하는 것과는 매우 다릅니다. 표준 라이브러리의 도움을 받아 Python의 생성기는 컨텍스트 관리자로 아름답게 작동합니다.
>>> from contextlib import contextmanager
>>> @contextmanager
def debugWith(arg):
print("preprocessing", arg)
yield arg
print("postprocessing", arg)
>>> with debugWith("foobar") as s:
print(s[::-1])
preprocessing foobar
raboof
postprocessing foobar
물론 인쇄 작업은 여기서 할 수있는 가장 지루한 작업이지만 가시적 인 결과를 보여줍니다. 더 흥미로운 옵션으로는 리소스 자동 관리 (파일 / 스트림 / 네트워크 연결 열기 및 닫기), 동시성 잠금, 일시적으로 함수 래핑 또는 교체, 데이터 압축 해제 및 재 압축이 있습니다. 함수를 호출하는 것이 코드에 코드를 삽입하는 것과 같으면 with 문은 코드의 일부를 다른 코드로 래핑하는 것과 같습니다. 그러나 그것을 사용하면 언어 구조에 쉽게 연결할 수있는 확실한 예입니다. 수율 기반 생성기는 컨텍스트 관리자를 만드는 유일한 방법은 아니지만 확실히 편리한 방법입니다.
및 일부 소진
파이썬에서 for 루프는 흥미로운 방식으로 작동합니다. 다음과 같은 형식으로되어 있습니다.
for <name> in <iterable>:
...
먼저, 호출 한 표현식 <iterable>
이 반복 가능한 객체를 얻기 위해 평가됩니다. 둘째, iterable이 __iter__
호출하고 결과 iterator가 장면 뒤에 저장됩니다. 이후 __next__
에 반복자에서 호출되어 입력 한 이름에 바인딩 할 값을 가져옵니다 <name>
. 이 단계는를 호출 할 때까지 반복 __next__
합니다 StopIteration
. for 루프가 예외를 삼키고 거기서부터 실행이 계속됩니다.
제너레이터로 돌아 오기 : 제너레이터를 호출 __iter__
하면 제너레이터 만 반환됩니다.
>>> x = (a for a in "boring generator")
>>> id(x)
51502272
>>> id(x.__iter__())
51502272
이것이 의미하는 것은 무언가에 대해 반복하는 것을 당신이하고 싶은 것과 분리하고 그 행동을 도중에 바꿀 수 있다는 것입니다. 아래에서 두 개의 루프에서 동일한 생성기가 어떻게 사용되는지 확인하고 두 번째 루프에서는 첫 번째에서 중단 된 위치에서 실행을 시작합니다.
>>> generator = (x for x in 'more boring stuff')
>>> for letter in generator:
print(ord(letter))
if letter > 'p':
break
109
111
114
>>> for letter in generator:
print(letter)
e
b
o
r
i
n
g
s
t
u
f
f
게으른 평가
목록과 비교할 때 생성기의 단점 중 하나는 생성기에서 액세스 할 수있는 유일한 항목은 생성기에서 나오는 것입니다. 이전 결과로 돌아가거나 중간 결과를 거치지 않고 나중 결과로 넘어갈 수 없습니다. 이것의 장점은 발전기가 동등한 목록에 비해 거의 메모리를 차지하지 않는다는 것입니다.
>>> import sys
>>> sys.getsizeof([x for x in range(10000)])
43816
>>> sys.getsizeof(range(10000000000))
24
>>> sys.getsizeof([x for x in range(10000000000)])
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
sys.getsizeof([x for x in range(10000000000)])
File "<pyshell#10>", line 1, in <listcomp>
sys.getsizeof([x for x in range(10000000000)])
MemoryError
발전기는 느슨하게 연결될 수도 있습니다.
logfile = open("logs.txt")
lastcolumn = (line.split()[-1] for line in logfile)
numericcolumn = (float(x) for x in lastcolumn)
print(sum(numericcolumn))
첫 번째, 두 번째 및 세 번째 줄은 각각 발전기를 정의하지만 실제 작업은 수행하지 않습니다. 마지막 행이 호출되면 sum은 numericcolumn에 값을 요청하고 numericcolumn은 lastcolumn에 값을 필요로하고 lastcolumn은 로그 파일에서 값을 요청한 다음 실제로 파일에서 행을 읽습니다. 이 스택은 sum이 첫 번째 정수를 얻을 때까지 풀립니다. 그런 다음 두 번째 줄에 대해 프로세스가 다시 발생합니다. 이 시점에서 sum에는 두 개의 정수가 있으며 함께 더합니다. 세 번째 줄은 파일에서 아직 읽지 않았습니다. 그런 다음 Sum은 numericcolumn에서 값을 요청하고 (전체적으로 체인의 나머지 부분에 대해 전혀 알 수 없음) numericcolumn이 소진 될 때까지 추가합니다.
여기서 가장 흥미로운 부분은 줄을 개별적으로 읽고 소비하고 버린다는 것입니다. 전체 파일이 한 번에 메모리에있는 것은 아닙니다. 이 로그 파일이 테라 바이트 인 경우 어떻게됩니까? 한 번에 한 줄만 읽기 때문에 작동합니다.
결론
이것은 파이썬에서 생성기의 모든 사용에 대한 완전한 검토는 아닙니다. 특히 무한 생성기, 상태 시스템, 값을 다시 전달하고 코 루틴과의 관계를 건너 뛰었습니다.
생성기를 깔끔하게 통합되고 유용한 언어 기능으로 사용할 수 있음을 입증하는 것으로 충분하다고 생각합니다.
yield
본질적으로 상태 엔진입니다. 매번 같은 결과를 반환하는 것은 아닙니다. 그것은 무엇 것이다 절대 확실 할 것은 열거에서 호출 할 때마다 다음 항목을 반환합니다. 스레드는 필요하지 않습니다. 현재 상태를 유지하려면 클로저가 필요합니다.