PEP 290 – 코드 마이그레이션 및 현대화
- Author:
- Raymond Hettinger <python at rcn.com>
- Status:
- Active
- Type:
- Informational
- Created:
- 06-Jun-2002
- Post-History:
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 최신 버전의 Python이 설치되었을 때 Python 애플리케이션을 업데이트하기 위한 절차와 아이디어를 모은 것입니다.
마이그레이션 팁은 호환되지 않을 가능성이 있는 영역을 강조하고, 이러한 차이를 찾고 해결하는 방법을 제안합니다. 현대화 절차는 새로운 언어 기능을 활용하도록 이전 코드를 업데이트하는 방법을 보여 줍니다.
근거
이 절차 저장소는 알려진 마이그레이션 문제와 그러한 문제를 해결하기 위한 절차의 카탈로그 또는 체크리스트 역할을 합니다.
마이그레이션 문제는 여러 가지 이유로 발생할 수 있습니다. 일부 사용되지 않는 기능은 PEP 4의 지침에 따라 서서히 폐기됩니다. 또한 일부 코드는 버전 간에 변경될 수 있는 문서화되지 않은 동작에 의존합니다. 일부 코드는 이후에 버그로 밝혀진 동작에 의존할 수 있으며, 버그가 수정되면 해당 동작이 변경됩니다.
새로운 Python 버전에서 이전에 제공되던 것보다 향상된 명확성이나 성능을 제공하는 기능을 추가하면 현대화 옵션이 생깁니다.
새 항목을 위한 지침
커밋 권한이 있는 개발자는 이 PEP를 직접 업데이트할 수 있습니다. 그 외의 사람은 개발자에게 아이디어를 보내 포함 가능성을 검토하도록 할 수 있습니다.
일관된 형식을 사용하면 저장소를 더 쉽게 이용할 수 있지만, 명확성을 높이기 위해 섹션을 자유롭게 추가하거나 삭제하십시오.
유지 관리자가 업데이트할 가능성이 있는 코드를 찾는 데 도움이 되도록 grep 패턴을 도구로 제공할 수 있습니다. 그러나 완전히 자동화된 검색/대체 방식의 정규 표현식은 권장하지 않습니다. 대신 각 코드 조각을 개별적으로 평가해야 합니다.
금기 사항 섹션은 새 항목에서 가장 중요한 부분입니다. 이 섹션에는 업데이트를 적용해서는 안 되는 알려진 상황이 나열됩니다.
마이그레이션 문제
비교 연산자는 0 또는 1을 생성하기 위한 지름길이 아닙니다.
Python 2.3 이전에는 비교 연산이 True 또는 False가 아니라 0 또는 1을 반환했습니다. 일부 코드는 불리언에 해당하는 값이 적절하지 않은 곳에서 0 또는 1을 생성하기 위한 지름길로 이를 사용했을 수 있습니다. 예를 들어:
def identity(m=1):
"""Create and m-by-m identity matrix"""
return [[i==j for i in range(m)] for j in range(m)]
Python 2.2에서는 identity(2)를 호출하면 다음이 생성됩니다.:
[[1, 0], [0, 1]]
Python 2.3에서는 동일한 호출이 다음을 생성합니다.:
[[True, False], [False, True]]
불리언은 정수의 서브클래스이므로 행렬은 계속 정상적으로 계산되지만, 예상대로 출력되지는 않습니다. 리스트 컴프리헨션은 다음과 같이 변경해야 합니다.:
return [[int(i==j) for i in range(m)] for j in range(m)]
True 또는 False 대신 숫자를 예상할 수 있는 다른 애플리케이션에서 사용할 데이터를 저장할 때도 비슷한 문제가 발생합니다.
현대화 절차
현대화의 이점을 활용하는 데 필요한 Python 버전별로 절차를 그룹화합니다.
Python 2.4 이상
리스트 시작 부분에서 삽입 및 제거하기
Python의 리스트는 오른쪽에서 추가 및 제거를 수행할 때 가장 효율적으로 작동하도록 구현되어 있습니다. pop(0) 또는 insert(0, x)를 사용하면 전체 리스트에 대해 O(n) 데이터 이동이 발생합니다. 이러한 요구를 해결하기 위해 Python 2.4에서는 새로운 컨테이너인 collections.deque()를 도입합니다. 이 컨테이너는 왼쪽과 오른쪽 모두에서 효율적인 추가 및 제거 연산을 제공하지만, 그 대신 getitem/setitem 액세스가 훨씬 느립니다. 새로운 컨테이너는 데이터 큐를 구현하는 데 특히 유용합니다:
패턴:
c = list(data) --> c = collections.deque(data)
c.pop(0) --> c.popleft()
c.insert(0, x) --> c.appendleft()
위치 찾기:
grep pop(0 or
grep insert(0
사용자 지정 정렬 단순화하기
Python 2.4에서는 리스트의 sort 메서드와 새로운 내장 함수 sorted가 모두 정렬 키를 계산하기 위한 key 함수를 허용합니다. 모든 비교에 적용되는 cmp 함수와 달리, 키 함수는 각 레코드에 한 번만 적용됩니다. 이는 cmp보다 훨씬 빠르고 일반적으로 더 읽기 쉬우며 코드도 적게 사용합니다. 키 함수는 정렬의 안정성도 유지합니다(동일한 키를 가진 레코드는 원래 순서로 유지됩니다.
비교 함수를 사용하는 원래 코드:
names.sort(lambda x,y: cmp(x.lower(), y.lower()))
명시적 데코레이션을 사용한 대체 원래 코드:
tempnames = [(n.lower(), n) for n in names]
tempnames.sort()
names = [original for decorated, original in tempnames]
키 함수를 사용하도록 수정한 코드:
names.sort(key=str.lower) # case-insensitive sort
위치 찾기: grep sort *.py
Lambda의 일반적인 사용 대체하기
Python 2.4에서는 operator 모듈에 itemgetter()와 attrgetter()라는 두 가지 새로운 함수가 추가되어 lambda 키워드의 일반적인 사용을 대체할 수 있습니다. 새로운 함수는 더 빠르게 실행되며, 일부 사람들은 가독성을 향상한다고 생각합니다.
패턴:
lambda r: r[2] --> itemgetter(2)
lambda r: r.myattr --> attrgetter('myattr')
일반적인 컨텍스트:
sort(studentrecords, key=attrgetter('gpa')) # set a sort field
map(attrgetter('lastname'), studentrecords) # extract a field
위치 찾기: grep lambda *.py
단순화된 역방향 반복
Python 2.4에서는 역방향 반복을 위한 reversed 내장 함수를 도입했습니다. 역방향 반복을 위한 기존 접근 방식은 장황함, 성능 문제(속도 및 메모리 소비), 명확성 부족 등의 문제를 겪었습니다. 선호되는 스타일은 시퀀스를 정방향으로 표현하고, 결과에 reversed를 적용한 다음, 그 결과로 얻은 빠르고 메모리 효율적인 이터레이터를 반복하는 것입니다.
반열린 구간으로 표현한 원래 코드:
for i in range(n-1, -1, -1):
print seqn[i]
여러 단계로 역순화한 대체 원래 코드:
rseqn = list(seqn)
rseqn.reverse()
for value in rseqn:
print value
확장 슬라이싱으로 표현한 대체 원래 코드:
for value in seqn[::-1]:
print value
reversed 함수를 사용하도록 수정한 코드:
for value in reversed(seqn):
print value
Python 2.3 이상
문자열 멤버십 테스트
Python 2.3에서는 string2 in string1에서 string2의 길이 제한이 해제되어 이제 어떤 길이의 문자열이든 사용할 수 있습니다. 원래 문자열에서 부분 문자열의 위치에 관심이 없는 경우 부분 문자열을 검색할 때 in연산자를 사용하면 의미가 명확해집니다.
패턴:
string1.find(string2) >= 0 --> string2 in string1
string1.find(string2) != -1 --> string2 in string1
apply()를 직접 함수 호출로 대체하기
Python 2.3에서는 함수 호출에 * 및 ** 구문을 도입한 Python 1.6으로 인해 apply()가 더 이상 필요하지 않게 되었으므로 Pending Deprecation으로 표시되었습니다. 직접 함수 호출을 사용하면 내장 함수 조회를 생략할 수 있으므로 항상 apply()를 사용하는 것보다 조금 더 빨랐습니다. 이제 apply()는 warnings 모듈을 사용하기 때문에 더 느립니다.
패턴:
apply(f, args, kwds) --> f(*args, **kwds)
참고: Python 2.3.3에서는 Pending Deprecation이 apply()에서 제거되었습니다. Python 1.5.2까지 거슬러 올라가는 Python 버전에서도 작동하는 코드를 유지 관리해야 하는 사람들에게 문제가 되며, 해당 버전에는 apply()의 대안이 없었기 때문입니다. 그러나 이 함수는 여전히 사용 중단된 상태입니다.
Python 2.2 이상
딕셔너리 멤버십 테스트
딕셔너리 멤버십을 테스트할 때는 ‘has_key()’ 메서드 대신 ‘in’ 키워드를 사용하십시오. 결과가 더 짧고 읽기 쉽습니다. 이 방식은 리스트의 멤버십 테스트와 일관됩니다. has_key는 속성 검색이 필요하고 비교적 비용이 큰 함수 호출을 사용하므로 결과가 약간 더 빠릅니다.
패턴:
if d.has_key(k): --> if k in d:
주의 사항:
- 일부 딕셔너리와 유사한 객체는
__contains__()메서드를 정의하지 않을 수 있습니다.:if dictlike.has_key(k)
찾기: grep has_key
딕셔너리 순회
딕셔너리를 순회할 때는 새로운 iter 메서드를 사용하십시오. iter 메서드는 모든 키, 값 또는 항목의 완전한 복사본을 포함하는 새 리스트 객체를 만들 필요가 없으므로 더 빠릅니다. 필요한 키, 값 또는 항목(키/값 쌍)만 선택하면 임시 객체 참조를 만드는 데 드는 시간을 절약할 수 있으며, 항목의 경우 키의 두 번째 해시 조회도 절약할 수 있습니다.
패턴:
for key in d.keys(): --> for key in d:
for value in d.values(): --> for value in d.itervalues():
for key, value in d.items():
--> for key, value in d.iteritems():
주의 사항:
- 리스트가 필요하다면 반환 형식을 변경하지 마십시오.:
def getids(): return d.keys()
- 일부 딕셔너리와 유사한 객체는
iter메서드를 정의하지 않을 수 있습니다.:for k in dictlike.keys():
- 이터레이터는 슬라이싱, 정렬 또는 기타 연산을 지원하지 않습니다.:
k = d.keys(); j = k[:]
- 딕셔너리 이터레이터는 딕셔너리 수정을 금지합니다.:
for k in d.keys(): del[k]
stat 메서드
stat 상수 또는 인덱스를 새로운 os.stat 속성 및 메서드로 바꾸십시오. os.stat 속성 및 메서드는 순서에 의존하지 않으며 stat 모듈을 가져오지 않아도 됩니다.
패턴:
os.stat("foo")[stat.ST_MTIME] --> os.stat("foo").st_mtime
os.stat("foo")[stat.ST_MTIME] --> os.path.getmtime("foo")
찾기: grep os.stat 또는 grep stat.S
types 모듈에 대한 의존성 줄이기
types 모듈은 향후 폐기될 가능성이 높습니다. 대신 내장 생성자 함수를 사용하십시오. 내장 생성자 함수가 약간 더 빠를 수 있습니다.
패턴:
isinstance(v, types.IntType) --> isinstance(v, int)
isinstance(s, types.StringTypes) --> isinstance(s, basestring)
이 기법을 완전히 사용하려면 Python 2.3 이상이 필요하지만(basestring은 Python 2.3에서 도입되었습니다), 대부분의 용도에는 Python 2.2로 충분합니다.
찾기: grep types *.py | grep import
__builtins__ 모듈과 충돌하는 변수 이름 피하기
Python 2.2에서는 dict 및 file에 대한 새로운 내장 타입이 추가되었습니다. 스크립트에서는 해당 타입을 가리는 변수 이름을 할당하지 않아야 합니다. list와 같은 기존 내장 항목에도 동일한 조언이 적용됩니다.
패턴:
file = open('myfile.txt') --> f = open('myfile.txt')
dict = obj.__dict__ --> d = obj.__dict__
찾기: grep 'file ' *.py
Python 2.1 이상
whrandom 모듈 폐기
무작위 관련 메서드는 모두 random 모듈 한 곳에 모아 두었습니다.
패턴:
import whrandom --> import random
찾기: grep whrandom
Python 2.0 이상
문자열 메서드
string 모듈은 향후 폐기될 가능성이 높습니다. 대신 문자열 메서드를 사용하십시오. 문자열 메서드가 더 빠르기도 합니다.
패턴:
import string ; string.method(s, ...) --> s.method(...)
c in string.whitespace --> c.isspace()
찾기: grep string *.py | grep import
startswith 및 endswith 문자열 메서드
슬라이싱 대신 이러한 문자열 메서드를 사용하십시오. 슬라이스를 만들 필요가 없고 개수를 잘못 셀 위험도 없습니다.
패턴:
"foobar"[:3] == "foo" --> "foobar".startswith("foo")
"foobar"[-3:] == "bar" --> "foobar".endswith("bar")
atexit 모듈
atexit 모듈은 프로그램 종료 시 실행될 여러 함수를 지원합니다. 또한 매개변수화된 함수를 지원합니다. 안타깝게도, 이 구현은 단일 종료 함수만 지원하는 sys.exitfunc 속성과 충돌합니다. sys.exitfunc에 의존하는 코드는 더 새롭고 다재다능한 atexit 모듈을 사용하기로 한 다른 모듈(라이브러리 모듈 포함)과 충돌할 수 있습니다.
패턴:
sys.exitfunc = myfunc --> atexit.register(myfunc)
Python 1.5 이상
클래스 기반 예외
문자열 예외는 폐기되었으므로 Exception 베이스 클래스를 상속받으십시오. 폐기된 문자열 예외와 달리, 클래스 예외는 모두 다른 예외 또는 Exception 베이스 클래스를 상속받습니다. 이를 통해 예외를 의미 있게 그룹화할 수 있습니다. 또한 “except Exception” 절이 모든 예외를 잡을 수 있게 해줍니다.
패턴:
NewError = 'NewError' --> class NewError(Exception): pass
찾기: PyChecker를 사용하십시오.
모든 Python 버전
None에 대한 검사
None 객체는 단 하나뿐이므로, 동등성은 항등성으로 검사할 수 있습니다. 항등성 검사가 동등성 검사보다 약간 더 빠릅니다. 또한 일부 객체 타입은 비교 연산을 오버로드할 수 있으므로, 동등성 검사가 훨씬 느려질 수 있습니다.
패턴:
if v == None --> if v is None:
if v != None --> if v is not None:
찾기: grep '== None' 또는 grep '!= None'
Copyright
This document has been placed in the public domain.