Skip to content
This page has been auto-translated and may contain errors.View in English

람다 함수와 컴프리헨션

docs.scrimba.com

이 세 기능은 공통점이 있습니다. 여러 줄이 필요한 생각을 하나의 읽기 좋은 식으로 표현할 수 있게 해줍니다. 잘 사용하면 코드가 짧고 명확해집니다. 잘못 사용하면 읽기 어려워집니다. 이 장에서는 각 기능을 언제 사용해야 하고 언제 멈춰야 하는지 다룹니다.

람다, 컴프리헨션, zip은 흔한 패턴을 식으로 압축하는 세 가지 도구입니다. 필수는 아니지만 파이썬 코드 곳곳에 나타나므로 인식하고 능숙하게 작성할 가치가 있습니다. 지도 원칙은 더 짧게 하려는 것뿐 아니라 의도를 더 명확하게 할 때만 사용하는 것입니다.

이 도구들은 모두 변환을 (값으로 계산되는 코드 조각)으로 표현하는데, 이는 단계별로 결과를 만드는 명령형 루프 대신 표현합니다. 람다는 작은 이름 없는 함수를 인라인으로 제공합니다. 컴프리헨션은 한 번에 컬렉션을 만듭니다. 제너레이터는 lazy(전체가 아닌 필요할 때 값을 생성)이므로 전체 시퀀스 대신 한 항목만 메모리에 보유합니다. zip은 여러 시퀀스를 함께 이동합니다. 효과는 "어떻게 조립하는가" 대신 "내가 원하는 것"으로 읽히는 코드입니다. 단, 한 줄이 더 이상 읽기 쉽지 않을 때까지만 유지하세요.

람다 함수

람다는 이름이 없는 한 줄짜리 함수입니다. lambda 키워드로 만듭니다. 진정한 유용성은 먼저 이름 있는 함수를 정의할 필요 없이 필요한 바로 그 자리에 인라인으로 작성할 수 있다는 것입니다. 이것이 sorted()와 함께 유용한 이유입니다.

람다는 익명의 단일 식 함수입니다. 여러 인자를 받을 수 있지만 본문은 문(statement)이 아닌 단일 식이어야 합니다. 주된 용도는 전체 def가 불필요한 복잡성만 추가하는 인라인 key= 또는 콜백 인자입니다. 더 복잡한 것은 def를 사용하세요.

람다는 def가 만드는 것과 같은 종류의 함수 객체이지만 세 가지 제한이 있습니다. 이름이 없어서 traceback(Python이 무언가 발생할 때 출력하는 에러 보고서)에 <lambda>로 표시되고, 본문이 한 식이라 문(statement)을 포함할 수 없으며, 문서화 문자열을 가질 수 없습니다. 실제 디버깅 시간이 들어가는 함정은 클로저의 late binding(함수가 주변 스코프에서 변수를 캡처하는 것)입니다. 루프 내에서 만들어진 람다는 루프 변수를 참조로 캡처하므로 모든 람다는 람다가 생성되었을 때의 값이 아니라 변수의 최종값을 봅니다:

python
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs])        # [2, 2, 2], [0, 1, 2]가 아님

funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs])        # [0, 1, 2]: i=i가 현재 값을 기본 인자로 바인딩

i=i 트릭은 현재 값을 생성 시점에 기본 인자로 고정합니다. 루프에서 호출 가능 객체를 만들 때마다 이를 사용하세요.

python
double = lambda x: x * 2
double(5)   # 10

다음과 동일합니다:

python
def double(x):
    return x * 2

대부분의 경우 def를 사용하세요. 람다는 하나의 진정한 장점을 가집니다. 이름을 붙이지 않고 필요한 바로 그 자리에 인라인으로 작성할 수 있습니다. 이것이 sorted(), map(), filter()와 함께 유용한 이유입니다:

python
players = [("민준", 87), ("지훈", 74), ("수진", 92)]

sorted(players, key=lambda p: p[1])              # 점수로 정렬 (오름차순)
sorted(players, key=lambda p: p[1], reverse=True)  # 점수로 정렬 (내림차순)

