어떤 프로젝트에서 여러 함수를 호출하는 루틴을 필요로 한다. 그런데 이 함수의 이름 및 인자 목록은 상황에 따라 변할 수 있고, 함수의 호출 타이밍도 조금씩 변화시킬 필요가 있다고 한다.
그래서 프로젝트원은 함수 호출 및 함수에 필요한 인자를 미리 '스크립트화'하여 프로그램 안에서 보다 유연하게 함수 및 인자 호출을 할 수 있도록 프로그램을 디자인하고 싶어 한다. 이럴 때는 과연 어떻게 해야 할까?
파이썬에는 모든 함수 및 변수 (이것들은 파이썬에서 모두 '객체(object)'로 취급된다) 목록들이 내부적인 '테이블'에 기록되어 있다. 이 '테이블'이란 오브젝트의 인스턴스 이름을 키로 하고 오브젝트 자체가 내용인 딕셔너리 형태로 구성되어 있다. 그러므로 프로그램 내부에서 쓰이는 오브젝트의 이름만 정확히 지칭할 수 있으면 얼마든지 그 이름을 이용해 실제 객체에 접근할 수 있다.
빌트인 함수로써 현재 스코프 내에서의 오브젝트들 테이블을 호출한다.
빌트인 함수로써 현재 글로벌 스코프 내에서의 오브젝트 테이블을 호출한다.
getattr, hasattr 역시 빌트인 함수이다. 시그니쳐는 다음과 같다.
getattr(object, name[, default]) hasattr(object, name)
getattr은 해당 오브젝트 object 안에 어트리뷰트 이름인 name이 존재한다면 그 객체를 리턴한다. 만일 발견되지 않는다면 default를 리턴한다. hasattr은 오브젝트 내부에 어트리뷰트가 존재하는지 파악하여 불리언 값을 리턴한다.
간단한 코드를 통해 예를 들어 보자
#coding: utf-8 def criteria_1(param_1): print 'criteria 1' print 'param_1:', param_1 print '' def criteria_2(param_1, param_2): print 'criteria 2' print 'param_1:', param_1 print 'param_2:', param_2 print '' def criteria_3(param_1, param_2, param_3): print 'criteria 3' print 'param_1:', param_1 print 'param_2:', param_2 print 'param_3:', param_3 print '' def call_wrapper(func_name, var_list): varcls = variables() func = globals()[func_name] params = {} for var in var_list: params[var] = getattr(varcls, var) func(**params) class variables: def __init__(self): self.param_1 = 'variable1' self.param_2 = 12.25 self.param_3 = (1, 2, 3) # test # function names and variable names function_names = ['criteria_1', 'criteria_2', 'criteria_3'] var_names = ['param_1', 'param_2', 'param_3'] # run criteria_1 only by names call_wrapper('criteria_1', ['param_1']) call_wrapper('criteria_2', ['param_1', 'param_2']) call_wrapper('criteria_3', ['param_1', 'param_2', 'param_3'])
호출해야 할 함수의 종류는 3가지이며, 이 함수의 이름과 인자는 각각 조금씩 다르다. 스크립트가 실행되는 '# test' 주석 이후의 코드에서 함수를 호출하기 위한 이름과 인자를 미리 문자열로 지정해 두었다. 이 이름은 호출되는 함수의 인자명과 동일해야 한다.
각 인자 이름과 매칭되는 실제 값들은 'variables'라는 클래스에서 지정을 한다. 여기서 약간의 제약이 존재한다. 스크립트를 통해 일괄적으로 클래스 내부의 어트리뷰트의 실제 객체로 접근하려면 클래스 내부의 변수 이름 또한 함수의 인자명과 동일하게 해 주는 것이 용이하다. 물론 다르게 해 줄 수도 있지만, 그렇게 하려면 추가적인 연산이 필요하므로 가능한한 맞추어 주는 것이 좋다.
call_wrapper 함수는 스크립느를 통해 실행될 함수의 이름과 각 함수의 인자를 '문자열'의 형태로 전달한다. 그러면 call_wrapper 함수는 실제 함수 객체와 함수에 필요한 인자의 목록을 딕셔너리 형태로 저장한다.
그리고 func(**params)
라는 구문에서 목표로 하는 함수에 정확히 원하는 인자를 제공하게 된다.
딕셔너리 구조체를 위처럼 입력하는 구문은 매우 톡특한 파이썬 문법이다. (다른 언어도 이러한 형식을 지원하는지?)
그러므로 초기 function_names, var_name에 대한 룰을 구체적으로 정의하면 미리 함수 호출 및 인자값을 스크립트화하여 원하는 타이밍에 맞춰 함수호출을 자유자재로 유연히 호출할 수 있을 것이다. call_wrapper 내부에서 필요한 객체에 접근하기 위해 부가적으로 locals, getattr 등의 함수 호출이 발생하나, 딕셔너리 객체에 의한 검색이므로 프로그램의 유연성에 비해 지불하는 오버헤드는 매우 미미하다고 볼 수 있다.