파이썬 코딩 도장 베타 : 48 . 8 asyncio 사용 하 기 
 Unit 48 . 부록 / ► 
 48 . 8 asyncio 사용 하 기 
 ◀ 48 . 7 메타 클래스 사용 하 기 48 . 9 가상 환경 사용 하 기 ▶ ︎ 
 asyncio ( Asynchronous I / O ) 는 비동기 프로그래밍 을 위한 모듈 이 며 CPU 작업 과 I / O 를 병렬 로 처리 하 게 해줍니다 . 
 먼저 asyncio 를 사용 하 려면 다음 과 같이 async def 로 네이티브 코 루틴 을 만듭니다 ( 파이썬 에서 는 제네 레이터 기반 의 코 루틴 과 구분 하 기 위해 async def 로 만든 코 루틴 은 네이티브 코 루틴 이 라고 합니다 ) . 
 async def 키워드 는 파이썬 3 . 5 이상 부터 사용 가능 
 async def 함수 이름 ( ) : 
 asyncio _ async _ def . py 
 async def hello ( ) :     # async def 로 네이티브 코 루틴 을 만 듦 
 loop . run _ until _ complete ( hello ( ) )     # hello 가 끝 날 때 까지 기다림 
 loop . close ( )                          # 이벤트 루프 를 닫 음 
 실행 을 해 보 면 ' Hello , world ! ' 가 출력 됩니다 . 먼저 async def 로 hello 를 만듭니다 . 그 다음 에 asyncio . get _ event _ loop 함수 로 이벤트 루프 를 얻 고 loop . run _ until _ complete ( hello ( ) ) 와 같이 run _ until _ complete 에 코 루틴 객체 를 넣 습니다 ( 네이티브 코 루틴 을 호출 하 면 코 루틴 객체 가 생성 됨 ) . 
 이벤트 루프 . run _ until _ complete ( 코 루틴 객체 또는 퓨처 객체 ) 
 run _ until _ complete 는 네이티브 코 루틴 이 이벤트 루프 에서 실행 되 도록 예약 하 고 , 해당 네이티브 코 루틴 이 끝날 때 까지 기다립니다 . 이렇게 하 면 이벤트 루프 를 통해서 hello 코 루틴 이 실행 됩니다 . 할 일 이 끝났으면 loop . close 로 이벤트 루프 를 닫 아 줍니다 . 
 이번 에 는 await 로 네이티브 코 루틴 을 실행 하 는 방법 입니다 . 다음 과 같이 await 뒤 에 코 루틴 객체 , 퓨처 객체 , 태 스 크 객체 를 지정 하 면 해당 객체 가 끝 날 때 까지 기다린 뒤 결과 를 반환 합니다 . await 라는 단어 뜻 그대로 특정 객체 가 끝 날 때 까지 기다립니다 . 
 await 키워드 는 파이썬 3 . 5 이상 부터 사용 가능 , 3 . 4 에서 는 yield from 을 사용 
 여기 서 주의 할 점 이 있 는데 await 는 네이티브 코 루틴 안 에서 만 사용 할 수 있 습니다 . 
 그럼 두 수 를 더 하 는 네이티브 코 루틴 을 만들 고 코 루틴 에서 1 초 대기 한 뒤 결과 를 반환 해 보 겠 습니다 . 
 asyncio _ await . py 
 await asyncio . sleep ( 1 . 0 )     # 1 초 대기 . asyncio . sleep 도 네이티브 코 루틴 
 return a + b     # 두 수 를 더 한 결과 반환 
 async def print _ add ( a , b ) : 
 loop . run _ until _ complete ( print _ add ( 1 , 2 ) )     # print _ add 가 끝 날 때 까지 이벤트 루프 를 실행 
 loop . close ( )                                  # 이벤트 루프 를 닫 음 
 add : 1 + 2 
 add 에서 는 await asyncio . sleep ( 1 . 0 ) 로 1 초 대기 한 뒤 return a + b 로 두 수 를 더 한 결과 를 반환 합니다 . 사실 await asyncio . sleep ( 1 . 0 ) 은 없 어도 되 지만 코 루틴 이 비동기 로 실행 되 는 모습 을 확인 하 기 위해 사용 했 습니다 . 특히 asyncio . sleep 도 네이티브 코 루틴 이 라 await 를 사용 해야 합니다 . 
 참고 | 파이썬 3 . 5 이하 에서 asyncio 사용 하 기 
 async def 와 await 는 파이썬 3 . 5 에서 추가 되 었 습니다 . 따라서 그 이하 버전 에서 는 사용 할 수 없 습니다 . 파이썬 3 . 4 에서 는 다음 과 같이 @ asyncio . coroutine 데코레이터 로 네이티브 코 루틴 을 만듭니다 . 
 파이썬 3 . 4 에서 는 await 가 아닌 yield from 을 사용 합니다 . 
 파이썬 3 . 3 에서 asyncio 는 pip install asyncio 로 asyncio 를 설치 한 뒤 @ asyncio . coroutine 데코레이터 와 yield from 을 사용 하 면 됩니다 . 단 , 3 . 3 이하 버전 에서 는 asyncio 를 지원 하 지 않 습니다 . 
 이번 에 는 asyncio 를 사용 하 여 비동기 로 웹 페이지 를 가져와 보 겠 습니다 . 먼저 다음 과 같이 asyncio 를 사용 하 지 않 고 urllib . request 의 urlopen 으로 웹 페이지 를 가져온 뒤 웹 페이지 의 길이 를 출력 해 봅니다 . 
 for i in [ ' apple ', ' pear ', ' grape ', ' pineapple ', ' orange ', ' strawberry ']] 
 result . append ( len ( page ) ) 
 [ 89590 , 88723 , 88802 , 90142 , 90628 , 92663 ] 
 실행 시간 : 8 . 422 초 
 실행 을 해 보 면 웹 페이지 의 크기 가 출력 되 고 실행 시간 은 약 8 초 가 걸립니다 ( 웹 페이지 크기 는 매번 달라질 수 있 고 , 실행 시간 은 컴퓨터 마다 달라질 수 있 습니다 ) . 
 여기 서 는 urls 에 저장 된 순서 대로 주소 에 접근 해서 웹 페이지 를 가져오 도록 만들 었 습니다 . 이렇게 하 면 웹 페이지 하나 를 완전히 가져온 뒤 에 다음 웹 페이지 를 가져와야 해서 비 효율 적 입니다 . 
 그럼 asyncio 를 사용 해서 비동기 로 실행 해 보 겠 습니다 . 
 asyncio _ urlopen . py 
 # 태 스 크 ( 퓨처 ) 객체 를 리스트 로 만 듦 
 loop . run _ until _ complete ( main ( ) )           # main 이 끝날 때 까지 기다림 
 loop . close ( )                                 # 이벤트 루프 를 닫 음 
 [ 89556 , 88682 , 89925 , 90164 , 90513 , 93965 ] 
 실행 시간 : 1 . 737 초 
 asyncio 를 사용 하 니 실행 시간 이 8 초대 에서 1 초대 로 줄 었 습니다 . 
 urlopen 이나 response . read 같 은 함수 ( 메 서드 ) 는 결과 가 나올 때 까지 코드 실행 이 중단 ( block ) 되 는데 이런 함수 들 을 블로킹 I / O ( blocking I / O ) 함수 라고 부릅니다 . 특히 네이티브 코 루틴 안 에서 블로킹 I / O 함수 를 실행 하 려면 이벤트 루프 의 run _ in _ executor 함수 를 사용 하 여 다른 스레드 에서 병렬 로 실행 시켜야 합니다 . 
 run _ in _ executor 의 첫 번 째 인수 는 executor 인데 함수 를 실행 시켜 줄 스레드 풀 또는 프로세스 풀 입니다 . 여기 서 는 None 을 넣 어서 기본 스레드 풀 을 사용 합니다 . 그리고 두 번 째 인수 에 는 실행 할 함수 를 넣 고 세 번 째 인수 부터 는 실행 할 함수 에 들어갈 인수 를 차례 대로 넣 어 줍니다 . 
 이벤트 루프 . run _ in _ executor ( None , 함수 , 인수 1 , 인수 2 , 인수 3 ) 
 run _ in _ executor 도 네이티브 코 루틴 이 므로 await 로 실행 한 뒤 결과 를 가져옵니다 . 
 main 에서 는 네이티브 코 루틴 여러 개 를 동시 에 실행 하 는데 , 이때 는 먼저 asyncio . ensure _ future 함수 를 사용 하 여 태 스 크 ( asyncio . Task ) 객체 를 생성 하 고 리스트 로 만들 어 줍니다 . 
 그 다음 에 태 스 크 리스트 를 asyncio . gather 함수 에 넣 어 줍니다 . asyncio . gather 는 모든 코 루틴 객체 ( 퓨처 , 태 스 크 객체 ) 가 끝 날 때 까지 기다린 뒤 결과 ( 반환 값 ) 를 리스트 로 반환 합니다 . 
 asyncio . gather 는 리스트 가 아닌 위치 인수 로 객체 를 받 으므로 태 스 크 객체 를 리스트 로 만들 었 다면 asyncio . gather ( * futures ) 와 같이 리스트 를 언 패킹 해서 넣 어 줍니다 . 또한 , asyncio . gather 도 코 루틴 이 므로 await 로 실행 한 뒤 결과 를 가져옵니다 . 
 참고 로 asyncio . gather 에 퓨처 객체 를 넣 은 순서 와 결과 리스트 에서 요소 의 순서 는 일치 하 지 않 을 수 도 있 습니다 . 
 웹 페이지 를 순서 대로 가져올 때 와 asyncio 를 사용 하 여 비동기 로 가져올 때 를 비교 해 보 면 다음 과 같 은 모양 이 됩니다 ( 비동기 부분 은 간략 화 한 개념 도 이 며 실제 실행 과정 은 상당히 복잡 합니다 ) . 
 ▼ 그림 48 - 2 웹 페이지 를 순서 대로 가져올 때 와 비동기 로 가져올 때 
 참고 | run _ in _ executor 에 키워드 인수 를 사용 하 는 함수 넣 기 
 run _ in _ executor 같 은 함수 는 위치 인수 만 넣 을 수 있 는데 파이썬 에서 는 키워드 인수 를 많이 사용 합니다 . run _ in _ executor 에 키워드 인수 를 사용 하 는 함수 를 넣 을 때 는 functools . partial 을 사용 해야 합니다 . functools . partial 은 이름 그대로 부분 함수 를 만들 어 주 는 기능 입니다 . 
 functools . partial ( 함수 , 위치 인수 , 키워드 인수 ) 
 async def hello ( executor ) : 
 functools . partial 은 인수 가 포함 된 부분 함수 를 반환 하 는데 , 반환 된 함수 에 다시 인수 를 지정 해서 호출 할 수 있 습니다 . 
 >>> hello ( )                                                                                      # 포함 된 함수 생성 
 Hello - Python - Script 
 이번 에 는 async with 과 async for 문법 을 사용 하 는 방법 입니다 . 먼저 async with 은 클래스 나 함수 를 비동기 로 처리 한 뒤 결과 를 반환 하 는 문법 입니다 . 그리고 async for 는 비동기 로 반복 하 는 문법 입니다 
 async with 은 파이썬 3 . 5 이상 부터 사용 가능 
 async with 은 with 다음 에 클래스 의 인 스턴스 를 지정 하 고 as 뒤 에 결과 를 저장 할 변수 를 지정 합니다 . 
 async with 클래스 ( ) as 변수 : 
 async with 으로 동작 하 는 클래스 를 만들 려면 __ aenter __ 와 __ aexit __ 메서 드 를 구현 해야 합니다 ( asynchronous enter , asynchronous exit 라는 뜻 ) . 그리고 메서 드 를 만들 때 는 반드시 async def 를 사용 합니다 . 
 그럼 1 초 뒤 에 덧셈 결과 를 반환 하 는 클래스 를 만들 어 보 겠 습니다 . 
 asyncio _ async _ with . py 
 class AsyncAdd : 
 return self . a + self . b     # __ aenter __ 에서 값 을 반환 하 면 as 에 지정 한 변수 에 들어감 
 async with AsyncAdd ( 1 , 2 ) as result :     # async with 에 클래스 의 인 스턴스 지정 
 print ( result )     # 3 
 __ aenter __ 메 서드 에서 1 초 대기 한 뒤 self . a 와 self . b 를 더 한 결과 를 반환 하 도록 만듭니다 . 이렇게 __ aenter __ 에서 값 을 반환 하 면 as 에 지정 한 변수 에 들어갑니다 . __ aexit __ 메 서 드 는 async with as 를 완전히 벗어나 면 호출 되 는데 여기 서 는 특별히 만들 부분 이 없 으므로 pass 를 넣 습니다 ( 메 서드 자체 가 없 으면 에러 가 발생 합니다 ) . 
 이번 에 는 async for 입니다 . async for 로 동작 하 는 클래스 를 만들 려면 __ aiter __ 와 __ anext __ 메서 드 를 구현 해야 합니다 ( asynchronous iter , asynchronous next 라는 뜻 ) . 그리고 메서 드 를 만들 때 는 반드시 async def 를 사용 합니다 . 
 async for 는 파이썬 3 . 5 이상 부터 사용 가능 
 다음 은 1 초 마다 숫자 를 생성 하 는 비동기 이터 레이터 입니다 . 
 async _ for . py 
 class AsyncCounter : 
 if self . current < self . stop : 
 async for i in AsyncCounter ( 3 ) :     # for 앞 에 async 를 붙임 
 메서 드가 __ anext __, __ aiter __ 라는 점 만 다를 뿐 일반 적 인 이터 레이터 와 만드 는 방법 과 같 습니다 . 반복 을 끝낼 때 는 StopAsyncIteration 예외 를 발생 시키 면 됩니다 . 물론 네이티브 코 루틴 을 사용 할 때 는 앞 에 await 를 붙입니다 . 비동기 이터 레이터 를 다 만들 었 다면 네이티브 코 루틴 안 에서 async for i in AsyncCounter ( 3 ) : 과 같이 async for 에 사용 하 면 됩니다 . 
 참고 | 제네 레이터 방식 으로 비동기 이터 레이터 만들 기 
 yield 를 사용 하 여 제네 레이터 방식 으로 비동기 이터 레이터 를 만들 수 도 있 습니다 . 다음 과 같이 async def 로 네이티브 코 루틴 을 만들 고 yield 를 사용 하 여 값 을 바깥 으로 전달 하 면 됩니다 . 
 파이썬 3 . 6 이상 부터 사용 가능 
 async _ for _ yield . py 
 async def async _ counter ( stop ) :     # 제네 레이터 방식 으로 만들 기 
 async for i in async _ counter ( 3 ) :     # for 앞 에 async 를 붙임 
 실행 을 해 보 면 1 초 간격 으로 0 , 1 , 2 가 출력 됩니다 . 
 참고 | 비동기 표현식 
 async 와 await 를 사용 하 면 리스트 , 딕셔너리 , 세트 , 제네 레이터 표현식 을 비동기 표현식 으로 만들 수 있 습니다 
 < 버전 > 비동기 표현식 은 파이썬 3 . 6 이상 부터 사용 가능 </ 버전 > 
 리스트 : [ 변수 async for 변수 in 비동기 이터 레이터 ( ) ] 
 제네 레이터 : ( 변수 async for 변수 in 비동기 이터 레이터 ( ) ) 
 print ( a )     # [ 0 , 1 , 2 ] 
 다음 과 같이 표현식 안 에서 await 로 코 루틴 을 실행 할 수 도 있 습니다 . 여기 서 는 리스트 표현식 을 예 로 들 었 지만 딕셔너리 , 세트 , 제네 레이터 표현식 안 에서 도 await 를 사용 할 수 있 습니다 . 
 [ await 코 루틴 함수 ( ) for 코 루틴 함수 in 코 루틴 함수 리스트 ] 
 print ( a )     # [ 1 , 1 , 1 ] 
 48 . 1 비트 연산자 사용 하 기 
 48 . 2 연산자 우선 순위 
 48 . 3 내장 함수 
 48 . 4 이스케이프 시퀀스 
 48 . 5 실수 값 의 오차 
 48 . 6 with as 에 사용 할 수 있 는 클래스 만들 기 
 48 . 7 메타 클래스 사용 하 기 
 48 . 9 가상 환경 사용 하 기 
 48 . 10 window 에서 패키지 설치 에러 해결 하 기 
 48 . 11 PyCharm 사용 하 기