pickle 如何创建一个实例来无视构造函数?

How does pickle create an instance in defiance of the constructor?

我的最终目标是让 class 具有常规构造函数,但还有第二个构造函数,可用于创建 class.

的部分实例化版本
class Foo(object):
   def __init__(self, foo, bar):
      self.foo = foo
      self.bar = bar
   def create_partial(self, **kwargs):
      self.foo = kwargs.get('foo', None)
      self.bar = kwargs.get('bar', None)

我注意到 pickle 以某种方式实现了这一点。您可以编写仅处理其中一个参数的 __getstate____setstate__ 函数,它会忽略您尚未定义其中一个参数的事实。例如

class Foo(object):
  def__init__(self, foo, bar):
    self.foo = foo
    self.bar = bar
  def __getstate__(self):
    return {'foo': self.foo}
  def __setstate__(self, d):
    self.foo = d['foo']

然后我可以 pickle 和 unpickle 一个实例来得到一个半实例化的对象

myfoo = pickle.loads(pickle.dumps(Foo(1,2)))
print(myfoo.foo) # 1
print(myfoo.bar) # throws AttributeError
type(myfoo) # __main__.Foo

所以,我的问题是,它如何设法获得 class 的空白实例,将其实例化一半并让它仍然报告其类型为 Foo,此外,如果没有必须经过泡菜?

P.S。我知道想要做这件事很疯狂,但我想知道如何

您可以调用 Foo.__new__(Foo) 来创建您的对象,而无需通过 __init__

当您 运行 Foo() 时,我们认为这是调用 Foo.__init__()。但实际上,在此之前还有一个额外的步骤,即创建对象。 foo = Foo(a, b, c) 大致相当于

foo = Foo.__new__(Foo, a, b, c)
foo.__init__(a, b, c)

__new__是一个很少使用的魔术方法,它实际上构造对象。您几乎不需要重写它(重写它的最常见用例是在子类化 float 等内置类型时),并且大多数 类 继承默认值 object.__new__。我们可以直接调用后一个函数。

# Assuming that Foo doesn't override __new__
myfoo = Foo.__new__(Foo)
# Or, to be extra safe
myfoo = object.__new__(Foo)

请注意,__new__ 将预期的类型作为第一个参数,因此即使我们调用 object.__new__,我们仍然会得到 Foo,因为我们将 Foo 传递为参数。在此过程中没有施工人员受到伤害。