Possible Duplicate:
Subclassing Python tuple with multiple __init__ arguments
I want to define a class which inherits from tuple
, and I want to be able to instantiate it using a syntax not supported by tuple
. For a simple example, let's say I want to define a class MyTuple
which inherits from tuple
, and which I can instantiate by passing two values, x
and y
, to create the (my) tuple (x, y)
. I've tried the following code:
class MyTuple(tuple):
def __init__(self, x, y):
print("debug message")
super().__init__((x, y))
But when I tried, for example, MyTuple(2, 3)
I got an error: TypeError: tuple() takes at most 1 argument (2 given)
. It seems my __init__
function was not even called (based on the error I got and on the fact my "debug message" was not printed).
So what's the right way to do this?
I'm using Python 3.2.
Python's namedtuple was created to improve code readability by providing a way to access values using descriptive field names instead of integer indices, which most of the time don't provide any context on what the values are. This feature also makes the code cleaner and more maintainable.
A subclass “inherits” all the attributes (methods, etc) of the parent class. This means that a subclass will have everything that its “parents” have. You can then change (“override”) some or all of the attributes to change the behavior.
A named tuple is exactly like a normal tuple, except that the fields can also be accessed by . fieldname. Named tuples are still immutable, you can still index elements with integers, and you can iterate over the elements. With a named tuple, you can say record.
class MyTuple(tuple):
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
x = MyTuple(2,3)
print(x)
# (2, 3)
One of the difficulties of using super
is that you do not control which classes's method of the same name is going to be called next. So all the classes' methods have to share the same call signature -- at least the same number of items. Since you are changing the number of arguments sent to __new__
, you can not use super
.
Or as Lattyware suggests, you could define a namedtuple,
import collections
MyTuple = collections.namedtuple('MyTuple', 'x y')
p = MyTuple(2,3)
print(p)
# MyTuple(x=2, y=3)
print(p.x)
# 2
another approach would be to encapsulate a tuple rather than inheriting from it:
>>> class MyTuple(object):
count = lambda self, *args: self._tuple.count(*args)
index = lambda self, *args: self._tuple.index(*args)
__repr__ = lambda self: self._tuple.__repr__()
# wrap other methods you need, or define them yourself,
# or simply forward all unknown method lookups to _tuple
def __init__(self, x, y):
self._tuple = x,y
>>> x = MyTuple(2,3)
>>> x
(2, 3)
>>> x.index(3)
1
How practical this is, depends on how many capabilities and modifications you need, and wheter you need to have isinstance(MyTuple(2, 3), tuple)
.
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