【问题标题】:How does pickle create an instance in defiance of the constructor?pickle 如何无视构造函数创建实例?
【发布时间】:2021-08-13 02:58:52
【问题描述】:

我的最终目标是拥有一个具有常规构造函数的类,而且还有一个可用于创建该类的部分实例化版本的第二个构造函数。

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

所以,我的问题是,它如何设法获得一个空白类实例,将其实例化一半并让它仍然将其类型报告为 Foo,此外,我怎样才能在不经过 pickle 的情况下做同样的事情?

附:我知道这是一件想要做的疯狂的事情,但我想知道如何

【问题讨论】:

    标签: python pickle


    【解决方案1】:

    当您运行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 作为参数传递。在这个过程中没有构造函数受到伤害。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 2020-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-24
        • 1970-01-01
        • 1970-01-01
        • 2015-09-26
        • 2018-09-25
        相关资源
        最近更新 更多