코딩하는 타코야끼

[Python] 10강_Iterable 과 Decorator 본문

[T.I.L] : Today I Learned/Python

[Python] 10강_Iterable 과 Decorator

가스오부시 2023. 4. 9. 16:22
728x90
반응형

1. Iterator

🌓  Iterable(공장)

  • 여러개의 데이터를 하나씩 또는 한 단위씩 제공하는 객체.
  • Iterator객체를 반환하는 __iter__() 특수 메소드를 정의해야 한다.
    • __iter__()는 iter(Iterable) 내장함수에 의해 호출된다.

🌓  Iterator(가계)

  • 자신을 생성한 Iterable의 값들을 하나씩 또는 한 단위씩 제공하는 객체
  • Iterable의 값을 제공하는 __next__() 특수 메소드를 정의한다.
    • __next__() 는 next(Iterator) 내정함수에 의해 호출된다.
    • 더 이상 제공할 값이 없을 경우 StopIteration Exception을 발생시켜야 한다.
# list -> Iterable 타입
# list_iterator -> list의 iterable
l = [1,2,3] # iterable
l_iterator = iter(l)       # iterator를 조회 -> 리스트가 가진 값들을 하나씩 제공해주는 역할. l.__iter__()
print(type(l_iterator))
>>>
<class 'list_iterator'>
# 리스트 원소들을 list iterator를 이용해서 하나씩 조회
print(next(l_iterator)) # l_iterator.__next__()
>>>
1
---------------------
print(next(l_iterator))
>>>
2
---------------------
print(next(l_iterator))
>>>
3

# for in 문을 구현
def for_in(iterable):
    """
    iterable의 값을 모두 출력하는 함수 -> for in 문을 구현.
    """
    iterator = iter(iterable)
    while True:
        try:
            v = next(iterator)
            print(v)
        except: #StopIteration 예외가 발생하면 break하고 반복문을 중단.
            break

for_in(l)
>>>
1
2
3

📍 for in 문 Iterable의 값을 순환반복하는 과정

  1. 반복 조회할 iterable객체의 iter() 를 호출 하여 Iterator를 구한다.
  2. 매 반복마다 Iterator의 next() 를 호출하여 다음 원소를 조회한다.
  3. 모든 원소들이 다 제공해 StopIteration Exception이 발생하면 반복문을 멈추고 빠져나온다.

🌓 Iterable 구현 클래스

class MyIterable:
    
    def __init__(self, *args):
        """
        *args: 제공해줄 원소들을 가변인자 받는다.
        """
        self.values = args # 튜플
        
    def __str__(self):
        """
        제공할 원소들을 가진 튜플을 문자열로 반환.
        """
        return str(self.values)
    
    def __iter__(self):
        """
        iterable은 __iter__()를 반드시 재정의 해야한다.
        Iterator 객체를 생성해서 반환하도록 처리
        """
        return MyIterator(self.values) # MyIterator 가 MyIterable의 values값을 사용할 수 있도록 전달.

🌓 Iterator 구현 클래스

class MyIterator:
    
    def __init__(self,values):
        """
        Iterator의 initializer()에서 Iterable의 원소들을 받아야 한다. 
				(Iterable 객체 또는 그 원소들을 받도록 처리한다.)
        """
        self.values = values
        self.index = 0 # 몇번째 원소까지 제공했는지 상태를 저장할 attribute
    
    def __next__(self):
        """
        nex(iterator) 했을 때 호출된 메소드. 다음 원소를 제공. 
				제공할 다음 원소가 없으면 StopInteration Exception 발생시킨다.
        """
        # self.values의 값을 하나씩 조회해서 return
        if len(self.values) <= self.index:
            raise StopIteration()
        ret_value = self.values[self.index]
        self.index += 1
        return ret_value

🌓 Iterable과 Iterator를 한 구현 클래스

class MyIterable2:
    
    def __init__(self, *args):
        """
        *args: 제공해줄 원소들을 가변인자 받는다.
        """
        self.values = args # 튜플
        self.index = 0     # 다른점!!!!!!!
        
    def __str__(self):
        """
        제공할 원소들을 가진 튜플을 문자열로 반환.
        """
        return str(self.values)
    
    def __iter__(self):
        """
        iterable은 __iter__()를 반드시 재정의 해야한다.
        Iterator 객체를 생성해서 반환하도록 처리
        """
        return self # MyIterator 가 MyIterable의 values값을 사용할 수 있도록 전달. 
                    # 다른점!!!!!!!!!!!
        
    def __next__(self):
        """
        nex(iterator) 했을 때 호출된 메소드. 
				다음 원소를 제공. 제공할 다음 원소가 없으면 
				StopInteration Exception 발생시킨다.
        """
        # self.values의 값을 하나씩 조회해서 return
        if len(self.values) <= self.index:
            raise StopIteration()
        ret_value = self.values[self.index]
        self.index += 1
        return ret_value
    
    def __getitem__(self, index):
        # 객체[index] : self = 객체, index = index
        return self.values[index]

📍 Generator

  • Iterable과 Iterator를 합친 기능을 함수 형태로 구현(정의)한 것을 generator라고 한다.
    • 제공할 값들을 미리 메모리에 올리지 않고 로직을 통해 값들을 호출자가 필요할 때 마다 제공할 때 유용하다.

