pickle
직렬화 형식이 안전하지 않다.
악의적인 pickle데이터가 자신을 역직렬화하는 파이썬 프로그램의 일부를 취약하게 만들 수 있다.
Json
설계상 안전하다
객체 계층 구조를 간단하게 묘사한 값이 들어있다.
역직렬화해도 추가적인 위험에 노출될 일이 없다.
서로 신뢰할 수 없는 프로그램 통신 시 JSON같은 형식 사용한다.
코드로 보는 예시
class GameState:
def __init__(self):
self.level = 0
self.lives = 4
state = GameState()
state.level += 1
state.lives -= 1
print(state.__dict__)
{'level': 1, 'lives': 3}
#dump 함수를 사용해서 GameState 객체를 파일에 기록
import pickle
state_path = 'game_state.bin'
with open(state_path, 'wb') as f:
pickle.dump(state, f)
#직렬화된 적이 없게 다시 돌려받기
with open(state_path,'rb') as f:
state_after = pickel.load(f)
print(state_after.__dict__)
역직렬을 pickel을 활용하여 할 경우 클래스의 인스턴스 동작이 더 혼랍스럽다.
왜냐하면, pickle 모듈이 작동하는 방식의 부산물이기때문이다.
위의 문제 해결
pickel 동작 제어 및 동작의 신뢰성을 높인다.
디폴트 인자가 있는 생성자를 사용하면 GameState 객체를 언피클 했을 때도 함상 필요한 모든 애트리뷰트 포함
class GameState:
def __init__(self, level=0, lives=4, points=0):
self.level = level
self.lives = lives
self.points = points
#필요한 도우미 함수 만들기
def pickle_game_state(game_state):
kwargs = game_state.__dict__
return unpickle_game_state, (kwargs,)
def unpickle_game_state(kwargs):
return GameState(**kwargs)
import copyreg
copyreg.pickle(GameState, pickle_game_state)
state = GameState()
state.points += 1000
serialized = pickle.dumps(state)
state_after = pickle.loads(serialized)
print(state_after.__dict__)
하위 호환성으로 파이썬 객체의 필드가 제거되면 디폴트 인자를 사용할 수 없다.
해결: copyreg함수에게 전달하는 함수에 버전 파라미터 추가를하면 된다.
def pickle_game_state(game_state):
kwargs = game_state.__dict__
kwargs['version'] = 2
return unpickle_game_state, (kwargs,)
#GameState 생성자에 전달할 인자를 적절히 변경 가능
def unpickle_game_state(kwargs):
version = kwargs.pop('version', 1)
if version == 1:
del kwargs['lives']
return GameState(**kwargs)
클래스 이름을 바꾸면 코드가 깨지는 경우가 이ㅆ다.
프로그램이 존재하는 생명 주기에서 클래스 이름을 변경하거나 클래스를 다른 모듈로 옮기는 방식으로 코드 리팩터링
copyreg.pickle(BetterGameState, pickle_game_state)
신뢰할 수 있는 프로그램 사이에 객체를 직렬화하고 역직렬화할 때는 pickle 내장 모듈이 유용하다.
시간이 지남에 따라 클래스가 바뀔(애트리뷰트의 추가나 삭제 등)수 있으므로 이전에 피클한 객체를 역직렬화면 문제가 생긴다.
직렬화한 객체의 하위 호환성을 보장하고자 copyreg 내장 모듈과 pickle를 함께 사용