성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>파이썬 2.x에서의 동적 함수 추가</h1> <p> 이번 글의 모든 예제 코드는 Python 2.7.17에서 테스트된 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 파이썬에서 동적으로 함수를 추가하는 것은 자바스크립트만큼이나 간단합니다. 아래는, 새로운 함수를 인스턴스/정적/클래스 레벨로 MyObject에 추가하는 것을 보여줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > def f_instance(self, arg): print 'f_instance', self, arg def f_static(arg): print 'f_static', arg def f_class(cls, arg): print 'f_class', cls, arg class MyObject: def __init__(self): pass <span style='color: blue; font-weight: bold'>MyObject.fi = f_instance MyObject.fs = staticmethod(f_static) MyObject.fc = classmethod(f_class)</span> myobj = MyObject() myobj.fi('test') # 인스턴스 함수 호출 myobj.fs('test') # 정적 함수 호출 myobj.fc('test') # 클래스 함수 호출 print type(MyObject.fi) print type(MyObject.fs) print type(MyObject.fc) /* 출력 결과 f_instance <__main__.MyObject instance at 0x7f07092a1af0> test f_static test f_class __main__.MyObject test <type 'instancemethod'> <type 'function'> <type 'instancemethod'> */ </pre> <br /> 위의 코드 중 인스턴스 함수의 경우에는 types 모듈에 구현된 MethodType (또는 동일한 역할을 하는 UnboundMethodType)을 경유해,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > # types.py # ...[생략]... class _C: def _m(self): pass # ...[생략]... UnboundMethodType = type(_C._m) # Same as MethodType _x = _C() <span style='color: blue; font-weight: bold'>MethodType = type(_x._m)</span> </pre> <br /> 구현하는 것도 가능합니다. MethodType은 Python 2.x의 경우 인자 3개를 받는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > MethodType(function, instance, class) </pre> <br /> 인자만으로 보면 인스턴스와 클래스 함수를 재정의할 수 있을 것 같지만 실제로는 무조건 인스턴스 함수만을 정의할 수 있으며 단지 그 대상이 클래스 전체에 정의할 것이냐, 인스턴스에 한해 정의할 것이냐를 정하게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > from types import MethodType def f_instance(self, arg): print 'f_instance', self, arg def f_instance2(self, arg): print 'f_instance2', self, arg class MyObject: def __init__(self): pass <span style='color: blue; font-weight: bold'>MyObject.fi = MethodType(f_instance, None, MyObject)</span> # 클래스 범위로 메서드를 추가 myobj = MyObject() <span style='color: blue; font-weight: bold'>myobj.fi2 = MethodType(f_instance2, myobj, None)</span> # 인스턴스 범위로 메서드를 추가 myobj.fi('test') myobj.fi2('test2') myobj2 = MyObject() myobj2.fi2('test3') # (당연히) 예외 발생: AttributeError: MyObject instance has no attribute 'fi2' </pre> <br /> 주의해야 할 점이 있다면 MethodType의 인자 위치가 중요하지 않은 듯도 한데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > myobj = MyObject() <span style='color: blue; font-weight: bold'>MyObject.fi</span> = MethodType(f_instance, <span style='color: blue; font-weight: bold'>None, MyObject</span>) <span style='color: blue; font-weight: bold'>MyObject.fi2</span> = MethodType(f_instance, <span style='color: blue; font-weight: bold'>myobj, None</span>) myobj.fi('test') # 정상 호출 myobj.fi2('test2') # 정상 호출 myobj2 = MyObject() myobj2.fi('test3') # 정상 호출 myobj2.fi2('test4') # 정상 호출 </pre> <br /> 하지만 출력 결과를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > f_instance <__main__.MyObject instance at 0x7f81ed4b7a00> test f_instance <__main__.MyObject instance at 0x7f81ed4b7a00> test2 f_instance <__main__.MyObject instance at <span style='color: blue; font-weight: bold'>0x7f81ed4b7a50</span>> test3 f_instance <__main__.MyObject instance at <span style='color: blue; font-weight: bold'>0x7f81ed4b7a00</span>> test4 </pre> <br /> MyObject.fi2 함수의 경우 myobj2 인스턴스로 호출이 되었지만 MethodType 정의 시 넘겨 준 myobj 인자에 영향을 받고 있습니다. 혹은 반대로 MyObject가 아닌 myobj에 할당해보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > myobj = MyObject() <span style='color: blue; font-weight: bold'>myobj.fi</span> = MethodType(f_instance, <span style='color: blue; font-weight: bold'>None, MyObject</span>) <span style='color: blue; font-weight: bold'>myobj.fi2</span> = MethodType(f_instance, <span style='color: blue; font-weight: bold'>myobj, None</span>) myobj.fi('test') # 예외 발생 TypeError: unbound method f_instance() must be called with MyObject instance as first argument (got str instance instead) myobj.fi2('test2') # 정상 동작 </pre> <br /> f1 호출 시 첫 번째 인자가 (self에 해당하는) 인스턴스가 아니라는 오류를 내고 있는데요, 그래서 다음과 같이 우회해서 호출할 수는 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > myobj.fi(myobj, 'test') # 정상 동작 </pre> <br /> 아마도 이런 혼란 때문인지 파이썬 3.x의 경우에는 MethodType이 "(function, instance_or_class)" 2개의 인자만 받도록 바뀌었습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 위에서는, 전역 함수를 가져다 클래스의 함수로 추가했는데요, 그렇다면 다른 클래스로부터 가져오는 것도 가능할까요? 실제로 구현해 보면 정적 함수를 제외하고는 다소 직관적이지 않은 형태로 동작하게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class D: def __init__(self): pass def f_instance(self, arg): print 'D.f_instance', self, arg @staticmethod def f_static(arg): print 'D.f_static', arg @classmethod def f_class(<span style='color: blue; font-weight: bold'>cls_d, cls_my</span>, arg): # 2개의 class 인자 print 'D.f_class', cls_d, cls_my, arg class MyObject: def __init__(self): pass <span style='color: blue; font-weight: bold'>MyObject.fi = D.f_instance MyObject.fs = staticmethod(D.f_static) MyObject.fc = classmethod(D.f_class) </span> myobj = MyObject() dobj = D() <span style='color: blue; font-weight: bold'>myobj.fi(dobj, 'test1') # D 타입의 인스턴스를 함께 전달해야 함 myobj.fs('test2') myobj.fc('test3')</span> # f_class 함수는 2개의 클래스를 받는 인자를 함께 정의해야 함 /* 실행 결과 D.f_instance <__main__.D instance at 0x7f0e95377b90> test1 D.f_static test2 D.f_class __main__.D __main__.MyObject test3 */ </pre> <br /> 보다시피, 실행 결과를 보면 MyObject에 추가한 fi 인스턴스 함수의 경우 D 타입의 인스턴스를 함께 전달해야 하는 문제가 있고, fc 클래스 함수의 경우에는 2개의 추가 클래스 변수를 받도록 f_class를 정의해야 하는 이상한 변칙이 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이전 예제에서는 인스턴스/클래스 함수의 동작이 다소 이상했는데요, 이 중에서 인스턴스 함수의 경우에는 약간의 우회적인 방법을 통해 다음과 같이 개선할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > from functools import partial from types import MethodType class D: def __init__(self): pass def fi(self, arg): print 'D.fi', self, arg class MyObject: def __init__(self): self.proxy = D() <span style='color: blue; font-weight: bold'>def call_proxy(name, self, *args): return getattr(self.proxy, name)(*args)</span> <span style='color: blue; font-weight: bold'>method_name = 'fi' p = partial(call_proxy, method_name) p.__name__ = method_name p.__doc__ = getattr(D, method_name).__doc__ m = MethodType(p, None, MyObject) setattr(MyObject, method_name, m)</span> myobj = MyObject() <span style='color: blue; font-weight: bold'>myobj.fi('test1')</span> /* 출력 결과 D.fi <__main__.D instance at 0x7f4503a90a50> test1 */ </pre> <br /> myobj.fi 호출 시 별도의 D 인스턴스를 제거했는데요, 주의할 것은 그렇다고 해서 D.fi 함수의 self 타입이 MyObject는 아니라는 점입니다.<br /> <a name='socket'></a> <br /> 실제로 저런 트릭을 사용하고 있는 것이 Python 2.x에서 제공하는 socket 모듈입니다. 원래 socket 모듈에서 제공하는 socket은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > import socket print socket.socket # 출력 결과: <class '<span style='color: blue; font-weight: bold'>socket._socketobject</span>'> </pre> <br /> 같은 모듈에 정의된 _socketobject인데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class _socketobject(object): __doc__ = _realsocket.__doc__ __slots__ = ["_sock", "__weakref__"] + list(_delegate_methods) def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None): if _sock is None: _sock = _realsocket(family, type, proto) self._sock = _sock ...[생략]... def close(self, _closedsocket=_closedsocket, ...[생략]... def accept(self): ...[생략]... def dup(self): ...[생략]... def makefile(self, mode='r', bufsize=-1): ...[생략]... ...[생략]... </pre> <br /> 보는 바와 같이 connect 함수 등은 _socketobject 타입에 정의돼 있지 않습니다. 그럼에도 이런 함수들이 _socketobject 타입에서 제공되는 이유는 다음과 같이 동적으로 (socket 모듈의 socket이 아닌, 내장 _socket 모듈의) socket으로부터 가져온 함수들을 연결하기 때문입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > _socketmethods = ( 'bind', 'connect', 'connect_ex', 'fileno', 'listen', 'getpeername', 'getsockname', 'getsockopt', 'setsockopt', 'sendall', 'setblocking', 'settimeout', 'gettimeout', 'shutdown') def meth(name,self,*args): return getattr(self._sock,name)(*args) for _m in _socketmethods: p = partial(meth,_m) p.__name__ = _m p.__doc__ = getattr(_realsocket,_m).__doc__ m = MethodType(p,None,_socketobject) setattr(_socketobject,_m,m) </pre> <br /> <hr style='width: 50%' /><br /> <a name='qualname'></a> <br /> 이와 관련해 재미있는 점이 있다면, qualname을 구하는 데 있어 같은 socket에서 제공하는 함수라도 예기치 않은 동작이 발생한다는 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > from qualname import <a target='tab' href='https://pypi.org/project/qualname/'>qualname</a> # 파이썬 3.x의 x.__qualname__을 파이썬 2.x에서 대체 사용 import socket print qualname(socket.socket.accept) # 출력: _socketobject.accept print qualname(socket.socket.connect) # 예외 발생 </pre> <br /> connect에 대한 예외 메시지는 다음과 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Traceback (most recent call last): File "test.py", line 10, in <module> print qualname(socket.socket.connect) File "/usr/local/lib/python2.7/dist-packages/qualname.py", line 50, in qualname return obj.__qualname__ # raises a sensible error AttributeError: 'functools.partial' object has no attribute '__qualname__' </pre> <br /> 이런 socket 처리와 유사하게 만든, 간략한 재현 코드는 다음과 같이 작성할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > from qualname import qualname from types import MethodType class D: def __init__(self): pass def fi(self, arg): print 'D.fi', self, arg class MyObject: def __init__(self): pass dobj = D() m = MethodType(D.fi, dobj, MyObject) setattr(MyObject, 'fi', m) myobj = MyObject() dobj = D() myobj.fi('test1') # 출력: D.fi <__main__.D instance at 0x7fe3e1b7f960> test1 print qualname(myobj.fi) # 예외 발생 AttributeError: 'function' object has no attribute '__qualname__' </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2972
(왼쪽의 숫자를 입력해야 합니다.)