오늘은 전날 강사님이 제출해 주셨던 복습 문제를 풀어보는 시간으로 Python 심화 부분을 공부했습니다.
복습 문제들은 주말에 풀어두려 아껴두었지만 여러 사람이 풀었던 코드를 보고 풀이를 진행하니 또 다른 느낌이라 재미있었어요.
교안과 문제 위주로 정리 하고 부족한 이론은 따로 찾아서 같이 정리하였습니다.
클로저 (Closure)
지역 변수와 코드를 묶어서 사용하고 싶을 때 활용 (프로그램의 흐름을 변수에 저장)
클로저에 속한 지역 변수는 바깥에서 직접 접근할 수 없으므로 데이터를 숨기고 싶을 때 활용
+ 클로저의 이해를 위해 일급 객체(first-class citizen)에 대해서 이해할 것
1. 어떤 함수의 내부 함수일 것
2. 그 내부 함수가 외부 함수의 변수를 참조할 것
3. 외부 함수가 내부 함수를 리턴할 것
= 어떤 함수의 내부 함수가 외부 함수의 변수(프리변수)를 참조할 때,
외부 함수가 종료된 후에도 내부 함수가 외부 함수의 변수를 참조할 수 있도록 어딘가에 저장하는 함수
+ 프리변수 : 어떤 함수에서 사용되지만 그 함수 내부에서 선언되지 않은 변수를 뜻함
Python에서 클로저는 함수 객체의 __closure__ 속성을 통해 구현됨
= 이 속성은 내부 함수가 참조하는 외부 함수의 변수를 저장하는 튜플 (내부 함수가 외부 함수의 변수를 계속 참조할 수 있음)
ex)
# 1번 : 클로저가 아닌경우
def outer_function() :
def inner_function() :
return 100 + 100
return inner_function
# 2번 : 클로저인 경우
def outer_function(x) :
def inner_function(y) :
return x + y
return inner_function
inner = outer_function(100)
inner(200) # inner 입장에서 100을 변경할 수 있는 방법이 없음
위 1번에서는 inner_function이 외부 변수를 참조하고 있진 않음
함수를 반환하니 고차함수이지만, 클로저는 아님
2번에서 outer_function은 inner_function을 반환함, inner_function은 외부 함수의 변수 x를 참조하고 있음
따라서 outer_function을 호출하면 x = 10이라는 상태를 기억하는 클로저가 됨
closure(5)를 호출하면 inner_function(5)를 호출하는 것과 동일한 결과
이는 클로저가 inner_function의 코드뿐만 아니라 x = 10이라는 상태도 기억하고 있기 때문
데코레이터 (Decorator)
함수나 메소드를 꾸며주는 역할을 하는 기능 (코드의 간결성, 재사용성을 높임)
어떤 함수가 있을 때 해당 함수를 직접 수정하지 않고 함수에 기능을 추가하고자 할 때 데코레이터를 사용
ex)
def my_decorator(func) :
def wrapper() :
# func를 실행하기 전에 수행할 작업
print("데코레이터에서 수행하는 작업")
func() # 인자로 받은 함수 실행
# func를 실행한 후에 수행할 작업
print("데코레이터에서 끝나는 작업")
return wrapper
1. my_decorator 함수는 다른 함수(func)를 받아 wrapper 함수를 반환
@my_decorator
def say_hello():
print("안녕하세요!")
2. @데코레이터 형식으로 함수를 정의하면, 해당 함수가 자동으로 my_decorator로 감싸지게됨
3. say_hello 함수를 호출하면 데코레이터에 정의된 작업이 함께 수행
say_hello()
4. 결과 출력
데코레이터에서 수행하는 작업
안녕하세요!
데코레이터에서 끝나는 작업
그 외에도 중첩 데코레이터, 동적 데코레이터, 클래스형 데코레이터 등 있습니다.
필요하에 사용하게 될 경우 데코레이터에 대해 더 자세한 코드와 풀이로 정리하겠습니다.
+ lambda
간결한 함수를 빠르고 익명으로 생성할 수 있게 함
작은 규모의 함수를 정의할 때 사용
lambda parameters: expression
arguments : 함수에 전달되는 매개변수를 나타냄
expression : 매개변수를 사용하여 계산되는 표현식을 나타냄
parameters : lambda 함수 매개변수 여러 개의 매개변수는 쉼표(,)로 구분
square = lambda x: x*x
print(square(5))
아규먼츠(args)와 키워드 아규먼츠(kwargs)
= 가변 아규먼트 또는 가변 매개변수, 키워드 가변 아규먼트 또는 키워드 가변 매개변수로 부름
애스터리스크(*) 1개 : 튜플이나 리스트를 패킹, 언패킹 하는 데 사용
2개 : 딕셔너리를 패킹, 언패킹 하는 데 사용
변수명은 꼭 args나 kwargs를 사용하지 않아도 작동함
아규먼츠 (*args, arguments)
함수 내 파라미터로 사용되면 여러 아규먼트를 묶어 받을 수 있음
아규먼트로 전달된 값이 모두 하나에 변수에 묶임 : 패킹
# 패킹
def func(*args) :
print(args)
func(10, 20, 30)
# 10, 20, 30 => *args => (10, 20, 30)
# 언패킹
def func(a, b, c) :
print(a, b, c)
args = (10, 20, 30)
func(*args)
# (10, 20, 30) => *args => 10, 20, 30
+ *args뒤에 일반 변수를 선언하지 못함
키워드 아규먼츠 (**kwargs, keyword arguments)
딕셔너리로 가변 아규먼트를 받을 수 있는 방법
애스터리스크(*)를 1개만 사용하면 key만 넘어가고 key와 value 모두 넘기기 위해선 2개를 사용
# 패킹
def func(a=100, **kargs) :
print(a, kargs)
func(1000, i=1, j=2, k=3)
# a=1, b=2, c=3 => **kwargs => {'a': 1, 'b': 2, 'c': 3}
# 언패킹
def func(d=100, c=200, b=300, a=400) :
print(d, c, b, a)
kwargs = {'a': 1, 'b': 2, 'c': 3}
func(**kwargs)
# {'a': 1, 'b': 2, 'c': 3} => **kwargs => a=1, b=2, c=3
함수를 정의할 때 *args와 **kwargs를 함께 사용하면, 함수는 다양한 형태의 인자들을 유연하게 받을 수 있음
일반적으로 *args를 먼저 위치시킨 후 그다음에 **kwargs를 위치시킴
그리고 키워드 인자들은 명시적으로 이름을 지정해서 전달해야 함
주의할 점은 인자를 전달할 때 위치 인자들은 항상 키워드 인자들 앞에 와야 함, 그렇지 않으면 Python은 error 발생
이터레이터(Iterator)
값을 차례대로 꺼낼 수 있는 객체를 의미
이터레이터 객체를 생성하기 위해 __iter__와 __next__ 두 가지 매직 메서드를 구현해야 함
__iter__ : 이터레이터 객체 자신을 반환, for 루프 등이 이터레이터를 초기화할 수 있게 함
__next__ : 순회를 할 때 값을 하나씩 반환, 더 이상 반환할 값이 없을 때는 StopIteration 예외를 발생시켜 순회가 끝났음을 알림
= for문을 만나면 __iter__를 가장 먼저 실행합니다. 그리고 그다음부터는 __next__를 호출하며 다음 값을 꺼냄
이터레이터는 값의 언패킹이 가능
= 이터레이터에서 반환하는 값들을 변수 여러 개에 한 번에 할당할 수 있음
언패킹은 __next__로 반환된 값을 순서대로 받게 됨
제너레이터(Generator)
이터레이터를 생성해 주는 함수, yield 키워드를 사용
제너레이터는 함수 실행 중간에 값을 반환할 수 있고, 이후에 함수의 실행을 계속할 수 있는 특징이 있음
= 무한 루프를 통해 무한한 시퀀스를 생성하는 제너레이터는 조건을 만족하는 시점에서 break문을 사용하여 반복을 중단
제너레이터는 zip, map, filter와 같은 파이썬의 빌트인 함수와 함께 사용하여 코드를 더 간결하고 효율적으로 만들 수 있음
+ 제너레이터 컴프리헨션
리스트 컴프리헨션과 매우 유사한 구조
생성 시점에 모든 요소를 메모리에 로드하지 않는다는 점에서 차이가 있음
(큰 데이터셋을 처리할 때 매우 유용하며 메모리 사용량을 줄일 수 있음)
+ Nonlocal
Python 키워드 중 하나, 중첩 함수 내부에서 바깥 함수의 변수를 참조
주로 클로저(closure)에서 변수의 값을 변경하고자 할 때 사용
ex)
# nonlocal
a = 10
def f() :
a = 100
print(f'f a: {a}')
def ff() :
a = 1000
print(f'ff a: {a}')
def fff() :
nonlocal a # global a로 변경해 보세요.
a = 100
print(f'fff a: {a}')
fff()
print(f'ff a: {a}')
ff()
f()
print(f'global a: {a}')
# 출력값
f a : 100
ff a : 1000
fff a : 100
ff a : 100
global a : 10
아래 코드는 함수가 3개 중첩되어 있음
fff 함수에서 만약 전역 변수 a를 참조하고 싶다면 global a
자신을 감싸고 있는 바로 위에 함수를 참조하고 싶다면 nonlocal a 사용
= 사용할 때 참조하는 변수가 실제로 바깥 함수에 존재하는지 반드시 확인해야 함 (그렇지 않으면 SyntaxError가 발생)
더 자세한 코드는 제 GitHub 파일을 참고 바랍니다 :)
https://github.com/soohyun020812/ormcamp
GitHub - soohyun020812/ormcamp: 오름캠프 교육에서 활용한 실습 내용들 정리
오름캠프 교육에서 활용한 실습 내용들 정리. Contribute to soohyun020812/ormcamp development by creating an account on GitHub.
github.com