람다 없이는 key= 인자만을 위해 이름 있는 함수를 정의해야 했을 것입니다. 람다는 의도를 지역적이고 명확하게 유지합니다.

람다는 여러 인자를 받을 수 있습니다:

python
add = lambda a, b: a + b
add(3, 4)   # 7

람다를 언제 사용할 것인가: 작은 식이 한 곳에서만 사용될 때만. 커지거나 재사용이 필요하면 적절한 def를 작성하세요. 여러 연산자를 포함하거나 조건문이 필요한 람다는 일반적으로 def로 전환해야 한다는 신호입니다.

JunoLambda functions 람다는 lambda 키워드로 작성된 이름 없는 한 줄짜리 작은 함수입니다. 존재의 유일한 이유는 sorted()key=로 인라인이 되어 한 작업을 위해 별도의 함수를 정의하지 않는 것입니다. 한 깔끔한 식보다 길어지면 실제 def를 작성하고 훨씬 낫다고 느낍니다.
JunoLambda functions 람다는 익명의 단일 식 함수이며, 이름 있는 def가 소음만 추가하는 인라인 key= 또는 콜백으로 유용합니다. 본문은 한 식이고 문(statement)은 허용되지 않습니다. 더 복잡한 것은 def를 사용하세요.
JunoLambda functionsdef와 같은 함수 객체이지만 이름, 문, 문서화 문자열이 없으므로 한 줄 key=로 유지하세요. 함정은 late binding입니다. 루프에서 만들어진 람다는 루프 변수의 최종값을 읽으므로 lambda i=i: i를 작성하여 고정하세요. 이것은 누구나 정확히 한 번 걸립니다.

리스트 컴프리헨션

파이썬에서 가장 흔한 변환: 시퀀스를 받아서 각 항목에 무언가를 하고 새로운 리스트를 돌려받으세요. 리스트 컴프리헨션은 이를 한 줄의 읽기 좋은 코드로 합니다: [expression for item in iterable]. if로 필터를 추가할 수도 있습니다.

리스트 컴프리헨션은 루프로 만드는 패턴의 간결한 대체입니다. 일반적으로 매 번마다 .append()를 호출하는 동등한 for 루프보다 빠릅니다. Python이 매 번마다 메서드를 호출하지 않기 때문입니다. 구조는 [expression for item in iterable if condition]입니다. if 절은 선택사항입니다.

컴프리헨션은 매 번마다 .append()를 호출하는 루프보다 의미 있게 빠르므로 변환에서 리스트를 만드는 관용적 방법입니다. 또한 자신의 스코프(이름이 보이는 영역)에서 실행되므로 루프 변수가 주변 코드로 유출되지 않습니다: [n ** 2 for n in numbers] 이후 n 이름은 컴프리헨션 밖에 존재하지 않습니다. 주의할 함정은 본문이 실제 작업을 하는 곳에 닿는 것입니다. 식이 깔끔한 변환을 넘어 커지거나 로깅 또는 try 블록을 추가하고 싶으면 명시적 루프가 더 나은 도구입니다. 컴프리헨션은 식만 포함할 수 있고 문(statement)은 포함할 수 없기 때문입니다.

긴 방법:

python
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
    squares.append(n ** 2)

리스트 컴프리헨션:

python
squares = [n ** 2 for n in numbers]

구조는 항상 같습니다: [expression for item in iterable].

python
scores = [87, 42, 96, 55, 71]
scaled = [s * 1.1 for s in scores]          # 10% 보너스 적용
as_grades = [f"{s}/100" for s in scores]    # 각각 포맷팅
JunoList comprehensions[expression for item in iterable]는 시퀀스를 받아 각 항목에 한 가지를 하고 새 리스트를 돌려줍니다. 왼쪽에서 오른쪽으로 읽으면 정확히 무엇을 하는지 말해줍니다. 이것이 Python을 다른 언어에서 번역하기보다 Python으로 작성한다고 느끼게 한 첫 번째 Python 기능입니다.
JunoList comprehensions[expression for item in iterable]는 루프로 만들고-.append()하는 패턴을 대체하고 더 깔끔하게 읽습니다. 새 리스트를 만들고 아무것도 변경하지 않습니다. 식을 간단하게 유지하세요. 그렇지 않으면 얻은 가독성이 사라집니다.
JunoList comprehensions.append() 루프보다 빠르고 자신의 스코프에서 실행되므로 루프 변수는 절대 유출되지 않습니다. 지킬 줄: 컴프리헨션은 식을 받고 문(statement)은 받지 않으므로 try 블록이나 로그 줄을 원하는 순간 명시적 루프가 승리합니다.

