2

I'm trying to create a subclass of tuple which behaves in all aspects like a normal tuple with the exception that I initialize it with a string which is automatically split first by the constructor (I also want its __str__() to join this again, but that's not the issue here).

I thought this to be straight-forward and tried it like this:

class C(tuple):
  def __init__(self, text):
    super(C, self).__init__(text.split(':'))

  def __str__(self):
    return '[%s]' % ':'.join(self)

c = C('one:two:three')  # I expect this to be like ('one', 'two', 'three')

So I try to pass a text (a str), split that and call my superclass's constructor with the result. I expected to get a result like for tuple([ 'one', 'two', 'three' ]), i. e. a tuple of words: ('one', 'two', 'three').

But instead I get a tuple of characters, i. e. for the input 'one:two:three' I get a ('o', 'n', 'e', ':', 't', 'w', 'o', ':', 't', 'h', 'r', 'e', 'e') which is exactly the result I get when I call tuple('one:two:three').

I debugged the situation and found out that my code gets properly executed (my __init__() gets called and calls the other __init__() with the correct values). I also tried to replace the super construct by a concrete tuple.__init__(self, text.split(':')), but that didn't change anything. I also tried passing a tuple instead of the list created by split(), also no change. Actually, calling super's __init__() does not seem to have any effect. The interpreter still initializes the tuple with the string I pass originally.

Am I missing something? Why is this not working as expected? How can I create a class C which is a subclass of tuple and which I can initialize by calling C('one:two:three') to get an instance of C which is a tuple like ('one', 'two', 'three')?

Alfe
  • 56,346
  • 20
  • 107
  • 159
  • Which version are you using? I get `TypeError: object.__init__() takes no parameters` on 3.5 – Steven Summers Feb 23 '17 at 00:59
  • @StevenSummers: I'm using Python 2.7 in this project but I'm curious: Do you really get this error message about `object.__init__()` taking no arguments when you inherit `tuple` in 3.5? – Alfe Feb 23 '17 at 16:10
  • That is what I receive when running your code, same for 3.4 and 3.6 – Steven Summers Feb 24 '17 at 02:25

1 Answers1

2

Since tuples are immutable, use __new__ instead of __init__:

class C(tuple):
    def __new__(cls, text):
        return super(C, cls).__new__(cls, text.split(':'))

    def __str__(self):
        return '[%s]' % ':'.join(self)

c = C('one:two:three')
print(c)
print(list(c))
Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
  • 1
    For anyone wondering what's the difference between `__init__` and `__new__` this post really helped me http://stackoverflow.com/questions/4859129/python-and-python-c-api-new-versus-init – Ramast Feb 23 '17 at 02:12