우리는 최근 크롤러에서 얻은 다운로드 결과를 보관하고 싶었습니다. 이 결과는 Python 개체입니다(단순히 HTML이나 json을 저장하고 싶지 않고 전체 다운로드 프로세스를 복원하고 싶습니다). Python의 내장 피클 라이브러리(피클 오이 라이브러리)를 사용하면 객체를 바이트로 직렬화하고 필요할 때 역직렬화할 수 있습니다.
다음 코드를 통해 피클의 사용법과 기능을 간단하게 이해할 수 있습니다.
In [2]: import pickle In [3]: class A: pass In [4]: a = A() In [5]: a.foo = 'hello' In [6]: a.bar = 2 In [7]: pick_ed = pickle.dumps(a) In [8]: pick_ed Out[8]: b'\x80\x03c__main__\nA\nq\x00)\x81q\x01}q\x02(X\x03\x00\x00\x00fooq\x03X\x05\x00\x00\x00helloq\x04X\x03\x00\x00\x00barq\x05K\x02ub.' In [9]: unpick = pickle.loads(pick_ed) In [10]: unpick Out[10]: <__main__.A at 0x10ae67278> In [11]: a Out[11]: <__main__.A at 0x10ae67128> In [12]: dir(unpick) Out[12]: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slotnames__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo'] In [13]: unpick.foo Out[13]: 'hello' In [14]: unpick.bar Out[14]: 2
pickle의 사용법이 json과 다소 유사하다는 것을 알 수 있지만 몇 가지 근본적인 차이점이 있습니다.
json은 언어 간 범용 데이터 교환 형식으로 일반적으로 텍스트로 표현되며 사람이 읽을 수 있습니다. pickle은 Python 개체를 직렬화하는 데 사용됩니다. 직렬화의 결과는 사람이 읽을 수 없는 이진 데이터입니다. 게다가 json은 기본적으로 내장 유형 중 일부만 직렬화할 수 있으며, pickle은 상당히 많은 데이터를 직렬화할 수 있습니다.
또한 고대 원수도 내장되어 있습니다. 하지만 이 라이브러리는 주로 .pyc 파일용입니다. 사용자 정의 유형은 지원되지 않으며 완전하지 않습니다. 예를 들어, 순환 애플리케이션을 처리할 수 없습니다. 객체가 자신을 참조하는 경우 마샬링을 사용할 때 Python 인터프리터가 중단됩니다.
버전 호환성 문제
pickle은 Python용이고 Python에는 버전이 다르기 때문에(그리고 2와 3의 차이가 매우 크기 때문에) 직렬화된 객체가 더 높을 수 있는지(또는 더 낮을 수 있는지?) 고려해야 합니다. 역직렬화하려면 Python 버전을 사용하세요.
현재 5개의 피클 프로토콜 버전이 있습니다. 버전이 높을수록 Python 버전이 0-2는 Python3용이고 3-4는 Python3입니다.
Protocol version 0 is the original “human-readable” protocol and is backwards compatible with earlier versions of Python.Protocol version 1 is an old binary format which is also compatible with earlier versions of Python.Protocol version 2 was introduced in Python 2.3. It provides much more efficient pickling of new-style classes. Refer to PEP 307for information about improvements brought by protocol 2. (从这个版本往后,性能有显著提高)Protocol version 3 was added in Python 3.0. It has explicit support for bytes objects and cannot be unpickled by Python 2.x.This is the default protocol, and the recommended protocol when compatibility with other Python 3 versions is required.Protocol version 4 was added in Python 3.4. It adds support for very large objects, pickling more kinds of objects, and some data format optimizations. Refer to PEP 3154 for information about improvements brought byprotocol 4.
피클의 대부분의 입력 함수(예: dump( ), 덤프(), Pickler 생성자)는 모두 두 개의 내장 변수가 있는 프로토콜 버전 매개변수를 허용합니다.
pickle.HIGHEST_PROTOCOL은 현재 4
pickle.DEFAULT_PROTOCOL은 현재 3
usage
이고 내장 -in json 모듈 인터페이스는 유사하며, dump()는 직렬화 결과를 반환하는 데 사용되고, dump()는 직렬화한 다음 파일에 쓰는 데 사용됩니다. 같은 방식으로 load()와 load()가 있습니다. 그 중 덤프를 직렬화할 때 프로토콜 버전을 지정할 수 있습니다. 이는 역직렬화할 때 필요하지 않으며 버전이 자동으로 식별됩니다. 이는 zip 명령과 매우 유사합니다.
내장 유형의 직렬화
대부분의 내장 유형은 직렬화 및 역직렬화를 지원합니다. 특별한 주의가 필요한 것은 함수입니다. 함수의 직렬화는 함수 이름과 함수가 위치한 모듈을 기반으로 합니다. 함수의 코드나 속성(Python의 함수는 일급 객체이며 속성을 가질 수 있음)은 직렬화되지 않습니다. 이를 위해서는 함수가 위치한 모듈을 unpickle 환경에서 가져올 수 있어야 합니다. 그렇지 않으면 ImportError 또는 AttributeError가 발생합니다.
여기에는 매우 흥미로운 점이 있습니다. 모든 람다 함수는 Pickleable이 아닙니다. 그 이유는 이름이
사용자 정의 유형의 직렬화
이 기사 시작 부분의 실험 코드와 마찬가지로 대부분의 경우 사용자 정의 개체는 추가 작업 없이 직렬화/역직렬화될 수 있습니다. 역직렬화 프로세스 중에 클래스의 __init__()는 객체를 초기화하기 위해 호출되지 않지만 초기화되지 않은 새 인스턴스가 생성된 다음 해당 속성이 복원된다는 점에 유의해야 합니다(매우 영리함). 의사코드는 다음과 같습니다:
def save(obj): return (obj.__class__, obj.__dict__) def load(cls, attributes): obj = cls.__new__(cls) obj.__dict__.update(attributes) return obj
직렬화 프로세스 중에 객체 상태 저장과 같은 추가 작업을 수행하려는 경우 피클 프로토콜의 매직 메서드를 사용할 수 있으며 가장 일반적인 메서드는 __setstate__()입니다. 그리고 __getstate__().
보안 문제(!)
피클 문서의 시작 부분에는 다음과 같이 나와 있습니다. 출처를 알 수 없는 바이너리는 절대로 피클 해제하지 마세요. 다음 코드를 고려하세요:
>>> import pickle >>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.") hello world 0
이 코드는 unpickled import os.system()을 한 다음 echo를 호출합니다. 부작용은 없습니다. 하지만 rm -rf /·라면 어떨까요?
문서에서 제공하는 제안은 Unpickler.find_class()에서 검사 논리를 구현하는 것입니다. 전역 변수가 필요한 경우 함수 메서드를 호출해야 합니다.
import builtins import io import pickle safe_builtins = { 'range', 'complex', 'set', 'frozenset', 'slice', } class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): # Only allow safe classes from builtins. if module == "builtins" and name in safe_builtins: return getattr(builtins, name) # Forbid everything else. raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name)) def restricted_loads(s): """Helper function analogous to pickle.loads().""" return RestrictedUnpickler(io.BytesIO(s)).load()
압축
피클이 피클의 작업을 분리하고 압축은 다른 라이브러리에 맡겨지는 디자인이 매우 좋다고 생각합니다. 또한 피클링 후 파일을 읽을 수 없더라도 내용은 여전히 ASCII 코드로 표시되고 왜곡되지 않는다는 것을 알 수 있습니다. 압축 라이브러리의 압축을 호출해야 합니다. 실제 압축을 해보니 볼륨이 전작에 비해 1/3정도로 줄어들어 매우 인상적입니다.
요약
전역 변수를 가져오기 가능하게 유지하는 것은 약간 어렵습니다. 제가 직면해야 할 질문은, 오늘 절인 물건을 미래에 열어야 할 경우에도 열어볼 수 있느냐는 것입니다.
여기에는 프로젝트 버전, Python 버전, 피클 프로토콜 버전, 프로젝트가 의존하는 패키지 버전 등 여러 버전이 있습니다. 그 중에서 Python 버전과 Pickle 버전은 이전 버전과의 호환성을 믿고 안전하게 해결할 수 있고 해결하기 쉽다고 생각합니다. 주로 프로젝트와 버전 및 종속 버전을 선택합니다. 선택할 개체가 매우 복잡한 경우 이전 버전의 백업이 새 버전과 호환되지 않을 가능성이 높습니다. 가능한 해결책은 해시 값 기록과 같은 모든 종속성을 완전히 잠그는 것입니다. 특정 바이너리 시퀀스를 복원하려면 해당 시점에 프로젝트의 특정 종속성과 특정 커밋을 복원하세요.
하지만 현재로서는 기본적으로 요청.응답 개체를 피클하는 것이 요구 사항입니다. 나는 우리가 이전 버전과의 호환성에 의존할 수 있다고 생각합니다. 어느 날 요청에 큰 변화가 생기면 피클이 호환되더라도 그 당시에는 다른 전략을 고려할 수 있습니다.
위 내용은 Python 내장 피클 라이브러리의 객체 직렬화 및 역직렬화의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!