2012-01-02 3 views
3

C에서 Python 확장을 작성했습니다. 아무런 문제가 없습니다. 이제는 피클을 사용하고 싶습니다. 설명서에서 나는 혼란 스럽다.C++로 작성된 Python 확장에 pickle을 추가하십시오.

은 내가 무엇을 그리워하는 것은

http://docs.python.org/library/pickle.html#the-pickle-protocol

은 "호출 대상"합니다 무엇을 : 나는 "확장"여기를 제안 시험 __reduce__ 기능을 썼다. 나는 PyObject_Type(self)에서 결과를 시도했다. 이것은 "호출 가능"이며 근본적으로 작동하지만 객체가 채워지는 경우 __init__이 호출되어 약간의 문제가 발생합니다.

클래스 초기화를 피하면서 __new__ 메서드를 호출하는 호출 가능하도록하는 표준 방법이 있습니까?

+0

원칙적으로'__init__'는 호출되어서는 안되며, pickle이 선호하는 방법입니다. '__getinitargs__' 아래를보십시오 : "pickled 클래스 인스턴스가 unpickle되었을 때, 그것의'__init __()'메쏘드는 정상적으로 * 호출되지 않습니다." '__getinitargs__'를 구현하고 있습니까? –

+0

좋아, 잊어 버려. 그것은 정상적인 수업에만 해당됩니다. –

답변

2

__new__을 호출하고 유형을 메타 클래스로 사용하는 경우 __new__의 결과가 유형의 하위 유형 인 경우 __init__이 호출됩니다. 다른 말로하면, Foo 클래스가 있다고 가정 해 봅시다. PyObject_Type(self)의 결과를 호출하면 Foo()과 동일합니다. 이것은 Foo를 의미합니다. __new__이 호출되고 반환 값이 Foo의 하위 유형이면 __init__이 호출됩니다.

Foo()를 호출하면 type_call (typeobject.c에서)이 실제로 호출되므로 tp_new 다음에 tp_init가옵니다. PyObject_Type (self) 대신 Foo_new()와 같이 객체의 새로운 기능을 직접 제공하면 __init__을 호출하지 않고 __new__을 호출하면 원하는 것을 얻을 수 있습니다. (Foo를 __new__의 인수로 제공하는 것을 잊지 마십시오.)

마지막으로 질문에 대답 하시려면 Foo.__new__(Foo, ...)으로 전화하십시오. 여기 당신이 찾고있는 것을 수행하는 코드가 있습니다. 나는이 모든 것을 알아 내려고 할 때 여담으로

class Foo(object): 
    def __new__(cls): 
     return super(Foo, cls).__new__(cls) 

    def __init__(self): 
     print "__init__" 

    def __reduce__(self): 
     return (Foo.__new__, (Foo,)) 

print "one" 
x = Foo()    # prints __init__ 

print "two" 
y = Foo.__new__(Foo) # does not print __init__ 

print "three" 
import pickle 
p = pickle.dumps(Foo) 
z = pickle.loads(p) # does not print __init__ 

은 거의 모든 경우에, 나는 사실 내 코드를 구현하고 __init__ 호출 할 수있는 취약점이 발견했다. 내 오류는 __reduce__ 튜플의 세 번째 인수로 들어가는 항목을 분리하지 않는다는 사실에서 비롯되었습니다. (__init__에서 받아 들여지면) 두 번째 인수에 아무 것도 입력하지 않아도되며, 단지 __dict__을 직접 업데이트하는 세 번째 인수에서 여러 가지를 제공하면됩니다. Modules and Objects 디렉토리에서 cpython 소스 코드를 살펴보면, __reduce__의 구현을 볼 수 있습니다.

+0

안녕하세요. Nathan, 감사합니다.하지만 Python에서 설명한 내용이 제 컴파일 된 C++ 확장에 정의 된 유형에서 발생하지 않았기 때문에 감사합니다. 특히 '__reduce__'출력의 첫 번째 항목은 다음과 같습니다 : 'PyTuple_SetItem (res, 0, PyObject_Type (self)); ' '이 표시되면 _pickle.loads (...) _ 또는 이와 비슷한 결과가 나타납니다. 객체 자체를 호출하고 실제로'__new__'과'__init__'을 거치게됩니다. 필자는 Pickle이 프로토콜 2를 사용하도록하는 간단한 해결책을 발견했습니다. 이것은'__reduce__' 함수를 구현할 필요는 없지만'__getnewargs__'와 보통'__setstate__'과'__getstate__'를 필요로합니다. –

+0

맞아,'PyObject_Type (self)'를 반환하는 대신 self-> tp_new를 사용하고 싶습니다. 그렇게 해봤습니까? 그것이 작동하는지 작동하지 않는지 알려주십시오.그렇다면 내 소식을 업데이트 할 것입니다. 그렇지 않으면 내가 잘못 쓴 것을 찾아 낼 것이다. –

관련 문제