일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 구현
- 그래프 이론
- error:0308010C:digital envelope routines::unsupported
- npm start
- bfs
- Container vs VM
- 너비 우선 탐색
- 모듈러 연산 분배법칙
- 정처기 필기
- 클래스
- 나는 바보야...
- 최장공통부분문자열
- 깊이 우선 탐색
- 수학
- LCS 알고리즘
- 냅색 알고리즘
- 동적 계획법
- dfs
- 일단 시도
- 배낭 문제
- 최장공통부분수열
- 파이썬
- 다이나믹 프로그래밍
- 그래프 탐색
- 문자열
- Python
- lazy evaluation
- 그래프탐색
- db replication
- Docker 원리
- Today
- Total
Save my data
[Python][이게 왜 안돼?] * (애스터리스크), map, zip 본문
[Python][이게 왜 안돼?] yield 키워드와 Generator
예전에 회사에서 만들었던 프로젝트를 재구성하다가 몰랐던걸 발견해서 기록함.OpenCV로 캠영상을 읽은 뒤 웹(Flask)으로 스트리밍하는 모듈을 만드는 중이었다.캠을 읽는 단위는 클래스 단위로
mhd329.tistory.com
위 포스팅이랑 이어지는 글임.
멀티쓰레드 작업을 하려고 함수 하나 만들었다.
처음에는 이렇게 작성했음.
def multi_thread(method: str, cams_number: int, test: bool) -> None:
def run(args: tuple):
method, thread_number, test = args
cam = Cam(thread_number)
cam.run(method, test)
with ThreadPoolExecutor(max_workers=cams_number) as excutor:
[*excutor.map(run, ((method, i, test) for i in range(0, cams_number)))]
cv2.destroyAllWindows()
왜냐하면 `excutor.map`의 첫 번째로 들어가는 함수 인자는 하나의 인자만 받을 수 있는걸로 알고있었기 때문이다.
근데 여러 인자를 동시에 쓰고 싶었기 때문에 해당 인자들을 튜플로 묶어서 전달해줬다.
잘 작동했고 아무 문제 없었다.
근데 갑자기 이런 호기심이 들었다. 인자가 tuple[tuple] 형태로 전달되는데, 그걸 언패킹하면 어떻게 되지?
그러니까 원래 tuple[tuple] 구조였고 tuple 내부에서 각 tuple을 선택해서 줘야 되는건데, 그게 아니라 껍데기가 까진 상태에서 어떻게 되는지가 갑자기 궁금해졌다.
우선 아래와 같이 껍데기를 씌우지 않은 상태를 가정하고 그냥 줘봤다.
from concurrent.futures import ThreadPoolExecutor
def add(a, b):
return a + b
list1 = [1, 2, 3, 4]
list2 = [10, 20, 30, 40]
with ThreadPoolExecutor() as executor:
results = executor.map(add, list1, list2)
print(list(results))
>>> [11, 22, 33, 44]
이 경우는 잘 되고 있었다.
내 경우는 `((method, i, test), (method, i, test))` 같은 모습이니까, `*args` 하면 `(method, i, test), (method, i, test)` 처럼 겉껍데기가 없어진 모습일 것이다. 그러면 `*args` 했을 때,
[*excutor.map(run, (method, 0, test), (method, 1, test)] 같은 모습이 되겠다고 예상할 수 있다.
그리고 위의 예시 코드의 결과를 바탕으로, 주어진 이터러블 객체들의 같은 인덱스에 있는것 끼리 묶어서 연산할 것이라고 예측해볼 수 있다. 위에서는 (1, 10), (2, 20) ... 과 같이 연산되었으니까...
그것을 염두해두고 아래 코드를 실행해봤다.
def multi_thread(method: str, cams_number: int, test: bool) -> None:
def run(args: tuple[str, int, bool]) -> None:
method, thread_number, test = args
cam = Cam(thread_number)
cam.run(method, test)
with ThreadPoolExecutor(max_workers=cams_number) as excutor:
args = ((method, i, test) for i in range(0, cams_number))
[*excutor.map(run, *args)]
cv2.destroyAllWindows()
>>> TypeError: multi_thread.<locals>.run() takes 1 positional argument but 2 were given
뭔가 예상한대로 에러가 나긴 했다. 함수에서는 tuple 타입의 매개변수 하나만 받기로 했는데 두 개가 주어졌다고 한다.
출력해보면 다음과 같다.
def multi_thread(method: str, cams_number: int, test: bool) -> None:
def run(*args) -> None:
print(args)
cam = Cam(thread_number)
cam.run(method, test)
with ThreadPoolExecutor(max_workers=cams_number) as excutor:
args = ((method, i, test) for i in range(0, cams_number))
[*excutor.map(run, *args)]
cv2.destroyAllWindows()
>>> ('mt', 'mt')
>>> (0, 1)
>>> Traceback (most recent call last):
...
이것으로 map의 동작 방식을 알 수 있다.
`[*excutor.map(run, (method, 0, test), (method, 1, test)]` 처럼 동작하는데, 내부적으로는 zip()을 호출하여 각 요소의 같은 위치끼리 묶어서 함수에 전달한다.
바꿔서 쓰면 아래와 같다.
for args in zip(("mt", 0, True), ("mt", 1, True)):
run(*args)
즉, zip(("mt", 0, True), ("mt", 1, True))의 결과는 아래와 같이 되고,
[
("mt", "mt"),
(0, 1),
(True, True)
]
저것들이 run 함수에 전달되면서 아래와 같은 에러가 발생했던 것이다.
>>> ('mt', 'mt')
>>> (0, 1)
>>> Traceback (most recent call last):
...
'개인공부 > Python' 카테고리의 다른 글
[Python][이게 왜 안돼?] yield 키워드와 Generator (0) | 2025.02.24 |
---|---|
Python : map()에 대한 공부 이것저것. (0) | 2024.04.24 |
Flask에서 Jsonify를 굳이 쓰는 이유? (0) | 2023.09.20 |
[Django] set_cookie 메서드로 토큰 설정했는데 개발자도구 application의 쿠키에는 저장되지 않는 현상 (0) | 2023.05.31 |
self ? (파이썬) (0) | 2023.03.09 |