如何继承 defaultdict 并在子类方法中使用它的复制方法?

How can I inherit defaultdict and use its copy method in subclass method?

有代码。

from collections import defaultdict
class A(defaultdict):
  def __init__(self):
    super(A, self).__init__(lambda :0)
    self.x = 1

  def my_copy(self):
    return self.copy()

if __name__ == '__main__':
  a = defaultdict(lambda :0)
  b = a.copy() # no error when using the base class directly
  a = A()
  b = a.my_copy()

出现错误:

Traceback (most recent call last):
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1591, in <module>
    globals = debugger.run(setup['file'], None, None, is_module)
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1018, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
   exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Users/liu/project/scir/pytorch_test/t.py", line 14, in <module>
    b = a.my_copy()
  File "/Users/liu/project/scir/pytorch_test/t.py", line 8, in my_copy
    return self.copy()
TypeError: __init__() takes 1 positional argument but 3 were given

不知道怎么继承copy方法,也不知道为什么要给3个参数

当调用 copy 时,defaultdict 调用带有参数的构造函数,以传递 default_factory 函数和数据。

您的构造函数不接受任何参数,因此它只能使用固定工厂构建空字典。

像这样修复你的构造函数:

def __init__(self,*args):

但是你必须把args传递给妈妈class否则你复制的字典将是空的(不是你想要的)。

由于您正在专门化默认工厂,因此如果 args 为空,则必须进行特殊处理:

class A(defaultdict):
  def __init__(self,*args):
    if args:
        super(A, self).__init__(*args)
    else:
        super(A, self).__init__(int)  # better than lambda : 0

或者用三元可能更简单:

class A(defaultdict):
  def __init__(self,*args):
    super(A, self).__init__(*(args or (int,)))
  • args 不为空(从 copy 调用)时,副本采用原始属性(函数和数据)。
  • args 为空时,这意味着您正在创建一个新的字典,因此它只是修复了默认的工厂参数。

另外:您可以将 (lambda :0) 替换为 (int)

编辑:一种更复杂但确保用户无法更改默认值的方法是忽略第一个参数并强制 int(如果第一个参数不是 int,可能会发出警告) :

super(A, self).__init__(*([int]+list(args[1:])))

这样行得通,但我不太喜欢忽略争论的想法。

总的来说,继承内置类型通常很棘手,应谨慎使用(请参阅另一个尝试使用 pandas 数据帧进行继承的示例:)。有时使用 defaultdict 作为参数创建 class,并且 mimics/relays 只有您计划使用的方法会导致较少的副作用。

defaultdict.__init__() 接受三个参数:

  1. self(当然),
  2. 一个可选的工厂可调用丢失的密钥,并且
  3. 一组可选的 key:values(可以是 dict(key, value) 对的序列)。

defaultdict.copy() 将创建一个新的 defaultdict 实例并将其传递给它 factory 可调用的以及它当前 key:values 集的浅表副本。

你的子类的 __init__ 只接受 self 作为参数,但最终被调用了三个。

这里的解决方法是重写 A.__init__ 以便它可以处理这两种情况:

class A(defaultdict):
    def __init__(self, *args):
        # make sure we force the factory
        args = (int,) + args[1:]
        super(A, self).__init__(*args)

我决定将小评论扩展为答案。虽然在已经给出的答案中给出了完美的分析,但我不喜欢提议的论证修改。 defaultdict 和底层字典都有一个重要的签名(参数的使用)。下面的代码不涉及参数并将它们原封不动地传递给原始实现:

def __init__(self, *args, **kwargs):
    super(A, self).__init__(*args, **kwargs)
    self.default_factory = int

还保留了 kwargs,例如A(a=1,b=2) 有效。