본문 바로가기
Language & Tool/Python

🌳Python 고급 문법 요약 (데코레이터 포함)

by 박수무당벌레 2025. 6. 13.

관련글 : 

functools 기반 데코레이터 상세설명 https://waydd.tistory.com/58

 

 

1. 컴프리헨션 (Comprehension)

 

간결하고 직관적인 방식으로 반복 가능한 객체(iterable)를 기반으로 새로운 컬렉션을 만드는 문법

  • for 루프 + 조건문 + 새로운 컬렉션 생성을 한 줄에 표현
    A if 조건 else B 구조의 경우, 삼항 연산자(조건 표현식)이 되도록 함
    for문 중첩도 가능 ex) 리스트 안의 리스트를 평탄화, 2중 반복할 때 사용
  • 작은 데이터에서는 빠름 (C 구현 최적화)
  • 재사용성 : 한번 생성 이후 여러번 반복 사용 가능
  • 즉시 평가 (모든 원소를 메모리에 담아 생성)
    (예시) 중간 리스트가 한 번 만들어진 후 복사됨 (원소 개수만큼 한번에 메모리 차지)
※ 조건 필터링과 if-else 표현식 차이점
① 필터용 if           → 값은 고정, 조건만 검사  ex) [x for x in lst if x % 2 == 0]
② 조건표현식 if    → 값도 조건 따라 달라짐   ex) [x if x % 2 == 0 else -x for x in lst]

 

- 리스트 컴프리헨션

  • 기존 리스트를 기반 즉시 새로운 리스트를 생성 (빠르고 직관적)
  • 먼저 전체 리스트를 메모리에 만든 뒤 컨테이너에 넣음 (즉시 완성된 리스트 반환)
  • 순서 ⭕, 중복 허용
기본형 : [expression for variable in iterable[ if condition]]
확장형 : [expression_if_true if condition else expression_if_false for variable in iterable]
# 예시
[x**2 for x in range(5)]                          # → [0, 1, 4, 9, 16]
[x for x in range(10) if x % 2 == 0]              # → [0, 2, 4, 6, 8]

 

- 셋(Set) 컴프리헨션

  • 중복을 제거한 값을 기반으로 새로운 집합 생성
  • 순서 ❌, 중복 제거
기본형 : {expression for variable in iterable[ if condition]}
# 예시
{len(word) for word in ["apple", "banana", "kiwi", "apple"]}  
# → {4, 5, 6}

 

- 딕셔너리(Dictionary) 컴프리헨션

  • 키와 값의 조건이나 연산을 적용해 새로운 딕셔너리 생성
  • 키-값 쌍 생성, 중복 키 제거
기본형 : {key_expression: value_expression for variable in iterable[ if condition]}
# 예시
{word: len(word) for word in ["apple", "banana", "kiwi"]}  
# → {'apple': 5, 'banana': 6, 'kiwi': 4}

 

 

2. 제너레이터 

 

필요할 때마다 원소를 생성하는 객체 (리스트 아님)

중간 자료구조(materialization) 를 만들지 않고 iterable을 직접 순회 + 바로 대상 컨테이너에 요소를 채움 (바로 소비)

→ 메모리 효율이 약간 더 좋음

  • 주로 컨테이너 생성자와 함께 사용
    원소를 하나씩 생성(lazy) 후, 컨테이너(list, tuple, set, deque, ...) 가 이걸 소비하면서 바로 채움
    → 중간 결과를 메모리에 담지 않음, 곧바로 컨테이너가 소비
  • 재사용성 : 한 번만 소비 가능 (소진 후 다시 못 씀)
  • 지연 평가(lazy evaluation) 이용 (필요할 때마다 원소를 하나씩 생성)
  • yield 기반이라 큰 입력·스트리밍 처리에는 효율적, 전체를 다 모을 땐 리스트보다 느림
    단일 원소를 생성할 때만 메모리 사용

 

- iterable & iterator

  • 제너레이터 : yield 또는 () 제너레이터 표현식으로 생성된 객체
  • iterable: __iter__() 메서드를 가지며, for문 등에 사용 가능
  • iterator: __next__() 메서드를 가지며, next() 호출 가능
  • 즉, 제너레이터는 __iter__()와 __next__()를 모두 구현하므로 iterable이면서 iterator
※ Iterable (has __iter__ → returns self)
-  list, tuple, dict, set, str, range, generator
-  iterator
※ Iterator (has __next__) ⊂ Iterable
-  generator
-  iter(list), iter(str)

 

 

- 제너레이터 표현식 (generator expression)

  • () 괄호를 사용하여 반복 가능한 객체(iterable)를 기반으로 변환/필터링된 결과인 반복자(iterator) 생성
  • 직접 값을 생성하면서 next()로 꺼낼 수 있는(= 지연 평가) 이터레이터의 특수한 형태 
    → generator는 한 번만 소비 가능한 iterator  ≠ ex) list의 경우, 여러 번 반복 가능한 iterable
  • 리스트 컴프리핸션과 문법이 거의 동일, 컴프리헨션의 메모리 최적화 버전
기본형 : generator 객체(iterator) = (expression for variable in iterable[ if condition])
# 예시
g = (x * 2 for x in range(3))
print(next(g))  # 0
print(next(g))  # 2

 

