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

When calling copy, defaultdict invokes a constructor with arguments, to pass the default_factory function and the data.

Your constructor doesn't take any arguments, so it's only able to build empty dicts with fixed factory.

Fix your constructor like this:

def __init__(self,*args):

But you have to pass args to the mother class or your copied dictionary will be empty (not that you want).

Since you're specializing the default factory, you have to make a special case if args are empty:

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

Or maybe simpler with a ternary:

class A(defaultdict):
  def __init__(self,*args):
    super(A, self).__init__(*(args or (int,)))
  • When args is not empty (called from copy), then the copy takes properties of the original (function & data).
  • When args is empty, it means that you're creating a new dict, so it just fixes the default factory argument.

Aside: you could replace (lambda :0) by (int).

EDIT: a more complicated way but which makes sure that user cannot change default would be to ignore first argument and force int (maybe with a warning if first argument isn't int):

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

That would work, but I don't like the idea of ignoring an argument much.

As a conclusion, inheriting for built-in types in general is tricky and should be used with caution (see another example trying to do that with a pandas dataframe: building a class from an existing one). Sometimes creating a class with a defaultdict as argument, and which mimics/relays only the methods you're planning to use will lead to less side-effects.


I decided to expand what was a small comment to an answer. While a perfect analysis was given in answers already given, I dislike the proposed argument modification. Both defaultdict and the underlying dict have a non-trivial signature (usage of arguments). The code below does not touch the arguments and passes them unchanged to the original implementation:

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

Also the kwargs are preserved, e.g. A(a=1,b=2) works.


defaultdict.__init__() takes three arguments:

  1. self (of course),
  2. An optional factory callable for missing keys, and
  3. An optional set of key:values (which and be either a dict or a sequence of (key, value) pairs).

defaultdict.copy() will create a new defaultdict instance and pass it it's factory callable AND a shallow copy of it's current key:values set.

Your subclass's __init__ only takes self as argument, but ends up being called with three.

The fix here is to rewrite A.__init__ so it can handle both cases:

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