[Python] 버전2에서 버전3으로 옮겨갈 때 주의 점 들 Develop Tip

파이썬이 1990년 말에 만들어 졌다고 알고 있습니다.
(실제 1.0은 1994년에 나왔군요)
거의 25살 청년이 다 되었네요. 적지 않은 나이이지만,
우리나라에서는 비교적 최근에 유명해 졌다고 할 수 있습니다.
아마도 더 기본적인 것은 지금은 구글을 나왔지만 파이썬의 창시자가
구글에 몸담고 있었고 구글 API를 포함한 많은 것들이 모두 파이썬을
기본으로 시작하여 그렇지 않나 싶습니다.

심지어는 최근에 머신러닝으로 구글이 오픈한 텐서플로우 조차도 
파이썬과 C API 로만 구성되어 있는 것을 보더라도 금방 이해할 수 있습니다.

이것은 파이썬 만큼 글루 언어로서 다른 내부 블랙박스를 하이레벨에서 
핸들링하기 좋은 언어는 많지 않다는 것을 의미하는 것입니다.
파이썬 자체의 속도는 차체하고 (시간이 많이 걸릴 일들은 모두
그 하단의 블랙박스에 맡기면 됩니다) 쉽게 API를 핸들링 하고 
테스트 해 볼 수 있는 장점 등은 한번 사용해 보시면 앱니다.. ^^

제가 이런 말씀을 감히 드릴 수 있는 것은 어찌보면 25년 (곧 26년째)
개발을 하고 있는데 그동안 직접 프로덕트나 프로젝트에 이용해 왔던 언어
및 프레임워크 들:
C, C++, Motif, MFC, awk, perl, Java, C#, VB, Delphi, CGI,
PHP, ASP, JSP,...
등 중에서도 그 쓰임새가 남다르기 때문입니다.

암튼 현재 파이썬이 크게 2.7.11, 3.5.1 과 같이
2.x와 3.x가 공존하고 있습니다.

2.x가 존재하는 이유는 2008년 2.6이었을 때,
기존의 하위 호환성을 포기하는 대신 새로운 기능과
성능으로 나가겠다는 것을 표명한 상태입니다.
또한 공식 블로그 등을 보더라도 2.7.x 이후 버전은 나오지 않을 거라 들은 것 같습니다.

암튼 최근 3.5.x 도 나왔고 기존에 버전2의 것들도 필요에 따라 3으로 옮겨갈 필요가 있었습니다.

약 20개 정도의 기존에 2.7.x 용으로 작성했던 파이썬 코드와 대여섯개의
써드파티 라이브러리 등을 3.5.x 로 변경해 보았는데 대략 15개 정도의 것들만
유념하면 대부분의 경우에 이상없이 잘 동작할 수 있을 것 같았습니다.

그 시행착오를 공유해 봅니다.

1) Exception 예외처리에서 as
<<<
except OSError, e:
sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
sys.exit(1)
>>>
except OSError as e:
sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
sys.exit(1)

콤마 대신 as 로 주어야 합니다.


2) file() built-in
<<<
si = file(self.stdin, 'r')
>>>
si = open(self.stdin, 'r')

file build-in 함수를 사용한 경우 open 으로 그대로 변경하면 됩니다.


3) print keyword => print build-in function
<<<
print str(err)
>>>
print(str(err))

print 함수로 변경되었기에 print 다음에 괄호가 꼭 나와야 합니다.


4) long() ==> int()

long type is just int
long type 이 없어지고 int가 long 까지 커버를 한다고 하는군요.


5) basestring => str
<<<
isinstance(obj, basestring)
>>>
isinstance(obj, str)

기존에 basestring, unicode 등이 모두 str 자체 (unicode 의미)로 의미 변경되었습니다.


6) xrange ==> range

2.x 에서는 "for i in xrange(10)" 과 같이 range 와 xrange built-in 함수가 있었습니다.
차이점은 range는 [0,1,2,...,9] list를 반환하고x xrange는 generator로서 그 안에
해당 값으로 yield 시키는 차이가 있었습니다.
3.x 에서는 2.x의 range에 해당하는 함수가 없어지고 xrange 개념의 함수가 range 라고
되어 버렸습니다.
따라서 xrange 라고 되어 있던 부분은 그냥 range 라고 변경만 해 주면 됩니다.


7) byte array is not str

TypeError: a bytes-like object is required, not 'str'

>>> print(b'abcde' == 'abcde')
False
>>> print(b'abcde'.decode() == 'abcde')
True

2.x 에 없던 b'abcde' 라는 바이트 스트림 문자열 상수(리터럴)이 존재합니다.
암복호화를 비록하여 아주 많은 함수들이 기존 문자열 대신 이 바이트 스트림을 
패러미터 또는 결과값으로 이용합니다.
만약 바이트 스트림인 경우 이를 다시 문자열로 변환하기 위하여 decode()를 호출하여야
합니다. 패러미터는 디코딩 문자셋인데 디폴트는 "UTF-8" 입니다.


8) ImportError: No module named 'xmlrpclib'

import xmlrpc.client as xmlrpclib

2.x에 xmlrpclib 모듈을 이용했었다면 이제는
xmlrpc.server 와 xmlrpc.client 로 나누어 이용합니다.
클라이언트였다면 위와 같이 할 수 있습니다.


