[Python] 실행시 클래스 함수 패러미터 구해서 다른 클래스에 추가해주기 Develop Tip

말이 조금 어렵습니다만,
어떤 클래스의 인스턴스가 돌고 있다고 가정합니다.

그런데 그 함수가 자꾸 바뀌고 있어 (혹은 불특정 클래스라)
런타임시 해당 클래스의 메소드 및 패러미터 정보를 구할 필요가 있습니다.

또한 이렇게 구한 정보를 가지고,
다시 다른 클래스를 런타임에서 정의하는데
이전에 읽은 정보대로 메소드를 구성할 필요가 있었습니다.

일반적인 경우는 필요없으나,
XML-RPC 의 proxy 호출 등을 할 때 이런 요구사항이 필요하기도 합니다.

1) 런타임 시 클래스, 메소드, 패러미터 정보 구하기

import inspect

class A:
    def __init__(self, a=1, b='A'):
        """
        @brief A 생성자 함수
        @param a [int] 디폴트 값은 1
        @param b [str] 디폴트 값은 'A'
        @return [None]
        """
        self.a = a
        self.b = b
        print 'A.__init__: a=<%s>,b=<%s>' % (a, b)
    def f_a(self, p1, p2=2):
        self.a = p1
        self.b = p2
        print 'A.f_a: p1=<%s>,p2=<%s>' % (p1, p2)
        return self.a
    def f_b(self, p1, p2=2, p3='default_value'):
        """
        @brief f_b 메소드 인스턴스 함수 2
        @param p1 [int] 예제 패러미터 1
        @param p2 [int] 예제 패러미터 2 (디폴트 2)
        @param p3 [str] 예제 패러미터 3 (디폴트 'default_value')
        @return [int] self.b
        """
        self.a = p1
        self.b = p2
        print 'A.f_b: p1=<%s>,p2=<%s>,p3=<%s>' % (p1, p2, p3)
        return self.b

위와 같이 예제가 있다고 할 때,

이를 출력하기 위하여

umlist = inspect.getmembers(A, predicate=inspect.ismethod)
#print umlist
for um in umlist:
    if um[0].startswith('_'): continue
    print um[0]
    (args, varargs, keywords, defaults) = inspect.getargspec(um[1])
    #argspec = inspect.getargspec(um[1])
    print (args, varargs, keywords, defaults)
    print "<%s>" % um[1].__doc__

와 같이 만들어 호출해 보면,

f_a
(['self', 'p1', 'p2'], None, None, (2,))
<None>
f_b
(['self', 'p1', 'p2', 'p3'], None, None, (2, 'default_value'))
<
@brief f_b 메소드 인스턴스 함수 2
@param p1 [int] 예제 패러미터 1
@param p2 [int] 예제 패러미터 2 (디폴트 2)
@param p3 [str] 예제 패러미터 3 (디폴트 'default_value')
@return [int] self.b
>

와 같은 결과가 나옵니다.
메서드 이름 및 패러미터 더 나아가서는 __doc__ 문자열까지 구했습니다.
패러미터 목록에서 주목할 것은 default 값도 나타내 주는데 args의 뒤에해당되는 default 값 입니다.


2) 런타임에서 클래스 메소드 추가

우선 다음과 같은 클래스가 있습니다.

import types

class EmptyMethod(object):
    @classmethod
    def removeVariable(cls, name):
        return delattr(cls, name)
    @classmethod
    def addMethod(cls, func):
        return setattr(cls, func.__name__, types.MethodType(func, cls))

그리고 아래와 같이 self를 포함한 함수를 동적으로 정의합니다.

fexec="""
def hello(self, n):
    print n
"""
exec(fexec, globals(), locals())

이제 hello라는 함수를 EmptyMethod 클래스에 추가하려면,

EmptyMethod.addMethod(hello)

라고 지정하면 됩니다.

이제부터는 모든 EmptyMethod 인스터스에 hello 메서드를 호출할 수 있습니다.

instance.hello('안녕?')
instance2.hello('안녕?')


파이썬이 다른 언어와 달리 dynamic binding이 강력한 최대 장점이 위와 같이
런타임시에 클래스 자체의 메소드나 속성을 바꿀 수 있다는 것이 될 수 있습니다.

이런 dynamic binding 언어의 융통성은 많은 장점을 내포합니다.


어느분께는 도움이 되셨기를...

덧글

댓글 입력 영역

구글애드텍스트