객체 저장 (데이터 지속성)


233

다음과 같은 객체를 만들었습니다.

company1.name = 'banana' 
company1.value = 40

이 개체를 저장하고 싶습니다. 어떻게해야합니까?


1
피클 사용 방법에 대한 간단한 예는 여기에 오는 사람들의 를 참조하십시오 .
마틴 토마

@MartinThoma : 왜 당신이 ( 연관된 질문의 ) 받아 들여진 질문에 대한 대답을 선호 합니까?
martineau

내가 연결했을 때 수락 된 답변에는 없었습니다 protocol=pickle.HIGHEST_PROTOCOL. 내 대답은 피클 대안을 제공합니다.
마틴 토마

답변:


449

pickle표준 라이브러리에서 모듈을 사용할 수 있습니다 . 다음은 예제에 기본 응용 프로그램입니다.

import pickle

class Company(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

with open('company_data.pkl', 'wb') as output:
    company1 = Company('banana', 40)
    pickle.dump(company1, output, pickle.HIGHEST_PROTOCOL)

    company2 = Company('spam', 42)
    pickle.dump(company2, output, pickle.HIGHEST_PROTOCOL)

del company1
del company2

with open('company_data.pkl', 'rb') as input:
    company1 = pickle.load(input)
    print(company1.name)  # -> banana
    print(company1.value)  # -> 40

    company2 = pickle.load(input)
    print(company2.name) # -> spam
    print(company2.value)  # -> 42

파일을 열고 단일 객체를 작성하는 다음과 같은 간단한 유틸리티를 정의 할 수도 있습니다.

def save_object(obj, filename):
    with open(filename, 'wb') as output:  # Overwrites any existing file.
        pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)

# sample usage
save_object(company1, 'company1.pkl')

최신 정보

이것은 인기있는 답변이므로 몇 가지 고급 사용법 주제를 터치하고 싶습니다.

cPickle(또는 _pickle) vspickle

전자는 C로 작성되고 훨씬 빠르기 때문에 실제로 cPickle모듈을 사용하는 것이 거의 항상 바람직합니다 pickle. 그들 사이에는 약간의 차이가 있지만 대부분의 상황에서 동등하며 C 버전은 훨씬 우수한 성능을 제공합니다. 그것을 쉽게 전환 할 수는 없었습니다 import.

import cPickle as pickle

Python 3에서는 cPickle이름이 바뀌 _pickle었지만 pickle이제 모듈이 자동으로 수행 되므로 더 이상 필요하지 않습니다 . Python 3에서 pickle과 _pickle의 차이점무엇입니까?를 참조하십시오 . .

요약은 코드가 Python 2와 3에서 모두 사용 가능한 경우 코드가 항상 C 버전을 사용 하도록하기 위해 다음과 같은 것을 사용할 수 있다는 것입니다 .

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

데이터 스트림 형식 (프로토콜)

pickle"프로토콜 버전 0"은 ASCII이므로 "사람이 읽을 수있는" 문서에 설명 된대로 프로토콜 이라고하는 여러 가지 파이썬 고유의 형식으로 파일을 읽고 쓸 수 있습니다 . 0보다 큰 버전은 바이너리이며 사용 가능한 가장 높은 버전은 사용중인 Python 버전에 따라 다릅니다. 기본값은 Python 버전에 따라 다릅니다. Python 2에서 기본값은 Protocol version 이지만 Python 3.8.1에서는 Protocol version 입니다. Python 3.x에서 모듈에 추가되었지만 Python 2에는 존재하지 않습니다.04pickle.DEFAULT_PROTOCOL

운 좋게도 pickle.HIGHEST_PROTOCOL모든 호출에서 쓰는 것은 축약 형입니다 (원하는 것으로 가정하고 일반적으로 수행한다고 가정) -1. 음수 인덱스를 통해 시퀀스의 마지막 요소를 참조하는 것과 비슷한 리터럴 숫자 만 사용하십시오 . 따라서 글을 쓰는 대신 :

pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)

당신은 쓸 수 있습니다 :

pickle.dump(obj, output, -1)

어느 쪽이든, Pickler다중 피클 작업에 사용할 객체를 만든 경우 프로토콜을 한 번만 지정했을 것입니다 .

pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
   etc...

참고 : 다른 버전의 Python을 실행하는 환경에서 모든 프로토콜이 읽을 수있는 특정 프로토콜 번호를 명시 적으로 사용 (하드 코드)하고 싶을 것입니다 (나중 버전은 일반적으로 이전 버전에서 생성 된 파일을 읽을 수 있음) .

여러 객체

피클 파일이있는 동안 중 알 수없는 번호가있을 때, 위의 샘플과 같이 절인 객체의 수를 포함, 그것은 가변 크기의 컨테이너 일종의 그들 모두를 저장하는 유사한하는 것이 쉽게 list, tuple또는 dict및 쓰기 한 번의 호출로 파일에 모두 :