조건으로 필터링하기

if 절을 추가하여 테스트를 통과한 항목만 포함하세요. 결과는 조건이 True인 항목만 있는 새 리스트입니다.

컴프리헨션의 if 절은 if/else가 아니라 필터입니다. 항목당 한 번 실행되고 조건이 truthy인 항목만 포함합니다. 조건부 변환(조건에 따라 한 값을 다른 값으로 매핑)의 경우 주 식 안에 삼항 식을 사용하세요.

if의 위치가 필터링인지 변환인지 결정하고 혼동하면 잘못된 길이의 결과가 나오는 일반적인 원인입니다. 끝에 있는 if는 필터링합니다: [x for x in data if x > 0]은 항목을 버립니다. 앞의 조건은 매핑합니다: [x if x > 0 else 0 for x in data]는 모든 항목을 유지하고 실패한 것들을 다시 씁니다(여기서는 음수를 0으로 고정). 한 컴프리헨션에서 둘 다 결합할 수 있습니다 [x * 2 for x in data if x > 0], 그리고 여러 후행 if 절을 쌓을 수 있습니다. 이들은 and로 체이닝됩니다.

python
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
evens = [n for n in numbers if n % 2 == 0]    # [2, 4, 6, 8]
odds = [n for n in numbers if n % 2 != 0]     # [1, 3, 5, 7]
python
scores = [87, 42, 96, 55, 71, 38]
passing = [s for s in scores if s >= 60]    # [87, 96, 71]
failing = [s for s in scores if s < 60]     # [42, 55, 38]
JunoFiltering with a conditionif condition을 끝에 추가하여 테스트를 통과한 항목만 유지하세요: [x for x in data if x > 0]. falsy로 나오는 것은 새 리스트에서 제외됩니다. 이미 알고 있는 같은 컴프리헨션인데 그 위에 경호원이 있는 것입니다.
JunoFiltering with a condition 후행 `if`는 필터링하여 조건이 truthy인 항목만 유지합니다. 이는 식 내의 조건과 다릅니다: [x if x > 0 else 0 for x in data]는 값을 다시 쓰는 대신 버립니다. 작성하기 전에 어느 것을 원하는지 알아야 합니다.
JunoFiltering with a condition 끝에 `if`는 필터링하고 길이를 변경합니다. 앞의 조건은 매핑하고 길이를 유지합니다. 둘을 혼동하고 잘못된 크기의 결과를 배송합니다. 쌓인 후행 `if` 절은 `and`로 체이닝됩니다.

중첩 컴프리헨션

컴프리헨션을 중첩하여 리스트 목록을 단일 리스트로 평탄화할 수 있습니다. 왼쪽에서 오른쪽으로 읽으세요: 각 행에 대해, 그 행의 각 항목에 대해 항목을 포함하세요.

중첩 컴프리헨션은 왼쪽에서 오른쪽으로 실행됩니다. 첫 번째 for 절은 외부 루프이고 두 번째는 내부입니다. 2D 구조가 아닌 단일 평면 결과를 만듭니다. 컴프리헨션을 한눈에 읽기 어려우면 루프를 명시적으로 작성하세요.

절 순서는 동등한 중첩 루프와 같은 순서로 읽습니다. 첫 번째 for는 외부 루프, 두 번째는 내부이고, 나중의 절은 이전 절에서 바인딩된 이름을 사용할 수 있습니다. 이것이 효과를 발휘하는 곳은 평탄화입니다. 상처를 입는 곳은 두 시퀀스의 모든 쌍(참 조합 그리드)인데, 이 경우 itertools.product가 이중 for보다 훨씬 명확하게 읽힙니다. 코드베이스를 건전하게 유지하는 규칙: 컴프리헨션을 파싱하는 데 1초 이상 걸리면 명시적 루프가 더 나은 문서화이므로 대신 작성하세요.