🌓 제너레이터 함수에서 값을 반환

  • yield 반환값
    • 반환값을 가지고 호출한 곳으로 돌아간다. 현재 상태(돌아가기 직전 상태)를 기억하면서 돌아간다.
      • 값을 반환하고 일시정지 상태라고 생각하면 된다.
    • 다음 실행시점에 yield 구문 다음 부터 실행된다.
  • return [valuye]
    • generator 함수 종료
    • StopIteration 발생시킨다.
  • Generator 의 원소 조회
    • next(Generator객체)
def test_gen(num=0):
    print('1.', num)
    num += 10
    yield num
    print('2.', num)
    num += 20
    yield num
#   return None (생략)

gen = test_gen(10) # generator를 호출 ==> generator 객체를 생성
#generator를 호출 => next()를 이용
next(gen) # yield 까지 일한다.
>>>
1. 10
20
--------------
next(gen) # 이전 yield 다음부터 다음 yield까지 처리
>>>
2. 20
40

🌓  Generator 표현식 (Generator Comprehension)

  • 컴프리헨션구문을 ( ) 로 묶어 표현한다.
  • 컴프리헨션 구문안의 Iterable의 원소들을 처리해서 제공하는 generator 표현식
  • Generator Comprehension 은 반복 가능한 객체만 만들고 실제 원소에 대한 요청이 왔을 때 값을 생성한다.
    • 메모리 효율이 다른 Comprehension들 보다 좋다.
gen = (i for i in range(10))
print(type(gen))
>>>
<class 'generator'>
-----------------------
for i in gen:
    print(i, end=', ')
>>>>>
1, 2, 3, 4, 5, 6, 7, 8, 9,

2. Decorator (장식자)

📍 파이썬에서 함수는 일급 시민(first class citizen) 이다.

  • 일급 시민 (first class citizen) 이란
    1. 변수에 대입 할 수 있다.
    2. Argument로 사용할 수 있다.
    3. 함수나 메소드의 반환값으로 사용 할 수 있다.

📍 지역함수(Local Function) 란

  • 함수 안에 정의 한 함수를 말한다.
    • 중첩 함수(Nested function) 이라고도 한다.
  • 지역함수가 선언된 함수를 outer function 지역함수는 inner function 이라고 한다.
  • inner function은 outer function의 지역변수를 자유롭게 사용할 수 있다.
  • 기본적으로 inner function은 outer function 안에서만 호출 할 수있다.
  • 단 outer function이 정의된 inner function을 return value로 반환하면 밖에서도 호출 할 수 있다.

📍 Closure (클로저)

  • 지역함수(Inner function)를 정의한 Outer function이 종료되어도 지역함수가 종료될 때까지 outer function의 지역변수들은 메모리에 계속 유지 되어 inner function에서 사용할 수 있다.
  • 파이썬 실행환경은 inner function이 종료될때 까지 outer function의 지역변수들(parameter포함)을 사용할 수 있도록 저장하는 공간이 closure이다.
def outer():
    outer_var = 'outer함수의 변수'
    def inner(num):
        inner_var = "inner함수의 변수"
        print('inner함수의 매개변수 num:', num)
        print(inner_var)
        print(outer_var)
#     print(outer_var) # 지역변수를 호출(조회)
#     inner(100)       # 지역함수를 호출
    return inner # 함수를 반환.

func = outer()
func(300)
>>>
inner함수의 매개변수 num: 300
inner함수의 변수
outer함수의 변수

📍 Decorator (장식자)

🌓 개요

  • 기존의 함수를 수정하지 않고 그 함수 전/후에 실행되는 구문을 추가할 수 있도록 하는 함수를 말한다.
  • 기존 함수코드를 수정하지 않고 새로운 기능의 추가를 쉽게 해준다.
  • 함수의 전/후처리 하는 구문을 필요하면 붙이고 필요 없으면 쉽게 제거할 수 있다

🌓  Decorator 구현 및 사용

구현)

  1. 전/후처리 기능을 추가할 함수를 parameter로 받는다.
  2. 그 함수 호출 전후로 추가할 기능을 작성한 지역함수를 정의한다.
  3. 2번의 함수를 반환한다.

호출)

  • @decorator이름를 적용하고자하는 함수 선언전에 기술한다.

def a():
    print('안녕하세요.')
a()
>>>
안녕하세요.
def dash_decorator(func):
# func : 함수 - 전/후 처리를 추가할 original 함수
    def wrapper():    # 관례적으로 inner 함수는 wrapper라고 표현한다.
        print('-'*20) # 전처리
        func()
        print('-'*20) # 후처리
    return wrapper

w_a = dash_decorator(a)
w_a()
>>>
--------------------
안녕하세요.
--------------------
def shap_decorator(func):
    
    def wrapper():
        print("#"*20)
        func()
    return wrapper

# c()함수를 호출하면 c함수를 dash_decorator()함수의 argument로 전환해서 호출하고 
# 거기 반환되는 inner 함수를 실행시켜라.
@dash_decorator
@shap_decorator
def c():
    print("이것은 c 함수입니다.")
c()
>>>
--------------------
####################
이것은 c 함수입니다.
--------------------
반응형

'[T.I.L] : Today I Learned > Python' 카테고리의 다른 글

[Python] 11강_파이썬 정규표현식  (0) 2023.04.09
[Python] 9-2강_입출력  (2) 2023.04.09
[Python] 9-1강_Path  (0) 2023.04.09
[Python] 8강_예외처리  (0) 2023.04.09
[Python] 7강_패키지 모듈 import  (0) 2023.04.05