240117 복습

이규성·2024년 1월 17일
0

오늘 배운거:
링크텍스트

클로저

  • 함수 내 함수가 외부 변수를 참조하여 보존
# 클로징(함수가 사용되어 사라지는) 되어야하는 공간(변수)에 접근하는 것을 클로저라고 합니다.

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function

inner = outer_function(100)
inner(200) # inner 입장에서 100을 변경할 수 있는 방법이 없습니다.

클로저의 예시 코드다 함수는 사용이 끝나면 그 안의 값들은 사라진다.
주석에 써져있는대로 (함수가 사용되어 사라지는) 공간(변수)에 접근하는 것을 클로저라고 한다.

def calc(oper, original):
	def add(x, y):
    	return original + x + y
	def sub(x,y):
    	return original - x -y
    if oper == 'add':
    	return add
    if oper == 'sub':
    	return sub
 
 add = calc('add', 1000)
 add(10, 20) # 1000의 값에 접근 할 수 없다.
 
 sub = calc('sub', 100)
 sub(20, 10)

클로저의 또 다른 예시 코드 해당 코드를 통해 알 수 있는 점은
클로저를 통해 만든 객체(일차함수) addcalc함수의
original파라미터에 접근하지 못한다는 것이다.

데코리에터

def simple_decorator(function):
    def wrapper(a, b): # point 1
        print('전')
        result = function(a) # point 2
        print(result)
        print('후')
        return result
    return wrapper

@simple_decorator
def hello(a):
    return a ** 2

hello(10, 20) # => simple_decorator(hello)(10, 20) => wrapper(10, 20)

데코레이터를 잘 설명해주는 예시코드,
hello(10, 20)을 실행하면, hello라는 함수가 실행되는게 아니다~
hello라는 함수가 simple_decorator의 파라미터인 function의 아규먼트로 들어가서 실행된다.
위 코드안의 주석에도 써져있듯이 simple_decorator(hello)(10,20)와 같이 작동된다고
보면 편할 것이다.

그런데 여기서 의문? 굳이 데코레이터를 왜 사용하는가에 대한 의문이 생길 수 있을 것이다.
위 코드만봐도 데코레이터 없이 그냥 따로 함수를 만들면되는 거 아니야? 라고 할 수 있는 부분
나 역시 그렇게 생각했으나 객체지향형 프로그래밍을 할때는 이방법이 매우 효율적이고,가독성 부분에서도 깔끔하다고 강사님이 말씀하셨다. 나도 아직 객체지향형이라는 개념자체가 확 와 닿는 편이 아니여서 좀더 공부를 하면서 필요성을 체득해야 함을 느꼈다.

이터레이터와 제너레이터

이터레이터란, 값을 차례대로 꺼낼 수 있는 객체
시퀀스형 자료형이란 index가 있고 indexing, slicing이 가능한 자료형
제너레이터는 이터레이터를 만드는 함수

내용의 이해를 좀더 돕기 위해서,dict자료형은 대부분의 이터레이터가 속해있는 시퀸스 자료형은 아니지만,
값을 차례대로 꺼낼 수 있는 객체라는 점에서 이터레이터라고 정의 할 수 있겠다.

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
x = iter(MyIterator(5))
print(next(x))

print(next(x))
print(next(x))
print(next(x))
print(next(x))
print(next(x)) # for문이 에러때문에 멈춤

순수하게 이터레이터만으로 range와 for문의 작동 방식을 구현했다.
함수가 선언되면 iter 메서드는 한번만 선언되고, 그다음은 next메서드만 작동한다.
next메서드는 특정조건을 만족하면 에러를 일으켜서 종료된다.

def my_gen():
	x = 10
    yield x
    x = 20 
    yield x
    x = 30
    yield x
    x = 40
    yield x
    
list(zip('hello', my_gen()))

제네레이터는 이터레이터를 생성해주는 함수로,yield키워드를 사용하여 만든다.

해당 코드를 실행하면
[('h', 10), ('e', 20), ('l', 30), ('l', 40)]
라는 값이 나오는데 제너레이터를 통해 이터레이터를 생성하여 이런식의 코드를 짜주면
같은 값이 나오는
list(zip('hello', [10, 20, 30, 40]))
이런 코드 보다도 메모리 효율이 훨신 좋게 나온다. <= 이터레이터 특성상 해당 값을 즉석에서 만들어 할당하기 때문이다,리스트 같은 경우는 이미 값을 할당하고 있을테니 메모리 효율을 비교하면
이터레이터쪽이 낫다.

profile
꾸준한

0개의 댓글