알 수 없는 사용자 2022. 6. 12. 20:14

참고

https://blog.humminglab.io/posts/python-coroutine-programming-1/

 

Python 비동기 프로그래밍 제대로 이해하기(1/2) - Asyncio, Coroutine

Python2 와 비교하여 python3의 가장 돋보이는 killer feature 는 비동기 프로그래밍 지원이라고 할 수 있다. 이를 위하여 python 3.4에 asyncio 모듈이 추가되었고, python 3.5 에는 native coroutine 지원을 위한 async

blog.humminglab.io

- 이 블로그 참조, 내가 더 궁금한건 더 추가해서 설명함. 

- 거의 따라 쓴 부분이 많은 정도 


목차 

  • iterator
  • yield 키워드와 generator
  • yield from
  • asyncio
  • async, await
  • future

https://blog.humminglab.io/posts/python-coroutine-programming-1/

 

Python 비동기 프로그래밍 제대로 이해하기(1/2) - Asyncio, Coroutine

Python2 와 비교하여 python3의 가장 돋보이는 killer feature 는 비동기 프로그래밍 지원이라고 할 수 있다. 이를 위하여 python 3.4에 asyncio 모듈이 추가되었고, python 3.5 에는 native coroutine 지원을 위한 async

blog.humminglab.io

상단 블로그 개요 ( 목차도 상단에 블로그를 따라서 진행 ) 

코루틴 :

- 이벤트 방식이지만 blocking 방식의 프로그래밍 처럼 sequential 하게 코드를 작성할 수 있어, 단일 thread로 수만개의 네트워크 연결을 처리하는 서버를 오류 가능성을 최소화 하면서, 보다 편하게 개발할 수 있다.

 

하지만 이 기능을 제대로 이해하려고 python 매뉴얼을 보다 보면 iterator, generator, yield, coroutine 정도 까지는 큰 어려움 없이 이해가 되나, asyncio, yield from, asyncio.coroutine, future, async, await, async with, async for, … 비슷 비슷한 단어들의 설명이나 예제를 보다보면 각 기능의 차이점들이 무엇인지, coroutine이 event loop의 callback과 어떻게 같이 사용 되는지 알쏭달쏭 해지면서 머리 속이 점점 복잡해 진다.

이 글에서는 이와 같은 모호함을 지나 어느 정도 구조를 이해할 수 있게 된 상태에서, 새로 접근하는 사람을 위하여 이해하기 쉽도록 다시 정리해 본다.

 

Python의 coroutine이 복잡한 이유는 꾸준히 기능이 확장되었기 때문이라고 생각된다. 한번에 모든 기능이 정립되었으면 깔끔했을지 모르나, iterator부터 시작해서 하나씩 살이 붙다 보니, 개념을 정확히 이해하지 못하면 다양한 keyword의 활용 예를 보고 혼동에 빠지고 만다. 가장 이해하기 좋은 방법은 위처럼 간단한 예를 직접 실행해 보고, 필요한 부분은 PEP 문서 등의 개요와 built in package source를 찾아 보는 것이다.


def == method == 함수

  • 대표적으로 iterable한 타입 - list, dict, set, str, bytes, tuple, range
  • iterable 한 자료구조는 Iterator(__iter__)를 할 수 있다.

Coroutine 직접적인 관련은 없지만, 이해를 쉽게 하기 위하여는 iterator 부터 설명을 시작하여야 한다.

1.    Iterator 생긴 이유

기존의 list 형태는 원소가 실제 메모리에 할당된다.

sum = 0
for i in range(100000):
    sum += i

100000 를 전부 할당하면 비효율적임. 근데 이렇게 iterabledata구조인 경우는 하나하나 할당하기 보단 연산에 쓰는 data만 잠깐 할당해서 쓰고 버리는 게 효율 적이다.

2.    Iterator 직접 정의해서 사용하는 법

이와 같은 생각에서 만들어 것이 iterator이다. Python에서 iterator라는 것은 __iter__() __next__() method 가진 객체를 말한다. ( iterator 형식대로 __iter__ __next__ 함수를 적절히 사용해줘야함 )

