메서드 이름으로 파이선 메서드 호출하기

자바, C++ 등 여러 언어에서는 실행 중 객체가 어떤 값을 가지고 있고 어떤 메서드를 호출 할 수 있는지 코딩을 통해 알 수 있도록 리플랙션(Reflection) 기능을 지원합니다.

그렇다면 파이선은 어떤 방식으로 리플랙션을 지원할까요? 이번 글에서는 dir, getattr, callable 그리고 methodToCall을 사용해서 동적으로 객체의 메서드를 얻어내고, 원하는 메서드를 실행하는 방법을 알아보겠습니다.

1. 이슈

파이선으로 하이브 메타스토어를 이용한 코딩을 하던 중 문서와 예제가 부족하여 뭘 할 수 있고, 뭘 할 수 없는지 확인하고 싶었습니다. 하이브 소스코드나 메타스토어를 접근하는 소스코드를 보는 것이 정석이겠지만 우선 사용 가능한 메서드들을 모두 확인하는 것으로 시작하는 것이 시간을 절약할 수 있다는 생각에 객체로부터 메소드 목록을 얻는 방법을 찾았습니다.

이번 글의 제목은 메서드 이름으로 파이선 메서드 호출하기 이지만 하이브 메타스토어를 사용하기 위한 내용으로 시작합니다.

2. 하이브 메타스토어 클라이언트 얻기

스리프트 프로토콜을 이용한 하이브 클라이언트 인스턴스를 생성하는 코드입니다.

3. 예외처리!

위의 코드는 문제가 없지만 위에서 얻은 클라이언트를 바로 실행하면 아래와 같은 에러가 발생합니다.

client = get_client(_host_, _port_)
client.get_all_databases()

TTransportException: Transport not open

정상 동작하기 위해서 Transport open/close 처리를 해줍니다.

하이브가 관리하고있는 데이터베이스 목록이 출력됩니다.

4. 메서드 이름으로 메서드 호출하기

드디어 본론입니다. 매번 이렇게 앞뒤로 코드를 붙이기 귀찮으니 원하는 코딩 양을 줄이기 위해서 아래와 같이 감싸주는 코드를 작성했습니다. 클래스로 감싸서 생성자/소멸자에 open/close 코드를 넣는 방법도 있겠으나 1) 호출 깊이가 깊어지고 2) 코드양을 줄이고 싶은 마음과 3) 단순히 파이선의 리플랙션 방법을 본 갑자기 기억나서 아래와 같이 getattr을 찾아서 적용했습니다.

getattr은 객체로부터 메서드 이름을 통해 메서드의 위치를 얻어옵니다. 메서드가 어떤 매개변수가 필요할지 callit 함수 정의 시점에는 알지 못하기 때문에 *args로 가변길이 매개변수를 받을 수 있게 해줍니다. 이제 실행하는 코드입니다.

우선 객체가 가지고 있는 전체 메서드 목록을 얻어보고

[method for method in dir(client) if callable(getattr(client, method))]

['__init__',
 'add_index',
 'add_partition',
 ...
 'get_all_databases',
 'get_all_tables',
 'get_config_value',
 'get_database',
 'get_databases',
 'get_delegation_token',
 'get_fields',
 'get_index_by_name',
 'get_index_names',
 'get_indexes',
 'get_partition',
 'get_partition_by_name',
 'get_partition_names',
 ...
 'set_ugi',
 'shutdown']

get_all_databases 메서드를 호출해서 전체 데이터베이스 목록을 얻어옵니다.

callit('get_all_databases')

파이선 문맥 관리자 사용하기

위의 메서드 호출 방법이 동작하지만 파이선다운(Pythonic) 코드는 아닙니다. 다른 글에서 문맥 관리자(context manager) 기법에 대해서 더 알아보도록 하겠습니다.

Appendix. 참고

파이선을 이용한 하이브 메타스토어 기본 사용법은 에어플로우 (airflow)의 코드를 통해 배웠습니다.