데이터 분석가:Applied Data Analytics/파이썬

이터레이터, 제너레이터, 데코레이터, 람다, 일급객체

데이터분석 2025. 1. 23. 16:35

이터레이터, 제너레이터  :  이터레이터는 어떤건지? 왜 쓰고 어떤건지 알고 있어야 

데코레이터 : 꾸며주는 역할로 기능이 다양하다.

 

람다 : 중요해서 집중공부 필요.

 

전역변수가 뭔지 

 

이터레이터(Iterator)

이터러블 : for 반복자 in 반복할 수 있는 객체

# str이 이터러블한지 확인
str_ai = 'aiffel'
str_ai.__iter__()

이터러블하지 않은 경우 : int형
이터러블한 경우 : str, 리스트, 튜플, 셋, 딕셔너리, range()

이터러블 객체는 __iter__() 메서드를 사용할 수 있었는데, 이터레이터는 __next__() 메서드를 사용한다. 이때 __next__() 메서드를 값을 차례대로 꺼내주는 메서드

  • 이터러블 객체: __iter__() 메서드 사용
  • 이터레이터: __next__() 메서드 사용

 

이터레이터

해당 객체가 이터레이터인지 확인하는 방법은 __next__() 메서드를 사용하는 것이다. 이 메서드를 통해서 진짜로 해당 객체가 이터레이터인지 확인.

메소드를 사용하는 순서 :  이터러블은 __iter__() 로 이터레이터를 얻고, __next__()로 반복적으로 이터레이터의 값을 차례대로 꺼낼 수 있다.  값을 다 꺼내고 나면 오류가 난다.

이터러블 ➔ __iter__() ➔ 이터레이터 ➔ __next__() 

 

for i in range(0, 11) 
0부터 10까지의 연속된 정수를 만들어내는 반복문.
그런데 사실은 0부터 10까지의 모든 정수를 만들어내는 것이 아니라 값을 차례대로 꺼낼 수 있는 이터레이터 하나를 만든 것이었다.

6개의 자료형인 str, 리스트, 튜플, 셋, 딕셔너리, range() 함수에는 내부적으로 __iter__() 메서드가 구현되어 있었다

제너레이터(Generator)

제너레이터는 한번에 데이터를 모아서 처리하지 않기 때문에 큰 메모리를 사용하지 않아도 된다. 이는 규모가 있는 프로그램을 개발할 때 매우 중요한 요소이며, 제너레이터는 큰 규모의 확장성이 있는 프로그램을 개발하기 위해 사용한다

제너레이터를 새로운 변수에 바인딩해주고 그 변수를 이용하면 우리가 만들었던 제너레이터를 계속 쓸 수 있게 됩니다.
바인딩(binding)이란 변수에 변수와 관련된 속성을 연관시키는 과정.
이터널에서 함수 꺼내기
ite = [x for x in range(3)]  # 리스트는 이터러블 객체

# 이터레이터 첫번째 호출
for value in ite:  # 이터러블 객체를 for문에 넣으면 이터레이터가 됩니다.
    print(value)                       
 
결과 : 두번째 호출도 같습니다.
0
1
2
 

제너레이터에서 데이터 꺼내기

gen = (x for x in range(3) )

# 제너레이터 객체 첫번째 호출
for value in gen:
    print(value)
결과 : 두번째 호출에서 값을 꺼내지 못함. 왜냐하면 제너레이터는 값을 넘겨주고 나면 값을 소비하고 더 이상 기억하지 않기 때문.

 

0
1
2
  • 이터러블 → 여러 번 값을 가져올 수 있다.
  • 제너레이터 → 한 번 값을 소비하면 더이상 기억하지 않아서 재활용할 수 없다.

 

이터레이터와 제너레이터를 사용하는 이유

둘 다 많은 데이터를 효율적으로 처리하기 위해 사용되며, 특히, 데이터를 한꺼번에 메모리에 올리지 않고, 필요할 때마다 하나씩 처리하는 게 핵심