- 제너레이터 함수(generator function)

  • 마찬가지로 제너레이터 객체(iterator) 를 생성함
  • 함수 정의 필요 (def, yield)
  • 제너레이터 표현식보다 더 복잡한 로직 처리 가능 (조건문, 반복문, 예외처리, 로깅 등)
# 예시
def double():
    for x in range(3):
        yield x * 2

g = double()
print(next(g))  # 0
print(next(g))  # 2

# 주로 파일 읽기에 사용
def read_lines(file_path): 
    with open(file_path) as f:
        for line in f:
            yield line.strip() # 파일 한 줄씩

 

 

3. 함수형 스타일

 

함수형 프로그래밍 패러다임을 따르는 코딩 스타일

  • 함수는 변하지 않고, 데이터만 변형됨 (= 데이터 흐름 위주 ≠ 기존 명령형 스타일은 절차 중심
  • 상태를 변경하지 않고 새 값을 반환함 (= 불변성)
    → 전역 변수 사용이나 print(), input()도 되도록 지양, 즉 사이드 이펙트를 줄임
  • 코드의 명확성, 재사용성, 테스트 용이성을 높임
  • 순수 함수와 다르게 함수를 인자로 받거나 함수를 반환하는 고차 함수 사용 ( 조건 / 변환 / 동기화 반복을 처리 )
    → 반복문 대신 map, filter, reduce, 리스트 컴프리헨션 사용
# 명령형 스타일 (조건 + 변환)
result = []
for x in nums:
    if x > 0:
        result.append(x * 2)

# 함수형 스타일
result = list(map(lambda x: x * 2, filter(lambda x: x > 0, nums)))

# 동기화 반복
for name, score in zip(names, scores):
    print(f"{name}의 점수는 {score}")

 

- 대표적인 함수형 내장 함수

  설명 예시
map 각 요소에 함수 적용 map(str, [1, 2, 3]) → '1','2','3'
filter 조건을 만족하는 요소만 필터링 filter(lambda x: x>0, lst)
reduce 누적 계산 수행 (functools 필요) reduce(lambda x,y: x+y, lst)
zip 여러 iterable을 병렬 처리 zip([1,2], ['a','b'])
※ 함수형 친화적 유틸 함수
- lambda : 고차 함수, 익명 함수에서 활약
- enumerate : 순회와 인덱스 제공 (내부 상태를 직접 제어 X, 불변 데이터 반복 처리 간결)

 

 

4. 중첩 함수

 

함수 정의 안에 또 다른 함수를 정의하는 구조로, 부 함수 혹은 로컬 함수라고도 부름

  • 외부 함수에서 내부 함수를 반환하거나 호출 가능
  • 외부에서 쓸이유가 없는 경우(캡슐화), 로직이 국지적인 경우(가독성)에 사용
  • 함수가 너무 길고 복잡할 때나 재사용성이 높은 경우엔 X (따로 빼서 모듈화해야 유지보수 쉬움)
  • 클로저와 데코레이터 등의 기반
# 중첩 함수
def outer():
    def inner():
        pass

 

- 클로저 (Closure)

  • 중첩 함수의 일종
  • 외부 함수의 지역 변수 값을 내부 함수가 기억하고 사용하는 함수 객체 (함수형 프로그래밍에서 자주 사용)
    → 중첩함수를 반환할때, 그 내부 함수가 외부 함수의 스코프에 있는 변수를 참조하면 클로저가 형성
# 클로저
def outer(x):
    def inner(y):
        return x + y
    return inner

f = outer(10)  ← 이 시점에서 inner 함수가 클로저가 됨
f(5) → 15 
# x는 이미 outer 함수 실행이 끝났지만, inner()가 x의 값을 기억하고 있음 (= 상태 유지)

 

- 데코레이터

  • 기존 함수를 수정하지 않고 감싸서 부가기능을 적용하는 함수 ( 중첩함수 + 클로저 )
  • @ 기호를 이용해 간결하게 적용
# 데코레이터 정의
def deco(func): 
    def wrapper():
        print("before")
        func()
        print("after")
    return wrapper

# 데코레이터 적용 (= deco(hello)로 감쌈)
@deco 
def hello(): 
    print("hi")
    
# 함수 호출
hello() # before, hi, after 순으로 출력
@decohello = deco(hello)와 동일
- deco 함수는 hello를 인자로 받아 wrapper를 반환, hello는 wrapper로 대체
  → hello()를 호출하면 실제로는 wrapper()가 실행됨
- 내부에서 정의한 wrapper()가 중첩함수, func를 기억하고 사용하므로 클로저
- 실행 시 before → hello() → after 순으로 출력됨
※ 데코레이터 종류
- @deco : 사용자 정의 데코레이터
- @staticmethod, @classmethod : 클래스 내 메서드 제어 (표준 내장)
- @property : 게터/세터 기능 구현 (표준 내장)
- @lru_cache : 결과를 캐시해서 속도 향상 (functools 기반)
- @wraps : 메타정보 유지 (functools 기반)
- @login_required : 권한 체크 (Flask, Django 웹 프레임워크에서 제공)
- @timer : 실행 시간 측정용 데코레이터 (직접 정의)