오늘 배운거:
링크텍스트
- 함수 내 함수가 외부 변수를 참조하여 보존
# 클로징(함수가 사용되어 사라지는) 되어야하는 공간(변수)에 접근하는 것을 클로저라고 합니다.
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)
클로저의 또 다른 예시 코드 해당 코드를 통해 알 수 있는 점은
클로저를 통해 만든 객체(일차함수) add
는 calc
함수의
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]))
이런 코드 보다도 메모리 효율이 훨신 좋게 나온다. <= 이터레이터 특성상 해당 값을 즉석에서 만들어 할당하기 때문이다,리스트 같은 경우는 이미 값을 할당하고 있을테니 메모리 효율을 비교하면
이터레이터쪽이 낫다.