>>> a = [1, 2, 3]
>>> a_iter = iter(a)
>>> type(a_iter)
<class 'list_iterator'>
>>> next(a_iter)
1
>>> a_iter.__next__()
2
>>> a_iter.__next__()
3
>>> next(a_iter)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>
>>>
>>>
>>>
>>> d = {1:'a',2:'b',3:'c'}
>>> d_iter=iter(d)
>>> next(d_iter)
1
>>> d_iter.__next__()
2
>>> d.get(next(d_iter))
'c'
>>> next(d_iter)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

 

 

  • __iter__(): Iterator object(__next__() method 가진) 객체를 리턴
  • __next__(): 호출될 마다 다음 값을 리턴
class Counter(object):
    def __iter__(self):
        iter = Iterator()
        return iter
	
class Iterator(object):
    def __init__(self):
        self.index = 0
	
    def __next__(self):
        if self.index > 10:
            raise StopIteration
        n = self.index * 2
        self.index += 1
        return n

(__next__() method 가진)객체를 리턴하는 __iter__() method를 가진 Counter함수가 iterator 인 것이다!

next(“객체”) 를 사용하면 __next__() methodreturn 값이 나온다.

>>> c = Counter()
>>> next(c)
0
>>> next (c)
2

3.    Iterator 활용

>>> c = Counter()
>>> for i in c:
        print(i)   # 0~20까지 짝수 출력

Iterator 장점은 forloop에 활용이 된다.

4.    Iterator 추가로 설명 : iterable한(__iter__가 있는) 변수(클래쓰)의 iterator를 쓰는 방법

파이썬에 모든건 class,def 라고 있는데, 우리가 자주 쓰는 파이썬 내장 자료구조 list,tuple,dict,set class 이다.

따라서 list class 안에 코드를 파고 들어보면 우리가 위에서 했던 코드들 처럼 __next()__ 함수를 리턴하는 __iter__() 함수가 들어 있다. dir("클래스명") 하면 "클래스명"에 있는 내부함수를 볼 수 있다.

따라서 " a = ['a', 'b', 'c'] " or " num = 123 " <- 이렇게 변수를 만드는 부분들도 사실은 클래스 생성하는 것 

 

__add__일급함수 사용하는 경우

'a' 라는 클래쓰에 __add__ 라는 일급 함수가 정의 되어 있음.

따라서 "a + 200" == "a.__add__(200)" 임

자세한건 일급함수 __add__ __mul__ __sub__ 이런거 보면 알거임

c++ 오퍼레이터랑 비슷함.

 

 

__iter__ , __next__ 일급함수 사용하는 경우

5.    Iterator 추가로 설명 : dictionary에 iterator를 사용할 때 팁

dictionary 딕셔너리도 iterable 한 클래쓰이다.그래서 next로 찍어보면 key값을 순서대로 뽑아옴.value 값을 순서대로 찍어보고 싶다면 "a[key값.__next__()]' 이런식으로 뽑아오면 가능함

- for를 사용해서 응용

 

6.    Iterator 추가로 설명 : "list" 클래스를 상속받아서 사용 하는 방법 ( 'list'클래스 내부함수'index' 사용 )

코드 :

class AdvancedList(list):
    def replace(self, old, new):
        while old in self:
            self[self.index(old)] = new
            
x = AdvancedList([1, 2, 3, 1, 2, 3, 1, 2, 3])
x.replace(1, 100)
print(x)

결과 :

[100, 2, 3, 100, 2, 3, 100, 2, 3]

 

설명 :

 

7.    Iterator 추가로 설명 : __iter__() 함수랑 os,sys 라이브러리를 이용해 현재경로의 상위경로 얻기

이름@이름:~/ws/src/etri_hall_robot/constant$ python3.8
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>
>>> import os,sys
>>> path = os.path.abspath(os.path.dirname(sys.argv[0]))
>>> path_iter = path.__iter__()
>>> path_prev = ''
>>>
>>> for i in range(0,path.rfind('/')):
...     path_prev += ''.join(path_iter.__next__())
...
>>> path_prev
'/home/liam/ws/src/etri_hall_robot'
>>>