[파이썬] 모듈 치환 (override) 에 대한 고찰 Develop Tip

module.mymod 파이썬 패키지 안에 org 라는 모듈 (org.py)라는 모듈이 있다고 가정합니다.


org.py의 내용은 

#!/usr/bin/env python
#coding=utf8

##########################################################################################
G_VAL1='abc'
G_VAL2=123

##########################################################################################
class MyClass(object):
#=====================================================================================
def __init__(self):
self.a = 'a'
self.b = 2
#=====================================================================================
def method_a(self,a,b):
self.a = a
self.b = b
#=====================================================================================
def __repr__(self):
return 'mymod.org.MyClass{a="%s",b="%s"}' % (self.a, self.b)

##########################################################################################
if __name__ == '__main__':
mc = MyClass()
print mc
mc.method_a(G_VAL1,G_VAL2)
print mc

와 같습니다.

이제 module 안에 있는 main.py 에서 다음과 같이 mymod.org 를 찹조하여
작업을 하였습니다.

#!/usr/bin/env python
#coding=utf8

##########################################################################################
import module.mymod.org
from module.mymod import org

##########################################################################################
if __name__ == '__main__':
mc = module.mymod.org.MyClass()
print mc
mc.method_a(module.mymod.org.G_VAL1, module.mymod.org.G_VAL2)
print mc

mc = org.MyClass()
print mc
mc.method_a(org.G_VAL1, org.G_VAL2)
print mc

위의 프로그램을 실행하면...

mymod.org.MyClass{a="a",b="2"}
mymod.org.MyClass{a="abc",b="123"}
mymod.org.MyClass{a="a",b="2"}
mymod.org.MyClass{a="abc",b="123"}

와 같이 정상적으로 결과가 잘 나옵니다.
여기 까지는 일반 파이썬 프로그램과 동일 합니다.

그런데 여기서 한가지 가정을 해 봅니다.

만약 위와 같이 패키지를 리눅스에서 기존에 잘 사용하고 있었는데,
동일한 프로그램을 맥 OS X 플랫폼에서 사용한다면,
org 대신 org_r (org_r.py 입니다) 를 대신 돌리고 싶습니다.

기존 org.py 와 동일한 위치에 org_r.py 를 하나 만들고

#!/usr/bin/env python
#coding=utf8

##########################################################################################
G_VAL1='efg'
G_VAL2=456

##########################################################################################
class MyClass(object):
#=====================================================================================
def __init__(self):
self.a = 'z'
self.b = 9
#=====================================================================================
def method_a(self,a,b):
self.a = a
self.b = b
#=====================================================================================
def __repr__(self):
return 'mymod.org_r.MyClass{a="%s",b="%s"}' % (self.a, self.b)

##########################################################################################
if __name__ == '__main__':
mc = MyClass()
print mc
mc.method_a(G_VAL1,G_VAL2)
print mc

위와 같이 동일한 글로벌 이라던가 동일한 이름의 클래스 및 메서드 함수 등을 호출하도록 되어 있습니다.
이제 가장 쉬운 방법으로 맥인 경우 org 대신 org_r 을 호출하도록 main.py 를 수정하면

#!/usr/bin/env python
#coding=utf8

##########################################################################################
from platform import platform
if platform().lower().find('darwin') >= 0:
import module.mymod.org
import module.mymod.org_r
module.mymod.org = module.mymod.org_r
from module.mymod import org_r as org
else:
import module.mymod.org
from module.mymod import org

##########################################################################################
if __name__ == '__main__':
mc = module.mymod.org.MyClass()
print mc
mc.method_a(module.mymod.org.G_VAL1, module.mymod.org.G_VAL2)
print mc

mc = org.MyClass()
print mc
mc.method_a(org.G_VAL1, org.G_VAL2)
print mc

위와 같이 darwin 인 경우
import 한 모듈의 이름을 갈아 치우거나

import module.mymod.org
import module.mymod.org_r
module.mymod.org = module.mymod.org_r
아니면 as 로 aliasing 을 시키는 방법

from module.mymod import org_r as org
이 일반적입니다.

해당 결과는

mymod.org_r.MyClass{a="z",b="9"}
mymod.org_r.MyClass{a="efg",b="456"}
mymod.org_r.MyClass{a="z",b="9"}
mymod.org_r.MyClass{a="efg",b="456"}

와 같이 org_r 의 결과가 나옵니다.


그런데 위와 같은 경우 main 에서와 같이 다수의 많은 곳에서 org 를 참조하고 있었다면
모든 소스를 돌아가면서 수정해야 되는 난감한 상황이 발생합니다.

이런 경우에는 __init__.py 를 이용하면 됩니다.

main.py 를 원래대로 원복을 시킨 상태에서...


평소에는 거의 비워 놓은 org가 있는 mymod 안의 __init__.py 를


from . import org, org_r
from platform import platform
if platform().lower().find('darwin') >= 0:
org = org_r
 

위와 같은 내용으로 채워 놓으면
원하는 대로 org 를 호출한 수 많은 소스의 수정은 하나도 필요 없이

org 대신 org_r 이 실행하도록 하게 만들 수 있었습니다.

한가지 더 테스트를 하다가 살펴본 것인데...
main 에서

from module.mymod.org import MyClass, G_VAL1, G_VAL2
와 같이 직접 해당 모듈 안의 글로벌이나 클래스, 함수 등을 import 할 때에는
위의 결과가 적용되지 않았습니다.

이런 경우에는 __init__.py 에서

# org = org_r
for n in dir(org):
if n.startswith('__') and n.endswith('__'):
continue
if n not in dir(org_r):
continue
setattr(org, n, getattr(org_r, n))
org = org_r 이라고 모듈 자체를 치환 하는 것 보다는
해당 모듈의 동일한 이름을 갖는 글로벌, 클래스, 함수 등으로 Override 시키는
것이 모든 경우에 적용될 수 있는 것으로 보입니다.

전체 시스템을 리팩토링 등을 하려면 꼭 필요한 기능일 수 있는데
더 좋은 방법을 알고 계신 분이 있으시면 답글 남겨주시기 바랍니다.


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

덧글

  • 권남 2016/05/26 17:17 # 삭제 답글

    항상 지훈현서 블로그글 고맙게 읽고 있습니다.
    헌데 코드 포맷터가 가독성이 너무 떨어지고 눈이 아픈데요..
    코드 포맷터를 좀 더 가독성 높은 것으로 해주시면 읽는데 큰 도움이 될 것 같습니다.
  • 지훈현서아빠 2016/05/26 17:44 #

    항상 읽고 계시다니 정말 고맙습니다.
    가독성은 저 자신도 이 이글루를 이용하면서 항상 불편한 상황 입니다.
    봐서 다른 블로그로 갈아타야 할지 고민입니다만,
    당장은 힘이 드는군요.
    암튼 감사드립니다.
댓글 입력 영역

구글애드텍스트