RE: promoting [] by superclass?

Discussion in 'Python' started by Robert Brewer, Jun 8, 2004.

  1. Jim Newton wrote:
    > this is interesting. I though that if you passed
    > an argument to your class factory function, it would call
    > the __init__ with that argument.
    >
    > class x1(list):
    > def __init__(y):
    > pass
    >
    > class x2(list):
    > pass
    >
    > what does x1([]) do and what does x2([]) do?


    x1([]) first calls x1.__new__, but since you're not overriding that, it
    moves up the class heirarchy to the next superclass, which is 'list',
    and calls list.__new__(cls, sequence). Notice that there are two
    arguments to this method. That method is written in such a way that the
    result is of type 'cls' (in your example, cls == x1). So when this
    method returns, we've got an object of type 'x1'.

    Then x1.__init__ gets called, passing it the new object and the
    'sequence' arg. However, you've overridden list.__init__; moreover, it
    doesn't have the same number of arguments as __new__ did, so you get a
    TypeError saying that __init__ takes a different number of arguments. If
    you want to override __init__, it should be written:

    >>> class x1(list):

    .... def __init__(self, sequence):
    .... pass
    ....
    >>> x1([])

    []
    >>> type(x1([]))

    <class '__main__.x1'>

    Now, that only works if you wish to keep the same signature (arguments).
    If you want to provide another argument to the class instantiation, you
    need to override both __new__ and __init__ so that their argument lists
    "match":

    >>> class x1(list):

    .... def __new__(cls, sequence=[], maxlen=5):
    .... return list.__new__(cls, sequence)
    .... def __init__(self, sequence=[], maxlen=5):
    .... self.maxlen = maxlen
    ....
    >>> x1([])

    []
    >>> type(x1([]))

    <class '__main__.x1'>
    >>> x1([], 7)

    []
    >>> x1([], 7).maxlen

    7

    x2 doesn't override anything, so the default list.* methods get called
    for all operations. Remember, though, that list.__new__ produces an
    object whose type is the subclass automatically, without overriding;
    therefore:

    >>> class x2(list): pass

    ....
    >>> type(x2([]))

    <class '__main__.x2'>


    Hope that helps!

    Robert Brewer
    MIS
    Amor Ministries
    Robert Brewer, Jun 8, 2004
    #1
    1. Advertising

  2. Robert Brewer

    Jim Newton Guest

    i see, that does indeed help a lot. So from what you are saying,
    it is impossible to have the class name work as both a factory
    function and a casting function. I must decide whether i want
    Pair( [] ) to build a linked list of its arguments, or simply
    to do the class promotion, but it cannot be smart enough to
    do both.

    So the next best thing would be to have a cast funcion which
    calls list().

    So it would be great if Pair(1,2,3,4,5) would
    return [1, [2, [3, [5, nil]]]] with all the bracketed
    lists promoted to class Pair. Can that be done
    with the __init__ function? currently i'm doing that
    with a seperate function which takes any sequence as its argument.

    Basically i'd like the Pair.__init__ in the case of Pair(1,2,3,4,5),
    to set self[0]=1, and self[1] = Pair(2,3,4,5) in some iterative
    way.


    def seq2pair(seq):
    new = Pair()
    for index in xrange( len(seq), 0, -1):
    new = new.cons(seq[index - 1])
    return new

    class Pair(list):

    ...

    # x = (cons x list)
    # ==> x = list.cons(x)
    def cons(self, car):
    new = Pair()
    new.append(car)
    new.append(self)
    return new



    Robert Brewer wrote:
    > Jim Newton wrote:
    >
    >>this is interesting. I though that if you passed
    >>an argument to your class factory function, it would call
    >>the __init__ with that argument.
    >>
    >>class x1(list):
    >> def __init__(y):
    >> pass
    >>
    >>class x2(list):
    >> pass
    >>
    >>what does x1([]) do and what does x2([]) do?

    >
    >
    > x1([]) first calls x1.__new__, but since you're not overriding that, it
    > moves up the class heirarchy to the next superclass, which is 'list',
    > and calls list.__new__(cls, sequence). Notice that there are two
    > arguments to this method. That method is written in such a way that the
    > result is of type 'cls' (in your example, cls == x1). So when this
    > method returns, we've got an object of type 'x1'.
    >
    > Then x1.__init__ gets called, passing it the new object and the
    > 'sequence' arg. However, you've overridden list.__init__; moreover, it
    > doesn't have the same number of arguments as __new__ did, so you get a
    > TypeError saying that __init__ takes a different number of arguments. If
    > you want to override __init__, it should be written:
    >
    >
    >>>>class x1(list):

    >
    > ... def __init__(self, sequence):
    > ... pass
    > ...
    >
    >>>>x1([])

    >
    > []
    >
    >>>>type(x1([]))

    >
    > <class '__main__.x1'>
    >
    > Now, that only works if you wish to keep the same signature (arguments).
    > If you want to provide another argument to the class instantiation, you
    > need to override both __new__ and __init__ so that their argument lists
    > "match":
    >
    >
    >>>>class x1(list):

    >
    > ... def __new__(cls, sequence=[], maxlen=5):
    > ... return list.__new__(cls, sequence)
    > ... def __init__(self, sequence=[], maxlen=5):
    > ... self.maxlen = maxlen
    > ...
    >
    >>>>x1([])

    >
    > []
    >
    >>>>type(x1([]))

    >
    > <class '__main__.x1'>
    >
    >>>>x1([], 7)

    >
    > []
    >
    >>>>x1([], 7).maxlen

    >
    > 7
    >
    > x2 doesn't override anything, so the default list.* methods get called
    > for all operations. Remember, though, that list.__new__ produces an
    > object whose type is the subclass automatically, without overriding;
    > therefore:
    >
    >
    >>>>class x2(list): pass

    >
    > ...
    >
    >>>>type(x2([]))

    >
    > <class '__main__.x2'>
    >
    >
    > Hope that helps!
    >
    > Robert Brewer
    > MIS
    > Amor Ministries
    >
    >
    Jim Newton, Jun 8, 2004
    #2
    1. Advertising

  3. Robert Brewer

    Terry Reedy Guest

    "Jim Newton" <> wrote in message
    news:...
    > So it would be great if Pair(1,2,3,4,5) would
    > return [1, [2, [3, [5, nil]]]] with all the bracketed
    > lists promoted to class Pair. Can that be done
    > with the __init__ function? currently i'm doing that
    > with a seperate function which takes any sequence as its argument.

    .....separate...
    > Basically i'd like the Pair.__init__ in the case of Pair(1,2,3,4,5),
    > to set self[0]=1, and self[1] = Pair(2,3,4,5) in some iterative
    > way.


    If you use Pair.__init__, I believe the buildup has to be top-down
    recursive. Botton-up iteration would require a factory function. See
    below.

    > Robert Brewer wrote:
    > > TypeError saying that __init__ takes a different number of arguments.

    If
    > > you want to override __init__, it should be written:
    > >
    > >>>>class x1(list):

    > >
    > > ... def __init__(self, sequence):
    > > ... pass


    In order for Pair() to work like list(), sequence must be optional. I
    default it to None since that works. While default [] works in this case,
    it is not needed and is often a bug. First a small test to verify what
    init gets:

    >>> class Pair(list):

    .... def __init__(self, seq=None):
    .... print self, seq
    ....
    >>> l = Pair([1,2,3])

    [] [1, 2, 3]

    Since list (and Pair) are mutable, new creates an empty object, which
    __init__ must fill in. The parameters of __init__ are the empty object,
    conventionally called 'self', and the sequence to be used to fill it in.

    Now for real:

    class Pair(list):
    def __init__(self, seq=None):
    if seq:
    self.append(seq[0])
    self.append(Pair(seq[1:]))

    p = Pair([1,2,3])
    >>>print p

    [1, [2, [3, []]]]
    >>> while p:

    .... print type(p)
    .... p = p[1]
    ....
    <class '__main__.Pair'>
    <class '__main__.Pair'>
    <class '__main__.Pair'>
    >>> p, type(p)

    ([], <class '__main__.Pair'>)
    >>> p2=Pair([])
    >>> p == p2

    1

    Is ths what you wanted?
    Alternatively, if Pair is to be initialized with pairs, as might seem
    sensible:

    class Pair2(list):
    def __init__(self, pair=None):
    if pair:
    if len(pair) == 2:
    self.append(pair[0])
    self.append(pair[1])
    else: raise ValueError('nonempty Pair must be initialized with
    pair')

    then build up with iteration:

    def makePair(seq): # this could be a Pair staticmethod
    p = Pair2()
    while seq:
    p = Pair2((seq.pop(),p))
    return p

    >>> p2 = makePair([1,2,3])
    >>> p2

    [1, [2, [3, []]]]
    >>> while p2: print type(p2); p2 = p2[1]

    ....
    <class '__main__.Pair2'>
    <class '__main__.Pair2'>
    <class '__main__.Pair2'>

    Terry J. Reedy
    Terry Reedy, Jun 9, 2004
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Roedy Green
    Replies:
    4
    Views:
    368
    Roedy Green
    Jul 10, 2005
  2. Wladimir Borsov
    Replies:
    1
    Views:
    486
    Kimberly Lauren
    Aug 10, 2004
  3. Replies:
    3
    Views:
    376
    Guillaume
    Nov 21, 2005
  4. Evan Klitzke
    Replies:
    0
    Views:
    348
    Evan Klitzke
    Aug 2, 2007
  5. bart van deenen
    Replies:
    6
    Views:
    737
    bart van deenen
    Mar 3, 2009
Loading...

Share This Page