python
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [item for row in matrix for item in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

왼쪽에서 오른쪽으로 읽으세요: matrix의 각 행에 대해, row의 각 항목에 대해 항목을 포함하세요.

중첩 컴프리헨션은 빠르게 혼동될 수 있습니다. 파싱하는 데 순간 이상 걸리면 루프를 명시적으로 작성하세요.

JunoNested comprehensions 한 컴프리헨션에서 두 `for` 절은 리스트 목록을 단일 평탄한 리스트로 평탄화합니다: [item for row in matrix for item in row]. 왼쪽에서 오른쪽으로, 외부 루프 먼저, 루프를 작성하는 정확한 순서로 읽으세요. 눈에 걸리면 대신 실제 루프를 작성할 신호입니다.
JunoNested comprehensions 두 `for` 절을 쌓고 첫 번째는 외부 루프, 두 번째는 내부입니다: [item for row in matrix for item in row]는 2D 리스트를 하나로 평탄화합니다. 결과는 평탄합니다. 평면이 아닙니다. 한눈에 읽기 멈출 때 명시적 루프가 더 낫습니다.
JunoNested comprehensions 첫 `for`는 외부 루프이고 나중의 절은 이전 절의 이름을 사용할 수 있습니다. 평탄화에 좋고, 모든 쌍 조합에는 좋지 않습니다. 여기서는 `itertools.product`가 더 깔끔하게 읽힙니다. 파싱하는 데 1초 이상 걸리면 루프가 더 나은 문서화입니다.

딕셔너리 컴프리헨션

딕셔너리 컴프리헨션은 한 식에서 딕셔너리를 만들고, 리스트 컴프리헨션과 같은 아이디어를 사용합니다: {key: value for item in iterable}. 리스트 컴프리헨션처럼 if로 필터를 추가하세요.

딕셔너리 컴프리헨션은 key-value 쌍을 생성하는 모든 iterable에서 새로운 dict를 만듭니다. 구문은 {key_expr: val_expr for item in iterable if condition}입니다. 루프에서 중복된 key는 최종 값을 사용하여 조용히 덮어씁니다. 기존 dict의 .items()는 딕셔너리 컴프리헨션에 가장 흔한 소스 iterable입니다.

실제 코드에서 두 가지가 문제가 됩니다. 첫째, 모든 key는 hashable이어야 합니다(Python이 고정된 조회 번호로 축약할 수 있는 값이고, 변하지 않음을 의미하므로 문자열과 숫자, 튜플은 가능하지만 리스트와 딕셔너리는 안 됩니다). 리스트를 생성하는 key 식은 TypeError를 발생시킵니다. 둘째, 중복된 key는 에러가 없습니다. 루프가 같은 key를 두 번 생성하면 나중의 값이 조용히 이전 값을 덮어쓰므로, 실제로 유일하지 않은 소스에서 dict를 만들 때 조용한 데이터 손실입니다. 기존의 두 딕셔너리만 병합하려고 할 때 | 연산자(딕셔너리 병합 형태, Python 3.9 이상)는 컴프리헨션보다 의도를 더 명확하게 나타냅니다.

python
names = ["민준", "지훈", "수진"]
scores = [87, 74, 92]

score_map = {name: score for name, score in zip(names, scores)}
# {"민준": 87, "지훈": 74, "수진": 92}

필터와 함께:

python
passing = {name: score for name, score in score_map.items() if score >= 80}
# {"민준": 87, "수진": 92}
python
words = ["사과", "바나나", "체리"]
word_lens = {word: len(word) for word in words}
# {"사과": 2, "바나나": 3, "체리": 2}
JunoDict comprehensions{key: value for item in iterable}는 한 줄에서 딕셔너리를 만들고, key와 value 사이 콜론이 있는 리스트 컴프리헨션과 같은 모양입니다. 이미 가진 dict를 다시 모양을 내기 위해 `.items()`와 쌍으로 만들거나 두 리스트를 한 매핑으로 꿰매기 위해 `zip()`과 쌍으로 만드세요. 원하는 쌍만 유지하기 위해 끝에 `if`를 추가하세요.
JunoDict comprehensions{key: value for item in iterable}는 쌍의 모든 소스에서 dict를 만듭니다. 두 일반적인 소스는 기존 dict의 `.items()`과 두 리스트의 `zip()`입니다. 중복된 key를 주의하세요. 마지막 것이 우승하고 조용히 덮어씁니다.
JunoDict comprehensions Key는 hashable이어야 합니다(리스트 없음). 중복된 key는 에러가 없고, 나중의 값이 조용히 덮어쓰므로 소스가 유일하지 않을 때 데이터를 먹습니다. 그래서 개수를 신뢰하기 전에 유일성을 확인하세요. 두 dict를 병합하려면 `|`이 컴프리헨션보다 더 명확하게 말합니다.

세트 컴프리헨션

세트 컴프리헨션은 한 식에서 세트를 만들고, 중괄호와 콜론이 없습니다. 결과가 세트이므로 중복은 자동으로 제거됩니다.

세트 컴프리헨션은 {expression for item in iterable}를 사용하고 set을 생성합니다. 자동으로 중복을 제거합니다. 변환에서 만들어진 유일한 컬렉션이 필요하고 순서가 중요하지 않을 때 사용하세요.

결과가 set이므로 리스트 버전이 제공하지 않는 두 가지가 따릅니다. 식에서 중복된 값은 자동으로 하나로 축약됩니다. 이것이 요점입니다. 한 식에서 변환을 중복 제거하는 가장 깔끔한 방법입니다. 비용은 세트에 순서가 없으므로 항목이 어떤 특정 시퀀스로 나올 것이라고 의존할 수 없다는 것입니다. 또한 멤버는 hashable이어야 합니다. 이는 dict key에 적용되는 같은 규칙입니다. Python이 고정된 조회 번호로 축약할 수 있는 값으로, 리스트를 제외합니다.

python
words = ["사과", "바나나", "체리", "사과"]
unique = {w.lower() for w in words}    # {"사과", "바나나", "체리"}

유일한 값을 원하고 순서를 신경 쓰지 않을 때 세트 컴프리헨션을 사용하세요.

JunoSet comprehensions{expr for item in iterable}와 중괄호와 콜론이 없는 세트를 만들고, 세트는 중복을 무료로 버립니다. 그래서 당신의 작업이 "유일한 것들을 주세요"이면 한 줄에서 합니다. 그렇지만 어떤 특정 순서로 돌아올 것을 기대하지 마세요.
JunoSet comprehensions{expr for item in iterable}는 `set`을 생성하고 그렇게 중복 제거합니다. 변환에서 유일한 컬렉션을 원하고 순서가 중요하지 않을 때 닿으세요. 중괄호와 콜론이 없습니다. 이것이 딕셔너리 컴프리헨션과 분리하는 유일한 것입니다.
JunoSet comprehensions 세트는 자동으로 중복을 버립니다. 이것이 리스트 컴프리헨션 위에 선택하는 전체 이유입니다. 거래는 의존할 순서가 없고, 멤버는 hashable이어야 합니다. 그래서 안에 리스트 없습니다. 최고의 적합: 한 식에서 변환을 중복 제거합니다.

제너레이터 표현식

제너레이터는 대괄호 대신 괄호를 사용하는 리스트 컴프리헨션처럼 보입니다. 핵심 차이점은 리스트 컴프리헨션은 전체 리스트를 메모리에 한 번에 만듭니다. 제너레이터는 필요할 때만 한 번에 하나의 값을 생성합니다. 큰 시퀀스의 경우 훨씬 적은 메모리를 사용합니다.

제너레이터 식은 컬렉션이 아닌 iterator를 생성합니다. 값을 lazy하게 계산합니다. 다음 값은 요청될 때만 생성됩니다. 이는 결과가 sum(), max(), any() 같은 함수로 즉시 소비될 때 가장 가치 있으므로 먼저 전체 리스트를 만들 이유가 없습니다.

제너레이터는 lazy하게(한 번에 하나씩, 무언가가 다음을 요청할 때만) 값을 생성하므로 입력이 얼마나 큰지 관계없이 메모리가 평평하게 유지됩니다. 리스트 컴프리헨션은 한 번에 모든 요소를 보유하는 곳에서 현재 값만 보유합니다. 이는 크거나 스트리밍 소스에서 sum(), max(), any()로 공급하는 올바른 호출을 만듭니다. 기억할 실패 모드는 제너레이터가 단일 사용입니다. 무언가가 끝까지 반복되면 exhausted되고 더 이상 아무것도 생성하지 않으므로 같은 제너레이터 위의 두 번째 루프는 0번 실행되고 에러가 없습니다. 데이터를 두 번 이동해야 하면 한 번에 리스트를 만들고 반복하세요.

python
squares_gen = (n ** 2 for n in range(1000000))
python
total = sum(n ** 2 for n in range(1000000))   # sum()은 제너레이터를 소비합니다

sum(), max(), min(), any() 같은 함수에 제너레이터를 직접 전달할 때 추가 괄호를 버릴 수 있습니다:

python
total = sum(n ** 2 for n in range(1000))   # 두 괄호가 아닌 한 괄호

대부분의 일상 코드의 경우 리스트 컴프리헨션은 괜찮습니다. 큰 데이터셋을 처리하거나 메모리에 모든 것을 보유하는 것이 낭비일 스트리밍 데이터의 경우 제너레이터를 사용하세요.

JunoGenerator expressions 제너레이터는 대괄호 대신 괄호를 사용하는 리스트 컴프리헨션처럼 보이지만 전체 리스트를 먼저 만드는 대신 한 번에 하나씩 값을 만듭니다. 거대한 시퀀스의 경우 메모리를 많이 절약합니다. 깔끔한 경우: 한 것을 `sum()` 또는 `max()`에 직접 떨어뜨리고 리스트 만들기를 스킵하세요.
JunoGenerator expressions 제너레이터 식은 lazy iterator를 반환합니다. 요청될 때만 다음 값을 계산하므로 아무것도 전체 리스트를 메모리에 만들지 않습니다. 결과가 `sum()`, `max()`, `any()`에 직접 공급될 때 최고입니다. 기억하세요. 단일 사용이고 일단 소비되면 비어있습니다.
JunoGenerator expressions Lazy하고 입력이 얼마나 크든 메모리에 평평하므로 큰 또는 스트리밍 소스에서 `sum()` 또는 `any()`로 공급하는 올바른 도구입니다. 잡기: 단일 사용이고 exhausted generator 위의 두 번째 루프는 0번 에러 없이 실행됩니다. 두 번 통과가 필요합니까? 리스트를 한 번 만드세요.

zip()

zip()은 두 개 이상의 시퀀스에서 항목을 함께 묶어서 병렬로 루프할 수 있도록 합니다. 가장 짧은 시퀀스에서 멈춥니다. 두 리스트가 서로 대응할 때 인덱스를 관리하는 것을 피하는 깔끔한 방법입니다.

zip()은 lazy iterator of tuples를 반환하고 입력 iterables를 함께 소비합니다. 가장 짧은 입력에서 멈춥니다. 더 긴 시퀀스는 조용히 자릅니다. 시퀀스의 길이가 다를 수 있는 경우 itertools.zip_longest()는 지정된 값으로 더 짧은 것들을 채웁니다.

zip()은 입력을 함께 이동하고 가장 짧은 것이 부족해지는 순간 멈춥니다. 그 자르기는 설계하려는 gotcha입니다. 1000개 기록의 리스트를 999개의 리스트와 쌍으로 만들면 마지막 기록을 조용히 잃고, 에러도 경고도 없습니다. 길이가 일치하는 것으로 예상되면 zip(seq_a, seq_b, strict=True)(Python 3.10 이상)는 데이터를 버리는 대신 다르면 발생시키고, itertools.zip_longest는 다르도록 허용될 때 간격을 채웁니다. 알 가치가 있는 다른 트릭: zip(*rows)는 행 목록을 열 목록으로 변환하므로 전치합니다. *는 외부 리스트를 별도의 인자로 언팩합니다.

python
names = ["민준", "지훈", "수진"]
scores = [87, 74, 92]

for name, score in zip(names, scores):
    print(f"{name}: {score}")
# 민준: 87
# 지훈: 74
# 수진: 92

zip()은 가장 짧은 시퀀스에서 멈춥니다. 시퀀스의 길이가 다를 수 있으면 채우기 값과 함께 itertools.zip_longest()를 사용하세요.

zipped 쌍 목록에서 두 개의 별도 리스트로 변환하려면 zip(*pairs)를 사용하세요:

python
pairs = [("민준", 87), ("지훈", 74), ("수진", 92)]
names, scores = zip(*pairs)
# names = ("민준", "지훈", "수진")
# scores = (87, 74, 92)

*pairs는 리스트를 별도 인자로 언팩하므로 zip(*pairs)zip(("민준", 87), ("지훈", 74), ("수진", 92))가 됩니다. * 연산자는 함수 장에서 다룹니다.

zip()은 인덱스를 수동으로 관리하지 않고 여러 시퀀스를 병렬로 반복하는 깔끔한 방법입니다:

python
before = [10, 20, 30]
after = [15, 18, 35]

for b, a in zip(before, after):
    change = a - b
    print(f"{b} -> {a} ({'+' if change >= 0 else ''}{change})")
Junozip() `zip()`은 두 개 이상의 시퀀스를 묶어서 함께 루프할 수 있게 하고, 인덱스 juggling은 없습니다. 가장 짧은 것에서 멈추므로 mismatched 길이는 조용히 extras를 잃습니다. 그리고 `zip(*pairs)`는 그것을 역으로 실행하고, 튜플의 리스트를 별도 리스트로 분할합니다.
Junozip() `zip()`은 lazy iterator of tuples를 반환하고 입력을 병렬로 stepping합니다. 가장 짧은 것에서 멈추므로 더 긴 시퀀스는 음성으로 잘립니다. `itertools.zip_longest()`는 길이가 합법적으로 다를 때 간격을 채우고, `zip(*pairs)`는 언팩합니다.
Junozip() Lazy, 병렬, 가장 짧은 입력에서 멈춤. 길이가 일치하는 것으로 예상될 때 데이터를 조용히 버립니다. Mismatch를 발생시키도록 `strict=True`를 전달합니다. `zip(*rows)`는 열의 한 호출에서 행 제목입니다.

map()과 filter()

map()filter()는 컴프리헨션이 하는 것을 하는 더 오래된 함수형 스타일 도구입니다. 이전 코드에서 이들을 볼 것이므로 무엇을 의미하는지 아는 것은 가치가 있습니다. 새 코드의 경우 컴프리헨션을 선호하세요. 대부분의 Python 개발자들에게 더 읽기 쉽습니다.

map(func, iterable)은 각 항목에 func를 적용하는 lazy iterator를 반환합니다. filter(func, iterable)func가 truthy인 항목의 lazy iterator를 반환합니다. 둘 다 컴프리헨션보다 먼저 존재합니다. 새 코드에서 컴프리헨션을 선호하세요. 필요한 것을 이미 하는 이름 있는 함수가 있을 때 map()을 사용하세요.

둘 다 lazy iterators를 반환하므로 지금 값을 원할 때 list()로 감싸세요. 이미 알고 있는 컴프리헨션에 일대일로 매핑됩니다. map(f, it)(f(x) for x in it)이고, filter(pred, it)(x for x in it if pred(x))입니다. 그 동등성이 결정 규칙입니다. 인라인 lambda와 함께 컴프리헨션은 더 나은 읽음이고 현대 기본값입니다. 이미 작업을 수행하는 이름 있는 함수와 함께 list(map(int, strings))는 "정수를 문자열에 매핑"으로 읽고 더 깔끔한 선택입니다. 그래서 유일한 진짜 호출은 이미 손에 함수가 있는지 여부입니다.

python
numbers = [1, 2, 3, 4, 5]

list(map(lambda x: x ** 2, numbers))         # [1, 4, 9, 16, 25]
list(filter(lambda x: x % 2 == 0, numbers))  # [2, 4]

컴프리헨션을 선호하세요. 대부분의 Python 개발자들에게 더 읽기 쉽습니다. 이미 존재하는 이름 있는 함수가 있을 때 map()을 사용하세요:

python
strings = ["1", "2", "3"]
numbers = list(map(int, strings))   # [1, 2, 3] (여기서 컴프리헨션보다 깔끔함)
Junomap() and filter() `map(func, iterable)`은 모든 항목에 대해 함수를 실행합니다. `filter(func, iterable)`은 함수가 truthy로 돌아오는 항목만 유지합니다. 이들은 컴프리헨션이 하는 것을 하는 더 오래된 방법이므로 다른 사람의 코드에서 만날 것입니다. 당신 자신의 것의 경우 컴프리헨션은 대부분의 사람들에게 더 명확하게 읽습니다.
Junomap() and filter() `map()`은 각 항목을 변환합니다. `filter()`은 truthy 것을 유지합니다. 모두 lazy하므로 결과를 보려면 `list()`로 감싸세요. 컴프리헨션은 새 코드의 기본입니다. 유일한 `map()` 우승 자리는 이미 있는 이름 있는 함수입니다. `map(int, strings)`는 컴프리헨션보다 더 깔끔하게 읽습니다.
Junomap() and filter() `map(f, it)`는 `(f(x) for x in it)`이고 `filter(pred, it)`는 `(x for x in it if pred(x))`이고 모두 lazy입니다. 그래서 선택은 순전히 가독성입니다. 인라인 람다와 컴프리헨션이 승리합니다. 이미 이름 있는 함수와 `map(int, strings)` 승리합니다.

실제로

플레이어 목록을 통과 점수로 필터링하고, sorted와 람다로 점수로 순위를 매기고, 열거된 위치로 출력하세요:

python
players = [
    {"name": "민준", "score": 87},
    {"name": "지훈", "score": 42},
    {"name": "수진", "score": 96},
    {"name": "현태", "score": 55},
]

passing = [p for p in players if p["score"] >= 60]
ranked = sorted(passing, key=lambda p: p["score"], reverse=True)
score_map = {p["name"]: p["score"] for p in ranked}

for i, (name, score) in enumerate(score_map.items(), start=1):
    print(f"{i}. {name}: {score}")

활성 관리자에 대한 사용자 목록을 필터링하고, id-to-name 조회 dict를 만들고, 각각 한 번에 정렬된 이름을 수집하세요:

python
raw_users = [
    {"id": 1, "name": "민준", "role": "admin", "active": True},
    {"id": 2, "name": "지훈", "role": "user", "active": False},
    {"id": 3, "name": "수진", "role": "admin", "active": True},
    {"id": 4, "name": "현태", "role": "user", "active": True},
]

active_admins = [u for u in raw_users if u["active"] and u["role"] == "admin"]
id_map = {u["id"]: u["name"] for u in raw_users}
names = sorted(u["name"] for u in raw_users if u["active"])

print(f"활성 관리자: {[u['name'] for u in active_admins]}")
print(f"모두 활성: {names}")

zip을 사용하여 기능 이름을 중요도 점수와 쌍으로 만들고, dict 컴프리헨션을 만들고, 람다로 정렬하고, 두 번째 컴프리헨션에서 값을 정규화하세요:

python
feature_names = ["나이", "수입", "점수", "근속"]
importances = [0.12, 0.34, 0.28, 0.26]

feat_dict = {f: i for f, i in zip(feature_names, importances)}
top_feats = sorted(feat_dict.items(), key=lambda x: x[1], reverse=True)[:2]

print("상위 2개 기능:")
for name, score in top_feats:
    print(f"  {name}: {score:.2f}")

# 합이 1.0이 되도록 정규화 (여기 값은 이미 합이 1이지만 패턴으로 표시)
total = sum(feat_dict.values())
normalised = {k: round(v / total, 4) for k, v in feat_dict.items()}
print(f"정규화됨: {normalised}")

zip은 중간 튜플을 만들지 않고 두 리스트를 쌍으로 만듭니다. dict 컴프리헨션은 한 식에서 매핑을 만듭니다. 정렬 람다는 이름 있는 key 함수를 피합니다. 정규화 컴프리헨션은 원래 dict를 변경하지 않고 값을 변환합니다.