Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Python 2.x와 Python 3.x의 차이점 완벽 정리

코딩 커뮤니티에서는 오랫동안 "파이썬을 배운다면 어느 버전이 좋을까?"라는 질문을 두고 뜨거운 논쟁이 이어져 왔습니다. 물론 2020년 1월 공식 지원이 종료된 Python 2는 이제 선택지가 아니지만, 여전히 많은 기업에서 레거시 코드로 Python 2를 운영하고 있기 때문에 두 버전의 차이를 아는 것은 실무에서 매우 중요합니다.

아래에서 Python 2.x와 Python 3.x의 핵심적인 차이점을 하나씩 살펴보겠습니다.

1. print 함수

가장 눈에 띄는 차이점은 print 문입니다. Python 2.x에서는 'print'가 단순한 문(statement)으로 취급되지만, Python 3.x에서는 'print'가 명시적으로 함수(function)로 처리됩니다. 따라서 Python 3에서는 출력할 내용을 반드시 괄호 안에 넣어야 하며, 그렇지 않으면 문법 오류(Syntax Error)가 발생합니다.

#Python 2.7

print 'Python', python_version()
print 'Hello, World!'
print('Hello, World!')
print "text", ; print 'some more text here'

실행 결과

Python 2.7.6
Hello, World!
Hello, World!
text print some more text here

반면 Python 3에서는 다음과 같이 작성해야 합니다.

#Python 3
import sys
print("Python version is %s.%s.%s" %sys.version_info[:3])
print('Hello, World!')

print("some text,", end="")
print('some more text here')

실행 결과

Python version is 3.6.1
Hello, World!
some text,some more text here
>>> print "Hello"
Syntax Error: Missing parentheses in call to 'print'

2. 정수 나눗셈(Integer Division)

Python 2는 소수점 없이 입력된 숫자를 모두 정수(integer)로 취급하기 때문에, 나눗셈 연산에서 예상치 못한 결과가 나올 수 있습니다. 예를 들어 Python 2 코드에서 3 / 2를 실행하면 우리가 기대하는 1.5가 아닌 1이 반환됩니다.

Python 2 스크립트에서 올바른 나눗셈 결과를 얻으려면 from __future__ import division을 추가하거나, 피연산자 중 하나를 float 타입으로 변환하면 됩니다.

#Python 2
print 'Python', python_version()
print '3 / 2 =', 3 / 2
print '3 // 2 =', 3 // 2
print '3 / 2.0 =', 3 / 2.0
print '3 // 2.0 =', 3 // 2.0

실행 결과

Python 2.7.6
3 / 2 = 1
3 // 2 = 1
3 / 2.0 = 1.5
3 // 2.0 = 1.0

Python 3에서는 / 연산자가 항상 참 나눗셈(true division)을 수행하고, 몫만 필요할 때는 //(floor division)를 사용합니다.

#Python 3.6.1
import sys
print('Python %s.%s.%s' %sys.version_info[:3])
print('3 / 2 =', 3 / 2)
print('3 // 2 =', 3 // 2)
print('3 / 2.0 =', 3 / 2.0)
print('3 // 2.0 =', 3 // 2.0)

실행 결과

Python 3.6.1
3 / 2 = 1.5
3 // 2 = 1
3 / 2.0 = 1.5
3 // 2.0 = 1.0

3. 유니코드(Unicode) 문자열

Python 3는 기본적으로 문자열을 유니코드(Unicode)로 저장합니다. 반면 Python 2에서는 문자열을 유니코드로 저장하려면 접두사 'u'를 붙여 명시해야 했습니다.

Python 2의 기본값인 ASCII 문자열보다 유니코드 문자열이 훨씬 범용적입니다. 유니코드는 로마자와 숫자뿐 아니라 한글, 일본어 등 외국어 문자와 이모지까지 저장할 수 있기 때문입니다.

#Python 2
>>> print type(unicode('this is like a python3 str type'))
<type 'unicode'>
>>> print type(b'byte type does not exist')
<type 'str'>
>>> print 'they are really' + b' the same'
they are really the same
#Python 3
import sys
print('Python %s.%s.%s' %sys.version_info[:3])
print('strings are now utf-8 μnicoΔé!')
print('Python %s.%s.%s' %sys.version_info[:3], end="")
print(' has', type(b' bytes for storing data'))
print('Python %s.%s.%s' %sys.version_info[:3], end="")
print(' also has', type(bytearray(b'bytearrays')))

실행 결과

Python 3.6.1
strings are now utf-8 μnicoΔé!
Python 3.6.1 has <class 'bytes'>
Python 3.6.1 also has <class 'bytearray'>

주의할 점은, Python 3에서 str과 bytes는 서로 다른 타입이라는 것입니다. 따라서 문자열과 바이트를 직접 연결하면 TypeError가 발생합니다.

>>> print ('they are really' + b' the same')
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
print ('they are really' + b' the same')
TypeError: must be str, not bytes

4. 예외 발생(Raising Exception)

Python 3에서는 예외를 발생시키는 문법도 달라졌습니다. 사용자에게 에러 메시지를 출력하려면 다음과 같은 형식을 사용해야 합니다.

raise IOError("your error message")

위 문법은 Python 2와 Python 3 양쪽에서 모두 동작하므로, 호환성을 고려한다면 이 방식을 사용하는 것이 좋습니다.

그러나 아래 코드는 Python 2에서만 동작하고 Python 3에서는 작동하지 않습니다.

raise IOError, "your error message"

5. 리스트 컴프리헨션의 루프 변수(List Comprehension Loop Variables)

Python 2에서는 for 루프에서 사용하는 제어 변수와 같은 이름의 전역 변수가 이미 존재하면, 리스트 컴프리헨션 실행 후 전역 변수의 값이 의도치 않게 변경되는 문제가 있었습니다.

Python 3에서는 이 문제가 수정되었습니다. 이제 for 루프의 제어 변수가 외부로 새어 나오지(leak) 않기 때문에, 코드의 다른 부분에서 사용 중인 변수 값이 오염될 걱정 없이 자유롭게 변수 이름을 재사용할 수 있습니다.

#Python 2
print 'Python', python_version()
i = 1
print 'before: i =', i
print 'comprehension: ', [i for i in range(5)]
print 'after: i =', i

실행 결과

Python 2.7.6
before: i = 1
comprehension: [0, 1, 2, 3, 4]
after: i = 4

i의 값이 컴프리헨션 실행 후 4로 변경되었음을 확인할 수 있습니다. 반면 Python 3에서는 어떻게 동작하는지 보겠습니다.

#Python 3
import sys
print('Python %s.%s.%s' %sys.version_info[:3])
i = 1
print('before: i =', i)
print('comprehension:', [i for i in range(5)])
print('after: i =', i)

실행 결과

Python 3.6.1
before: i = 1
comprehension: [0, 1, 2, 3, 4]
after: i = 1

컴프리헨션 실행 후에도 i의 값이 그대로 1로 유지되는 것을 볼 수 있습니다.

마치며

지금까지 print 함수, 정수 나눗셈, 유니코드 처리, 예외 발생 문법, 리스트 컴프리헨션의 변수 스코프까지, Python 2와 Python 3의 대표적인 차이점을 살펴보았습니다. 신규 프로젝트라면 당연히 Python 3를 사용해야 하며, 레거시 Python 2 코드를 다루는 개발자라면 위 내용을 숙지하고 마이그레이션 시 발생할 수 있는 오류를 미리 방지하는 것이 좋습니다.