공통점 

  1. 한 번에 한 개의 데이터만 처리: 둘 다 한 번에 하나씩 데이터를 반환해서 메모리 사용량을 줄인다.
  2. for 루프에서 사용 가능: 둘 다 이터러블(Iterable)하고, 반복(iteration)이 가능하다.
  3. next() 함수로 값 가져오기: next()를 사용해 데이터를 순차적으로 가져올 수 있다.

  1. 이터레이터:
    • 반복을 구현하기 위해 객체 지향적으로 설계해야 할 때 사용.
    • 상태를 직접 제어하거나, 여러 가지 커스터마이징이 필요할 때 적합.
  2. 제너레이터:
    • 데이터를 즉석에서 생성하면서 처리하고 싶을 때.
    • 더 적은 코드로 간단하게 구현 가능.

이터레이터 예

  • 만들어진 책을 읽는 행위라고 생각.
    • 책(이터레이터 객체)은 처음부터 끝까지 모든 페이지가 만들어져 있다.
    • 페이지를 넘기면서(반복) 내용을 읽는 것처럼, 데이터를 처리한다.

제너레이터 예

  • 책을 실시간으로 쓰면서 읽는 행위에 가까움.
    • 책 내용(데이터)을 미리 다 만들어 놓지 않고, 필요한 순간에 한 페이지씩 쓰고 읽는 것.
    • 메모리 효율적이고, 데이터가 많아도 부담이 적다.

차이점

특징 이터레이터 제너레이터
정의 방식 클래스 또는 객체로 정의 함수로 정의 (yield 사용)
구현 복잡성 상대적으로 코드가 복잡 간단하고 읽기 쉬움
메모리 효율성 데이터 소스를 명시적으로 메모리에 저장 가능 즉석에서 데이터를 생성, 메모리 효율이 더 좋음
상태 유지 상태를 수동으로 관리 상태를 자동으로 관리 (yield가 기억함)

 

데코레이터(Decorator)  :

데코레이터는 기존 함수에 추가 기능을 덧붙이는 도구. 쉽게 말해, 함수를 꾸며주는 역할을 함.

같은 코드를 여러번 사용하면 수정할 때도 여러번 수정해야 하는데, 이럴 때 데코레이터를 이용해서 작성하면 편리하다. 함수를 더 효율적으로 사용할 수 있다. 또는 어떤 함수가 있을 때, 해당 함수를 직접 수정하지 않고 함수에 기능을 추가하고자 할 때 데코레이터를 사용한다.

 

데코레이터(@) 만들기

단순히 어떤 함수(예: hi)에 기능을 추가하고자 한다면 해당 함수 위에 @데코레이터함수 를 적어주면 된다

 

 

  • 코드 재사용: 여러 함수에 공통으로 적용할 작업(로깅, 검사, 출력 등)을 쉽게 추가 가능.
  • 가독성 향상: 기존 함수를 수정하지 않고도 기능을 확장할 수 있다.

 

  • 데코레이터는 기본 음식을 꾸며주는 토핑.
  • 예를 들어, 피자(기본 함수)에 치즈나 페퍼로니(데코레이터)를 추가해서 더 맛있게 만드는 것과 같다.

def decorator(func):                       # 데코레이터 함수

     def wrapper():

            print("함수 실행 전")          # 추가 기능

            func()                                 # 원래 함수 실행

            print("함수 실행 후")          # 추가 기능

     return wrapper

@decorator                                  # 데코레이터 사용

def say_hello():

      print("Hello, World!")

 

say_hello()

 

리스트와 제너레이터의 메모리 사이즈 비교
import sys

# list
print(sys.getsizeof( [i for i in range(100) if i % 2] ))               # 홀수를 리스트로 만든다.
print(sys.getsizeof( [i for i in range(1000) if i % 2] ))

# generator
print(sys.getsizeof( i for i in range(100) if i % 2) ))
print(sys.getsizeof( i for i in range(1000) if i % 2) ))              # 홀수를 하나씩 생성
 

 

 

람다

람다 표현식 :  함수를 한 줄로 간단하게 정의하는

 

람다 표현식을 인수로 표현하기

 

일급 객체(First-Class Citizen) : 객체 지향 프로그래밍이 무엇인지 알아보고 파이썬의 함수에 대해 

 

