문맥
다음 Python 코드가 있다고 가정하십시오.
def example_function(numbers, n_iters):
sum_all = 0
for number in numbers:
for _ in range(n_iters):
number = halve(number)
sum_all += number
return sum_all
ns = [1, 3, 12]
print(example_function(ns, 3))
example_function여기서는 ns목록 의 각 요소를 간단히 살펴 보고 결과를 누적하면서 세 번 반으로 줄였습니다. 이 스크립트를 실행 한 결과는 다음과 같습니다.
2.0
1 / (2 ^ 3) * (1 + 3 + 12) = 2이므로
이제 (어떤 이유로 든 디버깅 또는 로깅), 중간 단계에 대한 정보를 표시하고 싶습니다 example_function. 아마도이 기능을 다음과 같이 다시 작성했을 것입니다.
def example_function(numbers, n_iters):
sum_all = 0
for number in numbers:
print('Processing number', number)
for i_iter in range(n_iters):
number = number/2
print(number)
sum_all += number
print('sum_all:', sum_all)
return sum_all
이전과 동일한 인수로 호출하면 다음을 출력합니다.
Processing number 1
0.5
0.25
0.125
sum_all: 0.125
Processing number 3
1.5
0.75
0.375
sum_all: 0.5
Processing number 12
6.0
3.0
1.5
sum_all: 2.0
이것은 내가 의도 한 것을 정확하게 달성합니다. 그러나 이것은 함수가 한 가지 작업 만 수행해야한다는 원칙에 위배되며 이제는 코드 example_function가 엄청나게 길고 복잡합니다. 이러한 간단한 함수의 경우 이것은 문제가되지 않지만 내 상황에서 서로 호출하는 매우 복잡한 함수가 있으며 인쇄 명령문에는 종종 여기에 표시된 것보다 더 복잡한 단계가 포함되어 코드의 복잡성이 크게 증가합니다. 내 기능 중 실제 목적과 관련된 줄보다 로깅과 관련된 코드 줄이 더 많았습니다!).
또한 나중에 더 이상 함수에 인쇄 문을 원하지 않는다고 결정하면 이 기능과 관련된 변수와 함께 모든 문을 수동으로 example_function삭제하고 삭제해야합니다 print. 지루하고 오류가있는 프로세스 -경향이 있습니다.
함수 실행 중에 항상 인쇄하거나 인쇄하지 않을 가능성이 있기 때문에 상황이 더욱 악화 print되어 유지 보수가 끔찍한 두 개의 매우 유사한 함수 ( 문이 있거나 하나가없는 함수)를 선언 하거나 다음과 같이 정의하십시오.
def example_function(numbers, n_iters, debug_mode=False):
sum_all = 0
for number in numbers:
if debug_mode:
print('Processing number', number)
for i_iter in range(n_iters):
number = number/2
if debug_mode:
print(number)
sum_all += number
if debug_mode:
print('sum_all:', sum_all)
return sum_all
우리의 간단한 경우에도 부풀어 오르고 (희망스럽게) 불필요하게 복잡한 기능을 수행합니다 example_function.
질문
인쇄 기능을 원래 기능과 "분리"하는 비법적인 방법이 example_function있습니까?
더 일반적으로, 선택적 기능을 함수의 주요 목적에서 분리하는 파이썬적인 방법이 있습니까?
내가 지금까지 시도한 것 :
내가 지금 찾은 해결책은 디커플링에 콜백을 사용하는 것입니다. 예를 들어 다음 example_function과 같이 다시 작성할 수 있습니다 .
def example_function(numbers, n_iters, callback=None):
sum_all = 0
for number in numbers:
for i_iter in range(n_iters):
number = number/2
if callback is not None:
callback(locals())
sum_all += number
return sum_all
그런 다음 원하는 인쇄 기능을 수행하는 콜백 함수를 정의하십시오.
def print_callback(locals):
print(locals['number'])
그리고 이렇게 전화 example_function:
ns = [1, 3, 12]
example_function(ns, 3, callback=print_callback)
그런 다음 출력합니다.
0.5
0.25
0.125
1.5
0.75
0.375
6.0
3.0
1.5
2.0
인쇄 기능을의 기본 기능과 성공적으로 분리합니다 example_function. 그러나이 방법의 주된 문제점은 콜백 함수가 example_function(이 경우 현재 번호를 반으로 줄인 직후) 특정 부분에서만 실행될 수 있으며 모든 인쇄가 정확하게 수행되어야한다는 것입니다. 이로 인해 콜백 함수의 디자인이 상당히 복잡 해져서 일부 동작을 수행 할 수없는 경우가 있습니다.
예를 들어, 질문의 이전 부분에서했던 것과 정확히 동일한 유형의 인쇄를 원한다면 (해당 반쪽과 함께 처리되는 숫자 표시) 결과 콜백은 다음과 같습니다.
def complicated_callback(locals):
i_iter = locals['i_iter']
number = locals['number']
if i_iter == 0:
print('Processing number', number*2)
print(number)
if i_iter == locals['n_iters']-1:
print('sum_all:', locals['sum_all']+number)
이전과 정확히 동일한 결과를 얻습니다.
Processing number 1.0
0.5
0.25
0.125
sum_all: 0.125
Processing number 3.0
1.5
0.75
0.375
sum_all: 0.5
Processing number 12.0
6.0
3.0
1.5
sum_all: 2.0
그러나 쓰고 읽고 디버깅하는 데 어려움이 있습니다.
logging모듈이 어떻게 도움이 되는지 모르겠습니다 . 내 질문 print에 컨텍스트를 설정할 때 명령문을 사용하지만 실제로 함수의 주요 목적에서 모든 유형의 선택적 기능을 분리하는 방법에 대한 솔루션을 찾고 있습니다. 예를 들어, 함수가 실행될 때 플롯하는 함수를 원할 수도 있습니다. 이 경우 logging모듈이 적용되지 않을 것이라고 생각합니다 .
logging있지만 (임의 의 제안에서 알 수 있듯이 ) 임의 코드를 분리하는 방법은 아닙니다.
logging모듈을 확인하십시오