9) AttributeError: 'dict' object has no attribute 'iteritems'
<<<
d.iteritems()
>>>
d.items()

dict 타입에 대하여 iteritems 메서드를 호출한 경우에는 items를 대신 부르면 됩니다.


10.1) AttributeError: 'dict' object has no attribute 'has_key'
<<<
d.has_key(k)
>>>
k in d

dict 에 대하여 has_key() 로 해당 키가 존재하는가를 2.x에서 많이 조사하였는데,
이 대신 in 오퍼레이터를 사용합니다.


10.2) 'dict.keys()' object is not list
<<<
ks=d.keys()
isinstance(ks, list)
>>>
False

dict 에 대하여 has_key() 로 해당 키가 존재하는가를 2.x에서 많이 조사하였는데,
기존 2에서 dict.keys() 로 해당 키만 가져오면 디폴트로 list 였지만 3에서는 dict_keys 라는 class가 리턴됩니다.
2에서 처럼 사용하고 싶으면...
<<<
ks=list(d.keys())
isinstance(ks, list)
>>>
True



11) import thread : ImportError
<<<
thread.get_ident()
>>>
threading.current_thread().ident

2.x에서 thread.get_ident() 를 호출하여 현재 쓰레드의 int 형 id를 얻었었는데 
3.x에서는 thread 모듈 자체가 없어졌군요.
대신 threading.current_thread().ident 라고 호출하면 됩니다.


12) TypeError: write() argument must be str, not bytes
또는 EOFError: Ran out of input

from pickle import loads, dumps

d={}

s = dumps(d)
with open('load_dump.pkl', 'bw+') as ofp:
  ofp.write(s)

with open('load_dump.pkl', 'br+') as ifp:
  s = ifp.read()
ld = loads(s)

print(d == ld)

위에 처럼 'w' 대신 'bw+', 'r' 대신 'br+'라고 지정
pickle을 위하여 dumps 혹은 loads를 하여 파일에 저장한다고 하였을 때
해당 저장 파일은 바이너리로 열고 닫아야 합니다.


13) AttributeError: 'function' object has no attribute 'func_name'
<<<
if original_function.func_name not in __ARG_SPEC__:
>>>
if original_function.__name__ not in __ARG_SPEC__:

함수 이름을 Inspect 할 경우 
기존의 func_name 대신 __name__ 이라고 참조합니다.


기타 더 많은 것들이 있을 수 있으나 이 정도면 이상없이 잘 동작했습니다.

또한 다음과 같은 서드파티 모듈들도 모두 3.x 를 잘 지원하였습니다.


pip3 install pudb # for terminal debugger
pip3 install psutil # for system resource monitoring
pip3 install pycrypto # for F33.util.ftcipher
pip3 install termcolor # for console color
pip3 install APScheduler # for F33.util.svc.service
pip3 install ujson # fast json encoder/decoder
pip3 install leveldb # for leveldb key-value storage
pip3 install requests # for task-job
pip3 install celery # for task-job
pip3 install redis # for task-job


14) apply built-in 함수

apply(pack, vs)

와 같이 사용한 소스도 있는데 2.3 에서 부터는 거의 이용되지 않는다고 합니다.
3에서는 아예 없어 졌네요.

apply(function, args, kwargs)

와 같이 사용된 것은

function(*args, **kwargs)

로 이용하면 된다고 합니다.



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

덧글

  • hare 2015/12/12 13:24 # 삭제 답글

    정리 감사합니다.
    하고 있던 프로젝트도 2.7 ->3.X로 변경하려다 포기한...
    다음부턴 3.x 을 베이스로 사용하려 하다가 좋은글 보았습니다. 감사합니다.
  • 지훈현서아빠 2015/12/14 19:07 #

    도움이 되셨다니 저의 보람입니다~ ^^
  • 율이파파 2016/10/06 20:09 # 삭제 답글

    안녕하세요.
    저는 software업계는 아니지만 필요에 의해 python을 배우려고 하고 있습니다.
    python을 접하면서 첫번째 고민이 2.7.× 또는 3.5.× 버전 선택이더라구요...
    조언 좀 부탁드리겠습니다~^^
    감사합니다.
  • 지훈현서아빠 2016/10/07 09:02 #

    거의 대부분의 파이썬 2,3 경험자들은 처음 시작하시는 것이면 3으로 시작하라고
    말하고 있습니다. 저도 예외는 아닙니다.
    도움이 되셨기를 바랍니다. ^^
  • 율이파파 2016/10/07 18:16 # 삭제 답글

    조언 감사합니다.^^
    python 3으로 시작해야겠네요~
    감사합니다~
  • 라키아 2017/02/02 23:29 # 삭제 답글

    많은 도움이 되었습니다. 감사합니다.
  • 지훈현서아빠 2017/02/02 23:42 #

    도움이 되셨다니 저의 보람입니다~ ^^
  • 민주 2017/02/08 23:40 # 삭제 답글

    도움많이 되었어요 감사해요 byte array에 대해 몰랐는데 알게되었고 오류해결했습니다~
  • 지훈현서아빠 2017/02/09 01:26 #

    도움이 되셨다니 저의 보람입니다~ ^^
댓글 입력 영역

구글애드텍스트