tech_companies = [
    Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')

다음을 사용하여 목록과 목록의 모든 내용을 복원하십시오.

with open('tech_companies.pkl', 'rb') as input:
    tech_companies = pickle.load(input)

가장 큰 장점은 나중에 다시로드하기 위해 얼마나 많은 객체 인스턴스가 저장되어 있는지 알 필요가 없다는 것입니다 (정보 없으면 가능하지만 약간 특수화 된 코드가 필요합니다). 관련 질문에 대한 답변보기 피클 파일로 여러 객체 저장 및로드? 다양한 방법에 대한 자세한 내용은 개인적으로 나는 @Lutz Prechelt의 답변 이 가장 좋습니다. 여기 예제에 맞게 조정되었습니다.

class Company:
    def __init__(self, name, value):
        self.name = name
        self.value = value

def pickled_items(filename):
    """ Unpickle a file of pickled data. """
    with open(filename, "rb") as f:
        while True:
            try:
                yield pickle.load(f)
            except EOFError:
                break

print('Companies in pickle file:')
for company in pickled_items('company_data.pkl'):
    print('  name: {}, value: {}'.format(company.name, company.value))

1
객체를 저장하는 더 쉬운 방법이있을 것이라고 상상했기 때문에 이것은 드물다 ... 'saveobject (company1, c : \ mypythonobjects)
Peterstone

4
@ Peterstone : 하나의 객체 만 저장하려면 예제의 절반 정도의 코드 만 필요합니다. 하나 이상의 객체를 저장할 수있는 방법을 보여주기 위해 의도적으로 작성했습니다. from) 같은 파일입니다.
martineau

1
@Peterstone, 책임을 분리해야 할 이유가 아주 많습니다. 이 방법으로 산세 공정의 데이터가 사용되는 방식에는 제한이 없습니다. 디스크에 저장하거나 네트워크 연결을 통해 전송할 수도 있습니다.
Harald Scheirich

3
@martinaeau, 이것은 perstones에 대한 응답으로, 하나의 기능이 하나의 객체를 디스크에 저장하는 기능을 가져야한다고 언급했다. 피클의 책임은 객체를 청크로 처리 할 수있는 데이터 로만 전환하는 것입니다. 파일에 물건을 쓰는 것은 파일 객체의 책임입니다. 분리 된 데이터를 유지함으로써 더 높은 재사용을 가능하게합니다. 예를 들어 피클 링 된 데이터를 네트워크 연결을 통해 전송하거나 데이터베이스에 저장함으로써 모든 책임은 실제 데이터와 분리되어 있습니다 – 객체 변환
Harald Scheirich

1
company1과를 삭제 company2합니다. 또한 Company어떻게되는지 삭제 하고 보여주지 않겠습니까?
Mike McKerns

49

객체가이라고 가정하는 것은 매우 강력한 가정이라고 생각합니다 class. 그렇지 않은 경우 어떻게해야 class합니까? 인터프리터에서 객체가 정의되지 않았다는 가정도 있습니다. 인터프리터에 정의되어 있으면 어떻게됩니까? 또한 속성이 동적으로 추가 된 경우 어떻게해야합니까? 일부 파이썬 객체가 __dict__생성 후 속성이 추가되면 해당 속성의 추가를 pickle존중하지 않습니다 (즉 pickle, 객체 정의에 대한 참조로 직렬화 되기 때문에 속성이 추가 된 것을 잊어 버립니다 ).

이러한 모든 경우에, pickle그리고 cPickle끔찍하게 당신을 실패 할 수 있습니다.

object속성 (객체 정의에 추가되거나 나중에 추가됨)이있는 곳 (임의로 생성 됨) 을 저장하려는 경우 가장 좋은 방법은을 사용 dill하는 것입니다. 파이썬에서 거의 모든 것을 직렬화 할 수 있습니다.

우리는 수업으로 시작합니다…

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> with open('company.pkl', 'wb') as f:
...     pickle.dump(company1, f, pickle.HIGHEST_PROTOCOL)
... 
>>> 

이제 종료하고 다시 시작하십시오 ...

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> with open('company.pkl', 'rb') as f:
...     company1 = pickle.load(f)
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1378, in load
    return Unpickler(file).load()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 858, in load
dispatch[key](self)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1090, in load_global
    klass = self.find_class(module, name)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1126, in find_class
    klass = getattr(mod, name)
AttributeError: 'module' object has no attribute 'Company'
>>> 

