#!/usr/bin/env python # coding: utf-8 # # 21. 변수 영역과 클로저의 상호작용 방식을 이해하라 # 숫자로 이뤄진 list를 정렬하되, 정렬한 리스트의 앞쪽에는 우선수위를 부여한 몇몇 숫자를 위치시켜야 한다고 가정 # In[1]: def sort_priority(values, group): def helper(x): if x in group: return (0, x) return (1, x) values.sort(key=helper) # In[2]: numbers = [8, 3, 1, 2, 5, 4, 7, 6] group = [2, 3, 5, 7] sort_priority(numbers, group) # In[3]: print(numbers) # - 파이썬이 클로저(closure)를 지원 : 클로저란 자신이 정의된 영역 밖의 변수를 참조하는 함수. # - 함수가 일급 객체 : 일급 객체라는 말은 이를 직접 가리킬 수 있고, 변수에 대입하거나 다른 함수에 인자로 전달할 수 있으며, 식이나 if 문에서 함수를 비교하거나 함수에서 반환하는 것 등이 가능하다는 것을 의미한다. # - 시퀀스를 비교하는 구체적인 규칙이 있음: 0번 인덱스에 있는 값을 비교한다음 이 값이 같으면 다시 1번 인덱스를 값을 비교한다. # 클로저를 사용해 우선순위가 높은 원소를 발견했음을 표시하는 플래그를 설정하면 어떨까? # In[5]: def sort_priority2(values, group): found = False def helper(x): if x in group: found = True return (0, x) return (1, x) values.sort(key=helper) return found # In[6]: found = sort_priority2(numbers, group) print('발견:', found) print(numbers) # 왜 False가 나올까? # # 식 안에서 변수를 참조할 때 파이썬 인터프리터는 이 참조를 해결하기 위해 다음 순서로 영역을 뒤진다. # # 1. 현재 함수의 영역 # 2. 현재 함수를 둘러싼 영역(현재 함수를 둘러싸고 있는 함수 등) # 3. 현재 코드가 들어 있는 모듈의 영역(전역 영역(global scope) 이라고도 부름) # 4. 내장 영역 # 파이썬에는 클로저 밖으로 데이터를 끌어내는 특별한 구문인 nonlocal 문이 있다 # In[7]: def sort_priority2(values, group): found = False def helper(x): nonlocal found if x in group: found = True return (0, x) return (1, x) values.sort(key=helper) return found # In[8]: found = sort_priority2(numbers, group) print('발견:', found) print(numbers) # global 문을 보완해 줄 수 있음 # nonlocal을 사용하는 방식이 복잡해지면 도우미 함수로 상태를 감싸는 편이 더 낫다 # In[9]: class Sorter: def __init__(self, group): self.group = group self.found = False def __call__(self, x): if x in self.group: self.found = True return (0, x) return (1, x) # In[10]: sorter = Sorter(group) numbers.sort(key=sorter) # In[12]: sorter.found # ## 기억해야 할 내용 # - 클로저 함수는 자신이 정의된 영역 외부에서 정의된 변수도 참조할 수 있다. # - 기본적으로 클로저 내부에 사용한 대입문은 클로저를 감싸는 영역에 영향을 끼칠 수 없다. # - 클로저가 자신을 감싸는 영역의 변수를 변경한다는 사실을 표시할 때는 nonlocal 문을 사용하라. # - 간단한 함수가 아닌 경우에는 nonlocal 문을 사용하지 마라.