Richard Jones' Log: Interesting Python problem...

Fri, 25 May 2007

This doesn't make much sense to me... anyone?

>>> class test(int):
...  def __new__(cls, v):
...   return super(test, cls).__new__(cls, v)
...
>>> test(1)
1
>>> class test(set):
...  def __new__(cls, v):
...   return super(test, cls).__new__(cls, v)
...
>>> test('123')
test(['1', '3', '2'])
>>>
>>>
>>> class test(int):
...  def __new__(cls, a, v):
...   return super(test, cls).__new__(cls, v)
...
>>> test('a', 1)
1
>>> class test(set):
...  def __new__(cls, a, v):
...   return super(test, cls).__new__(cls, v)
...
>>> test('a', '123')
Traceback (most recent call last):
  File "", line 1, in 
TypeError: test expected at most 1 arguments, got 2
>>>

p.s. new job == teh coolness. Hence no posts lately :)

Comment by Jason on Fri, 25 May 2007

Interesting. Looks like set() deals with the arguments in its __init__ function set_init, while int() does it in int_new.

Comment by Glyph Lefkowitz on Fri, 25 May 2007

It makes sense if you think about it. int is immutable, so it comes fully-formed from __new__ and its __init__ is a no-op.

Consider:

>>> s = set()
>>> s
set([])
>>> set.__init__(s, [1, 2, 3, 4, 5])
>>> s
set([1, 2, 3, 4, 5])
>>> i = 1
>>> int.__init__(i, 7)
>>> i
1

Comment by Michael on Fri, 25 May 2007

I think you forgot to enclose your arguments in square brackets, set expects a list, not 2 single arguments.

Comment by Brantley Harris on Fri, 25 May 2007

set has an __init__ function already defined that takes exactly one argument. When you call test('a', '123'), the order of operations is:

  1. call test.__new__('a', '123')
  2. call .__init__('a', '123') on the result.
It does not use for the __init__, the arguments that you sent in for the super(test, cls).__new__(cls, v), but rather what you sent into the test() class constructor.

Comment by Brantley Harris on Fri, 25 May 2007

Oh, and this code demonstrates:

>>> class test(object):
...    def __new__(cls, a, v):
...        return object.__new__(cls, v)
...        
...    def __init__(self, *args):
...        print args     
>>> test('a', '123')
('a', '123')

Comment by Calvin Spealman on Fri, 25 May 2007

As Glyph mentions, the reason behind this is known. However, it does seem like a bit of a problem. Do we need to know the internals of types to subclass them? That does not fit well in a Pythonic world, me-thinks.

Also, Michael, you are incorrect on the arguments. Set takes any iterable, and a string is a perfectly valid iterable.