Python

Python(17) args, kwargs, 이터레이터와 제너레이터

UserDonghu 2023. 9. 20. 19:57

*args : 가변 아규먼트, 여러가지 인자들을 가변인수로 받음. tuple로 저장

**kwargs : 딕셔너리로 가변인수를 받음.

 

a, b, *c = 10, 20, 30, 40, 50
print(a) # 10
print(b) # 20
print(c) # (30, 40, 50)

 

args 패킹

def func(*args):
    print(args) # (10, 20, 30)

func(10, 20, 30)

# 10, 20, 30 => *args => (10, 20, 30)

 

args 언패킹

def func(a, b, c):
    print(a, b, c) # 10 20 30

args = (10, 20, 30)
func(*args)

# (10, 20, 30) => *args => 10, 20, 30

 

kwargs 패킹

def func(a=100, **kwargs):
    print(a, kwargs)

func(1000, i=1, j=2, k=3)

# a=1, b=2, c=3 => **kwargs => {'a':1, 'b':2, 'c':3}

 

kwargs 언패킹

def func(d=100, c=200, b=300, a=400):
    print(d, c, b, a)

kwargs = {'a':1, 'b':2, 'c':3}
func(**kwargs)

# func(d = 100, c = 3, b = 2, a = 1) 랑 같음
# {'a':1, 'b':2, 'c':3} => **kwargs => a=1, b=2, c=3

 

주의할점

# *args뒤에 일반 변수를 선언하지 못함
def print_args(a, b, *args, d):
    print(args)
    for x in args:
        print(x)

print_args(100, True, 'Licat', 'Hello', 10) # Error

 

def print_args(a, b, *args, *c): # *args뒤에 또다른 가변 아규먼트를 넣지 못함
    print(args)
    for x in args:
        print(x)

print_args(100, True, 'Licat', 'hello', 10) # Error

 

이터레이터

값을 차례로 꺼내서 쓸 수 있는 객체

반드시 __iter__와 __next__ 매직 메서드 정의해야함.

 

이터레이터 예시

class MyIterator:
    def __init__(self, stop):
        self.current_value = 0  # 현재 값
        self.stop = stop  # 순회를 멈출 값

    def __iter__(self):
        return self

    def __next__(self):
        if self.current_value >= self.stop:
            raise StopIteration
        result = self.current_value
        self.current_value += 1
        return result

my_iterator = MyIterator(5)

for i in my_iterator:
    print(i)

# for을 만나면 __iter__가 실행이 되고, 반복이 시작되면 __next__를 호출함

 

for문 작동 원리

class MyIterator:
    def __init__(self, stop):
        self.current_value = 0  # 현재 값
        self.stop = stop  # 순회를 멈출 값

    def __iter__(self):
        return self

    def __next__(self):
        if self.current_value >= self.stop:
            raise StopIteration
        result = self.current_value
        self.current_value += 1
        return result

my_iterator = MyIterator(5)

i = iter(my_iterator) # 여기서부터
print(next(i)) # 0
print(next(i)) # 1
print(next(i)) # 2
print(next(i)) # 3
print(next(i)) # 4
print(next(i)) # error # 여기까지가 for 작동 원리

 

제너레이터

이터레이터를 생성해주는 함수. yield를 이용

yield를 만나면 다음에 다시 호출될 때 까지 기다림.

def my_generator(data):
    for i in data:
        yield i * 10

for i in my_generator([1, 2, 3]):
    print(i)
    
# 10
# 20
# 30
def my_generator():
    x = 10
    yield x
    x = 20
    yield x
    x = 30
    yield x

for i in my_generator():
    print(i)
    
# 10
# 20
# 30
def my_generator():
    count = 1
    while True:
        yield count
        count += 1

print(list(zip(['a', 'b', 'c'], my_generator()))) # [('a', 1), ('b', 2), ('c', 3)]

 

제너레이터 컴프리헨션

gen = (i for i in range(2, 11, 2))
print(type(gen))
for i in gen:
    print(i)

# <class 'generator'>
# 2
# 4
# 6
# 8
# 10

 

연습문제

# 회전초밥집에 들어갔습니다. 초밥은 아래와 같은 양식으로 나옵니다.
[['광어초밥', 1000], ['연어초밥', 2000], ['계란초밥', 3000]]
# 각 초밥은 몇 개 나올지 알 수 없습니다.
# 각 초밥은 1000원씩 비싸집니다.
# 초밥에 '어'가 나오는 초밥만 먹습니다.
# 내가 먹은 초밥의 비용을 계산하는 코드를 작성해 주세요.

def sushi_gen(data):
    for sushi in data:
        if '어' in sushi[0]:
            yield sushi[1]

def solution(data):
    price = 0
    for sushi_price in sushi_gen(data):
        price += sushi_price
    return price

data = [['광어초밥', 1000], ['연어초밥', 2000], ['계란초밥', 3000], ['문어초밥', 4000], ['장어초밥', 5000]]
    
print(f'내가 먹은 초밥의 총 비용은 {solution(data)}원 입니다')
# 문제1
# 다음과 같이 동작하는 제너레이터 함수 fibonacci(n)를 완성하세요. 
# 주어진 숫자 n까지의 피보나치 수열을 반환합니다.

def fib(n):
    pre = 1
    next = 1
    count = 0
    while True:
        temp = pre + next
        yield pre
        pre, next = next, temp
        count += 1
        if count == n:
            break

for i in fib(5):
    print(i)

'''
출력
1
1
2
3
5
'''

# 문제2
# 주어진 함수의 실행 시간을 측정하여 출력하는 데코레이터 time_it를 작성하세요. 
# (힌트: time 모듈의 time() 함수를 사용하세요.)

import time

def time_it(func):
    def wrapper():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f"{end_time - start_time:.4f}s")
    return wrapper

@time_it
def main():
    def fib(n):
        pre = 1
        next = 1
        count = 0
        while True:
            temp = pre + next
            yield pre
            pre, next = next, temp
            count += 1
            if count == n:
                break

    for i in fib(20):
        print(i)

main()

'Python' 카테고리의 다른 글

Python(19) 정규표현식  (0) 2023.09.22
Python(18) f-string 문법  (0) 2023.09.21
Python(16) 일급 함수와 고차 함수, 클로저, 데코레이터  (0) 2023.09.20
Python(15) 예외 처리와 오류 관리  (0) 2023.09.19
Python(14) 모듈  (0) 2023.09.19