2012-01-02 1 views
3

Я написал расширение Python в C. Он работает, никаких проблем. Теперь я хочу использовать соленый соус. Из документации я смущен. Я написал тестовую __reduce__ функции, предложенные здесь «расширения»:Добавить pickle на расширение Python, написанное на C++

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

, что я пропускаю это то, что «вызываемый объект» должен быть. Я попытался с результатом PyObject_Type(self). Это «вызываемый» и, по существу, работает, но когда объект неизолирован, он вызывает __init__, и это дает мне некоторые проблемы.

Есть ли стандартный способ иметь вызываемый вызов, который вызывает только метод __new__, избегая инициализации класса?

+0

В принципе, '__init__' не следует вызывать, и это предпочтительный способ рассола. См. Раздел '__getinitargs__':« Когда экземпляр маринованного класса не заполнен, его метод '__init __()' обычно * не * вызывается ». Вы внедряете '__getinitargs__'? –

+0

Хорошо, забудьте об этом. Это только для нормальных классов. –

ответ

2

Если вы звоните __new__, и вы используете тип как ваш метакласс, то будет вызван __init__, если результат __new__ является подтипом вашего типа. Иными словами, скажем, у вас есть класс Foo. Если вы вызываете результат PyObject_Type(self), это то же самое, что и звонок Foo(). Это означает, что Foo. __new__ будет вызываться, и если возвращаемое значение является подтипом Foo, то будет вызываться __init__.

Копаем немного глубже, когда вы вызываете Foo(), это действительно вызывает type_call (в typeobject.c), вот что делает tp_new, за которым следует tp_init. Если вы напрямую предоставляете новую функцию своего объекта (например, Foo_new() вместо PyObject_Type (self)), вы вызываете __new__ без звонка __init__, и вы можете получить то, что ищете. (Не забудьте указать 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__. Если вы просмотрите исходный код cpython в каталоге Modules and Objects, вы увидите ряд реализаций __reduce__, все из которых работают именно так.

+0

Привет, Натан, спасибо, но почему-то то, что вы описали в 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, попробовали ли вы это? Сообщите мне, если это работает или не работает.Если да, я обновляю свой пост. Если это не так, я пойду, что я написал неправильно. –