람다 함수 : (lambda 매개변수 : 리턴값)(인수)

y = ' 나폴리 '
(lambda x : x + y)('이탈리아')     결과 : '이탈리아 나폴리 '
 

 

람다 표현식을 인수로 사용하는 함수 3가지

map()

map(적용시킬 함수, 적용할 값들)   # 표현 방식
map(함수, 리스트 or 튜플)

# 리스트
two = [i for i in range(10)]
# map() + 람다 함수
map(lambda x : x * 2, two)
# 파이썬 3에서 map을 list로 리턴하기
list(map(lambda x : x * 2, two))                  결과 : [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

 

two = [i for i in range(1, 11)]
# 제곱값 리스트 생성
result = list(map(lambda x: x**2, two))
print(result)                                             결과 : [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]


filter() : 무언가를 걸러내는 도구입니다. 자세히 설명하면 특정 성질을 가진 것은 차단하고, 그렇지 않은 것은 통과시키는 도구

filter(함수, 리스트)

 

filter() + 람다 함수

list(filter(lambda x : x % 2 == 1, two))      결과 : [1, 3, 5, 7, 9]
 
list(filter(lambda x : x % 2, two))  위에와 같은결과 입니다.
파이썬은 x % 2 의 결과가 1일 때만 값을 남기기를 바라는데, 1은 True  파이썬은 다른 조건이 없으면 참일 때만 통과시키기 때문에 비교 연산자 ==가 없어도 출력이 그대로 된다. 
 
quiz_list = [i for i in range(10)]
result = list(filter(lambda x: 4 <= x <= 6, quiz_list))
print(result)                                      결과 : [4, 5, 6]  
 
 


reduce() : 리스트의 값을 차례로 합치거나 계산할 때 사용하는 함수

reduce(함수, 이터러블 객체)

# 리스트
a = [1, 2, 3, 4, 5]

# reduce(람다 함수, 이터러블 객체)
reduce(lambda x, y : x * y, a)
  • lambda x, y: x * y:
    • 리스트에서 두 개의 값을 꺼내서 곱하는 작업을 한다.
    • 예: 첫 번째 값과 두 번째 값을 곱하고, 그 결과에 다음 값을 곱하는 식.
  • reduce():
    • 리스트의 모든 값을 차례로 하나씩 꺼내면서 lambda 함수를 적용해 계산을 이어간다.
  • a: 숫자들의 리스트.  a = [1, 2, 3, 4, 5].

결과 : 5!(5 팩토리얼)의 값 120

 

리스트 컴프리헨션과 람다 표현식을 이용해 1부터 100까지의 합을 구하는 코드를 구현해보세요!
result = sum((lambda x: x[0] + x[1])((i, 101 - i)) for i in range(1, 51))
print(result)                        결과 : 5050
 
 
함수는 어떤 입력을 받아 작업을 수행하고 결과를 돌려주는 코드의 묶음.  
    매개변수는 함수가 작업을 수행하기 위해 필요한 입력 값.
 

※ 함수를 리턴값으로 사용할수 있다 쉽게 비유하자면 
**"요리 레시피를 만드는 레시피"**를 만드는 것과 비슷

  • 첫 번째 레시피(함수)는 **다른 요리 레시피(함수)**를 만들어서 반환
  • 반환된 레시피를 원하는 대로 사용할 수 있다.
  • 복잡한 작업을 단계적으로 나눌 때 유용하고 특히 함수 안에서 동작이 바뀌는 경우를 처리할 때

일급객체

  • 객체: 속성(변수)과 행동(함수)을 가지고 있는 대상.  속성은 변수로, 행동은 함수로 나타낸다.

3가지 조건을 만족하는 객체가 일급 객체.

  1. 변수에 할당하거나 데이터 구조(자료구조) 안에 그 객체를 담을 수 있다.
  2. 매개변수로 전달할 수 있다.
  3. 리턴값으로 사용될 수 있다.

파이썬에서는 int, float, str 타입의 객체뿐만 아니라 함수도 일급 객체이다.