Reputation: 60004
I seem to be unable to instantiate a namedtuple
subclass:
from collections import namedtuple
foo = namedtuple("foo",["a","b","c"])
class Foo(foo):
def __init__(self, a, b):
super(Foo, self).__init__(a=a,b=b,c=a+b)
When I try to create an instance, I get:
>>> Foo(1,2)
TypeError: __new__() takes exactly 4 arguments (3 given)
I expected Foo(1,2,3)
.
There seems to be a workaround: using a class method instead of __init__
:
class Foo(foo):
@classmethod
def get(cls, a, b):
return cls(a=a, b=b, c=a+b)
Now Foo.get(1,2)
indeed returns foo(a=1, b=2, c=3)
.
However, this looks ugly.
Is this the only way?
Upvotes: 4
Views: 839
Reputation: 1121166
Named tuples are immutable, you need to use the __new__
method instead:
class Foo(foo):
def __new__(cls, a, b):
return super(Foo, cls).__new__(cls, a=a, b=b, c=a+b)
(Note: __new__
is implicitly made a static method, so you need to pass on the cls
argument explicitly; the method returns the newly created instance).
__init__
can't be used because that is called after the instance has already been created and so would not be able to mutate the tuple anymore.
Note that you should really add a __slots__ = ()
line to your subclass; a named tuple has no __dict__
dictionary cluttering up your memory, but your subclass will unless you add the __slots__
line:
class Foo(foo):
__slots__ = ()
def __new__(cls, a, b):
return super(Foo, cls).__new__(cls, a=a, b=b, c=a+b)
That way you get to keep the memory footprint of your named tuples low. See the __slots__
documentation:
The action of a
__slots__
declaration is limited to the class where it is defined. As a result, subclasses will have a__dict__
unless they also define__slots__
(which must only contain names of any additional slots).
Upvotes: 7