Загружаем каталог…
Загружаем каталог…
07-2 클로저와 데코레이터 목차 1. 클로저란? 2. 클래스를 이용한 방법 3. __call__ 메서드 4. 클로저 만들기 5. 데코레이터란? 6. @ 를 이용한 데코레이터 7. *args , ` kwargs`** 1. 클로저란? 클로저(Closure) 는 쉽게 말하면 외부 함수가 종료된 이후에도 외부 함수의 변수를 기억하고 사용하는 내부 함수 이다. 말로만 보면 조금 어렵기 때문에 예제를 통해 이해해 보자. 항상 3을 곱하는 함수 어떤 숫자를 입력하면 항상 3 을 곱해 주는 함수를 만들어 보자. def mul3(n): return n * 3 사용 print(mul3(10)) 실행 결과 30 이번에는 항상 5 를 곱하는 함수가 필요하다고 해보자. def mul5(n): return n * 5 사용 print(mul5(10)) 결과 50 하지만 필요한 숫자가 계속 늘어난다면 mul3() mul5() mul6() mul7() mul8() ... 처럼 함수를 계속 만들어야 한다. 이것은 비효율적이다. 2. 클래스를 이용한 방법 먼저 클래스를 사용하면 이 문제를 해결할 수 있다. class Mul: def __init__(self, m): self.m = m def mul(self, n): return self.m * n 객체를 만들어 보자. mul3 = Mul(3) mul5 = Mul(5) 여기서 Mul(3) 을 실행하면 self.m = 3 이 저장된다. 따라서 mul3.mul(10) 을 실행하면 self.m * n 3 * 10 = 30 이 된다. 전체 코드는 다음과 같다. class Mul: def __init__(self, m): self.m = m def mul(self, n): return self.m * n mul3 = Mul(3) mul5 = Mul(5) print(mul3.mul(10)) print(mul5.mul(10)) 실행 결과 30 50 3. __call__ 메서드 앞에서 객체를 사용하려면 mul3.mul(10) 처럼 메서드를 직접 호출해야 했다. 하지만 __call__() 을 사용하면 객체 자체를 함수처럼 호출 할 수 있다. class Mul: def __init__(self, m): self.m = m def __call__(self, n): return self.m * n 이제 mul3 = Mul(3) 으로 객체를 만든 뒤 print(mul3(10)) 처럼 사용할 수 있다. 실행 결과 30 __call__ 동작 이해하기 다음 코드를 실행하면 mul3(10) 파이썬은 내부적으로 mul3.__call__(10) 을 호출한다고 생각하면 된다. 즉, mul3(10) ↓ __call__(10) ↓ self.m * 10 ↓ 3 * 10 ↓ 30 이다. 4. 클로저 만들기 클래스를 사용하지 않고 함수만으로도 같은 기능을 만들 수 있다. def mul(m): def wrapper(n): return m * n return wrapper 처음 보면 가장 헷갈리는 부분은 이것이다. return wrapper wrapper() 를 실행하는 것이 아니라 함수 자체를 반환 하고 있다. 함수를 변수에 저장할 수 있다 파이썬에서는 함수도 객체이기 때문에 변수에 저장할 수 있다. 예를 들어 def hello(): print("Hello") a = hello 라고 하면 a 에는 hello 함수 자체가 저장된다. 따라서 a() 를 실행하면 Hello 가 출력된다. 5. 클로저 동작 과정 다시 다음 코드를 살펴보자. def mul(m): def wrapper(n): return m * n return wrapper 그리고 mul3 = mul(3) 을 실행한다고 해보자. 1 mul(3) 호출 mul(3) 이므로 m = 3 이 된다. 2 내부 함수 생성 def wrapper(n): return m * n wrapper() 함수가 만들어진다. 이 함수는 외부 함수의 변수 m 을 사용하고 있다. 3 wrapper 함수 반환 return wrapper 에 의해 wrapper 함수 자체가 반환된다. 결과적으로 mul3 = mul(3) 에서 mul3 에는 wrapper 함수가 들어간다. 4 mul 함수 종료 여기서 중요한 점이 있다. mul() 함수는 이미 실행이 끝났다. 그런데 mul3(10) 을 실행하면 결과가 어떻게 될까? 30 이 출력된다. 왜 m = 3 이 남아 있을까? 원래라면 mul() 함수가 끝났으므로 내부 변수 m 도 사라질 것처럼 보인다. 하지만 반환된 wrapper() 함수가 m 을 사용하고 있기 때문에 m = 3 이라는 값을 기억하고 있다. 이것이 바로 클로저 이다. mul(3) ↓ m = 3 ↓ wrapper 함수 생성 ↓ wrapper가 m = 3을 기억 ↓ mul 함수 종료 ↓ mul3(10) ↓ 3 × 10 ↓ 30 6. 여러 개의 클로저 만들기 다음처럼 사용할 수 있다. def mul(m): def wrapper(n): return m * n return wrapper mul3 = mul(3) mul5 = mul(5) print(mul3(10)) print(mul5(10)) 실행 결과 30 50 여기서 mul3 = mul(3) 은 m = 3 을 기억하고, mul5 = mul(5) 는 m = 5 를 기억한다. 즉 서로 다른 값을 기억하는 함수가 만들어진다. mul3 └─ m = 3 기억 mul5 └─ m = 5 기억 📌 클로저 핵심 def outer(a): def inner(b): return a + b return inner 이 구조를 기억하면 된다. 외부 함수 ↓ 외부 변수 생성 ↓ 내부 함수 생성 ↓ 내부 함수가 외부 변수 사용 ↓ 내부 함수 반환 ↓ 외부 함수 종료 ↓ 내부 함수는 외부 변수를 계속 기억 7. 데코레이터란? 데코레이터(Decorator) 는 기존 함수의 코드를 직접 수정하지 않고 새로운 기능을 추가하는 방법 이다. 데코레이터를 이해하려면 앞에서 배운 클로저 가 중요하다. 8. 함수 실행 시간 측정하기 다음 함수가 있다고 해보자. def myfunc(): print("함수가 실행됩니다.") 이 함수가 실행되는 시간을 측정하고 싶다면 time 모듈을 사용할 수 있다. import time def myfunc(): start = time.time() print("함수가 실행됩니다.") end = time.time() print("함수 수행시간: %f 초" % (end - start)) 실행 myfunc() 결과 함수가 실행됩니다. 함수 수행시간: 0.0000XX 초 문제점 함수가 한 개라면 괜찮다. 하지만 실행 시간을 측정해야 하는 함수가 func1() func2() func3() func4() ... 처럼 많다면 모든 함수에 start = time.time() ... end = time.time() 을 반복해서 작성해야 한다. 이런 경우 데코레이터 를 사용할 수 있다. 9. 실행 시간을 측정하는 데코레이터 만들기 import time def elapsed(original_func): def wrapper(): start = time.time() result = original_func() end = time.time() print("함수 수행시간: %f 초" % (end - start)) return result return wrapper 여기에서도 클로저와 같은 구조가 사용된다. elapsed() ↓ wrapper() 생성 ↓ wrapper 반환 하지만 이번에는 elapsed() 가 함수를 입력값으로 받는다. def elapsed(original_func): 10. 함수도 인수로 전달할 수 있다 다음 함수가 있다고 해보자. def myfunc(): print("함수가 실행됩니다.") 그리고 decorated_myfunc = elapsed(myfunc) 을 실행한다. 여기서 myfunc() 라고 하지 않은 점이 중요하다. elapsed(myfunc) 처럼 함수 자체를 전달했다. 동작 과정 decorated_myfunc = elapsed(myfunc) 실행 myfunc 함수 ↓ elapsed()에 전달 ↓ original_func = myfunc ↓ wrapper 함수 생성 ↓ wrapper 함수 반환 ↓ decorated_myfunc에 저장 이제 decorated_myfunc() 을 실행하면 실제로는 wrapper() 가 실행된다. wrapper 내부 def wrapper(): start = time.time() result = original_func() end = time.time() print("함수 수행시간: %f 초" % (end - start)) return result 여기서 original_func() 는 처음 전달했던 myfunc() 를 의미한다. 따라서 wrapper 실행 ↓ 시작 시간 저장 ↓ myfunc 실행 ↓ 종료 시간 저장 ↓ 실행 시간 출력 이 된다. 11. 데코레이터 구조 이해하기 전체 코드를 다시 보면 import time def elapsed(original_func): def wrapper(): start = time.time() result = original_func() end = time.time() print("함수 수행시간: %f 초" % (end - start)) return result return wrapper def myfunc(): print("함수가 실행됩니다.") decorated_myfunc = elapsed(myfunc) decorated_myfunc() 핵심은 elapsed(myfunc) 가 기존 함수 myfunc 를 감싸는 새로운 wrapper 함수를 만들어 준다는 것이다. 그래서 데코레이터를 함수를 꾸며 주는 함수 라고 생각할 수 있다. 12. @ 를 이용한 데코레이터 파이썬에서는 위의 코드를 더욱 간단하게 표현할 수 있다. @elapsed def myfunc(): print("함수가 실행됩니다.") 전체 코드는 다음과 같다. import time def elapsed(original_func): def wrapper(): start = time.time() result = original_func() end = time.time() print("함수 수행시간: %f 초" % (end - start)) return result return wrapper @elapsed def myfunc(): print("함수가 실행됩니다.") myfunc() @elapsed 의 의미 다음 코드는 @elapsed def myfunc(): print("함수가 실행됩니다.") 개념적으로 다음과 같다. myfunc = elapsed(myfunc) 즉 기존 myfunc ↓ elapsed에 전달 ↓ wrapper 반환 ↓ myfunc에 다시 저장 된다. 따라서 이제 myfunc() 을 실행하면 단순히 원래 myfunc() 만 실행되는 것이 아니라 wrapper() 가 실행된다. 13. 데코레이터 실행 흐름 @elapsed def myfunc(): print("함수가 실행됩니다.") 그리고 myfunc() 을 실행하면 myfunc() ↓ wrapper() 실행 ↓ 시작 시간 기록 ↓ 원래 myfunc() 실행 ↓ 종료 시간 기록 ↓ 수행 시간 출력 이 된다. 14. 매개변수가 있는 함수에서는 문제가 발생한다 이번에는 myfunc() 가 값을 입력받도록 변경해 보자. @elapsed def myfunc(msg): print("'%s'을 출력합니다." % msg) 사용 myfunc("You need python") 그런데 기존의 wrapper() 는 다음과 같다. def wrapper(): 아무런 인수도 받을 수 없다. 그런데 우리는 myfunc("You need python") 처럼 인수를 전달하고 있다. 따라서 오류가 발생한다. TypeError 15. *args 와 **kwargs 이 문제를 해결하기 위해 *args 와 **kwargs 를 사용할 수 있다. *args *args 는 여러 개의 일반 인수를 받을 수 있다. def func(*args): print(args) 실행 func(1, 2, 3) 결과 (1, 2, 3) 즉 args 는 튜플 이 된다. *args ↓ 일반적인 여러 인수 ↓ 튜플 16. **kwargs **kwargs 는 이름=값 형태의 인수를 받을 수 있다. def func(**kwargs): print(kwargs) 실행 func(name="pingu", age=20) 결과 {'name': 'pingu', 'age': 20} 즉 kwargs 는 딕셔너리 가 된다. **kwargs ↓ key=value 형태의 인수 ↓ 딕셔너리 17. *args , **kwargs 함께 사용하기 def func(*args, **kwargs): print(args) print(kwargs) 실행 func(1, 2, 3, name="foo", age=3) 결과 (1, 2, 3) {'name': 'foo', 'age': 3} 정리하면 매개변수 받는 값 저장 형태 *args 일반 위치 인수 튜플 **kwargs key=value 형태 인수 딕셔너리 18. 데코레이터에 *args , **kwargs 적용하기 이제 wrapper() 를 다음처럼 변경한다. def wrapper(*args, **kwargs): 그리고 원래 함수를 실행할 때도 그대로 전달한다. original_func(*args, **kwargs) 전체 코드는 다음과 같다. import time def elapsed(original_func): def wrapper(*args, **kwargs): start = time.time() result = original_func(*args, **kwargs) end = time.time() print("함수 수행시간: %f 초" % (end - start)) return result return wrapper 이제 어떤 형태의 인수를 사용하는 함수라도 데코레이터를 적용하기 쉬워진다. 19. 최종 코드 import time def elapsed(original_func): def wrapper(*args, **kwargs): start = time.time() result = original_func(*args, **kwargs) end = time.time() print("함수 수행시간: %f 초" % (end - start)) return result return wrapper @elapsed def myfunc(msg): print("'%s'을 출력합니다." % msg) myfunc("You need python") 실행 결과 'You need python'을 출력합니다. 함수 수행시간: 0.0000XX 초 20. 클로저와 데코레이터 차이 둘은 서로 관련이 있지만 완전히 같은 것은 아니다. 구분 클로저 데코레이터 핵심 외부 함수의 값을 기억 기존 함수에 기능 추가 구조 내부 함수를 반환 함수를 받아 새로운 함수를 반환 사용 목적 상태나 값을 기억 공통 기능 재사용 예시 곱할 숫자 기억 실행 시간 측정 데코레이터는 클로저 구조를 활용하는 대표적인 방법 이라고 이해하면 쉽다. 21. 클로저 구조 다시 보기 def mul(m): def wrapper(n): return m * n return wrapper mul(3) ↓ m = 3 ↓ wrapper 생성 ↓ wrapper가 m을 기억 ↓ wrapper 반환 ↓ mul3(10) ↓ 30 핵심은 내부 함수가 외부 함수의 변수를 기억한다. 22. 데코레이터 구조 다시 보기 def decorator(original_func): def wrapper(*args, **kwargs): # 추가 기능 result = original_func(*args, **kwargs) # 추가 기능 return result return wrapper 사용 @decorator def myfunc(): pass 구조는 원래 함수 ↓ 데코레이터에 전달 ↓ wrapper가 원래 함수 기억 ↓ wrapper 반환 ↓ 원래 함수 대신 wrapper 실행 이라고 이해하면 된다. 📌 핵심 정리 클로저 def outer(a): def inner(b): return a + b return inner inner() 는 outer() 함수가 종료된 이후에도 a 의 값을 기억한다. 외부 함수의 값 ↓ 내부 함수가 기억 ↓ 외부 함수 종료 후에도 사용 가능 __call__ 객체를 함수처럼 사용할 수 있게 해주는 특수 메서드이다. class Test: def __call__(self): print("실행") a = Test() a() 데코레이터 기존 함수를 수정하지 않고 기능을 추가한다. @decorator def myfunc(): pass 개념적으로 myfunc = decorator(myfunc) 와 같은 구조이다. *args 여러 개의 일반 인수를 받는다. def func(*args): print(args) 튜플로 저장 **kwargs key=value 형태의 여러 인수를 받는다. def func(**kwargs): print(kwargs) 딕셔너리로 저장 💡 가장 중요한 흐름 클로저 외부 함수 실행 ↓ 외부 변수 생성 ↓ 내부 함수 생성 ↓ 내부 함수 반환 ↓ 외부 함수 종료 ↓ 내부 함수가 외부 변수 기억 데코레이터 기존 함수 ↓ 데코레이터에 전달 ↓ wrapper 함수 생성 ↓ 기존 함수 + 추가 기능 ↓ wrapper 반환 ↓ 함수 호출 클로저는 "값을 기억하는 함수", 데코레이터는 "기존 함수를 감싸서 기능을 추가하는 함수"라고 먼저 이해하면 훨씬 쉽다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
점프 투 파이썬 DAY16(클로저와 데코레이터). 07-2 클로저와 데코레이터 목차 1. 클로저란? 2. 클래스를 이용한 방법 3. __call__ 메서드 4. 클로저 만들기 5. 데코레이터란? 6. @ 를 이용한 데코레이터 7. *args , ` kwargs`** 1. 클로저란? 클로저(Closure) 는 쉽게 말하면 외부 함수가 종료된 이후에도 외부 함수의 변수를 기억하고 사용하는 내부 함수 이다. 말로만 보면 조금 어렵기 때문에 예제를 통해 이해해 보자. 항상 3을 곱하는 함수 어떤 숫자를 입력하면 항상 3 을 곱해 주는 함수를 만들어 보자. def mul3(n): return n * 3 사용 print(mul3(10)) 실행 결과 30 이번에는 항상 5 를 곱하는 함수가 필요하다고 해보자. def…
Открыть источник