There is the code.
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()
There is the error:
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
I don't know how to inherit the copy method and also don't know why I give 3 argument.
The Python defaultdict type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, then defaultdict will automatically create the key and generate a default value for it. This makes defaultdict a valuable option for handling missing keys in dictionaries.
A defaultdict can be created by giving its declaration an argument that can have three values; list, set or int. According to the specified data type, the dictionary is created and when any key, that does not exist in the defaultdict is added or accessed, it is assigned a default value as opposed to giving a KeyError .
get method and the experiment shows that defaultdict more that two times faster than dict. get method.
A defaultdict works exactly like a normal dict, but it is initialized with a function (“default factory”) that takes no arguments and provides the default value for a nonexistent key. A defaultdict will never raise a KeyError. Any key that does not exist gets the value returned by the default factory.
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,)))
args
is not empty (called from copy
), then the copy takes properties of the original (function & data).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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With