죄송합니다… pickle처리 할 수 ​​없습니다. 시도합시다 dill. 다른 객체 유형 (lambda좋은 측정을 위해 )을 것입니다.

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill       
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> 
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>> 
>>> with open('company_dill.pkl', 'wb') as f:
...     dill.dump(company1, f)
...     dill.dump(company2, f)
... 
>>> 

이제 파일을 읽습니다.

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('company_dill.pkl', 'rb') as f:
...     company1 = dill.load(f)
...     company2 = dill.load(f)
... 
>>> company1 
<__main__.Company instance at 0x107909128>
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>>    

효과가있다. 그 이유는 pickle실패하고 dill그렇지 않은 이유 는 (대부분) 모듈처럼 dill취급 __main__하고 참조로 피클 링하는 대신 클래스 정의를 피클 할 수 있기 때문입니다 (예 :pickle 입니다. dilla를 피클 링 할 수 있는 이유 lambda는 그것이 이름을 부여하기 때문입니다. 그러면 피클 링 매직이 일어날 수 있습니다.

실제로, 특히 만든 많은 개체가있는 경우 이러한 모든 개체를 저장하는 더 쉬운 방법이 있습니다. 전체 파이썬 세션을 덤프하고 나중에 다시 방문하십시오.

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> 
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>> 
>>> dill.dump_session('dill.pkl')
>>> 

이제 컴퓨터를 끄고 에스프레소 등을 즐기고 나중에 다시 오십시오 ...

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('dill.pkl')
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>> company2
<function <lambda> at 0x1065f2938>

유일한 주요 단점은 dill 파이썬 표준 라이브러리의 일부가 아니라는 것입니다. 따라서 서버에 파이썬 패키지를 설치할 수 없으면 사용할 수 없습니다.

그러나 시스템에 파이썬 패키지를 설치할 수 있다면로 최신 버전 dill을 구할 수 있습니다 git+https://github.com/uqfoundation/dill.git@master#egg=dill. 로 최신 릴리스 버전을 얻을 수 있습니다 pip install dill.


내가 받고 있어요 TypeError: __new__() takes at least 2 arguments (1 given)사용하려고 할 때 dill오디오 파일을 포함하는 다소 복잡한 객체 (약속 보이는).
MikeiLL

1
@MikeiLL : TypeError정확히 무엇을 할 때 당신은 시간을 받고 있습니까? 일반적으로 클래스 인스턴스를 인스턴스화 할 때 잘못된 수의 인수가 있음을 나타냅니다. 이 질문이 위 질문의 워크 플로에 속하지 않는 경우 다른 질문으로 게시하거나 전자 메일로 제출하거나 dillgithub 페이지 에 문제로 추가 할 수 있습니까?
Mike McKerns

3
@MikeLL이 게시 한 관련 질문 은 다음과 같습니다 . 답변에서 dill문제 가되지 않았습니다 .
martineau

dilMemoryError그래도 나에게 줘 ! 그렇게 cPickle, pickle하고 hickle.
Färid Alijani

4

anycache 를 사용 하여 작업을 수행 할 수 있습니다 . 모든 세부 사항을 고려합니다.

  • 을 백엔드로 사용 하여 파이썬 pickle모듈을 확장하여 lambda모든 멋진 파이썬 기능 을 처리 합니다.
  • 다른 객체를 다른 파일에 저장하고 올바르게 다시로드합니다.
  • 캐시 크기를 제한합니다
  • 캐시 지우기 허용
  • 여러 실행간에 객체를 공유 할 수 있습니다
  • 결과에 영향을 미치는 입력 파일을 존중할 수 있습니다

myfunc인스턴스를 생성 하는 함수가 있다고 가정합니다 .

from anycache import anycache

class Company(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

@anycache(cachedir='/path/to/your/cache')    
def myfunc(name, value)
    return Company(name, value)

Anycache myfunc는 처음에 호출 하고 파일 cachedir이름으로 함수 이름 및 인수에 따라 고유 식별자 를 사용하여 결과를 파일로 피클합니다 . 연속 실행시 절인 개체가로드됩니다. 이 경우 cachedir파이썬 실행 사이의 보존, 절임 객체는 이전 파이썬 실행에서 가져옵니다.

자세한 내용은 설명서를 참조하십시오


예를 들어 (함수 호출의 결과가 아닌) 컨테이너 또는 컨테이너 anycache의 둘 이상의 인스턴스를 저장 하는 데 어떻게 사용 합니까? classlist
martineau

2

company1python3과 함께 귀하의 질문에서 사용 하는 빠른 예 .

import pickle

# Save the file
pickle.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = pickle.load(open("company1.pickle", "rb"))

그러나이 답변에서 지적했듯이 피클은 종종 실패합니다. 그래서 당신은 정말로 사용해야 dill합니다.

import dill

# Save the file
dill.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = dill.load(open("company1.pickle", "rb"))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.