Style guide for subclassing built-in types?

Discussion in 'Python' started by Jane Austine, Feb 23, 2005.

  1. Jane Austine

    Jane Austine Guest

    Please see the following code:
    --------------------------------
    class rev_wrap(object):
    def __init__(self,l):
    self.l=l
    def __getitem__(self,i):
    return self.l[-i-1]

    class rev_subclass(list):
    def __getitem__(self,i):
    return list.__getitem__(self,-i-1)

    if __name__=='__main__':
    l=rev_wrap([1,2,3])
    assert l[0]==3
    assert list(l)==[3,2,1]

    l=rev_subclass([1,2,3])
    assert l[0]==3
    assert list(l)==[3,2,1]

    I know there is "reversed" and "reverse" but that's not the point. The
    code fails at the last line.

    Now that UserList is deprecated(not recommended) I suppose subclassing
    built-in types are preferable than wrapping them. How do I properly
    change the behaviours of built-in types? I think I have to override
    __iter__ and next to pass the last line. Is it a good style? If so,
    what is the most recommended way of implementing them?

    Thank you in advance.

    Jane
     
    Jane Austine, Feb 23, 2005
    #1
    1. Advertising

  2. Jane Austine

    Guest

    Jane Austine wrote:
    > Please see the following code:
    > --------------------------------
    > class rev_wrap(object):
    > def __init__(self,l):
    > self.l=l
    > def __getitem__(self,i):
    > return self.l[-i-1]
    >
    > class rev_subclass(list):
    > def __getitem__(self,i):
    > return list.__getitem__(self,-i-1)
    >
    > if __name__=='__main__':
    > l=rev_wrap([1,2,3])
    > assert l[0]==3
    > assert list(l)==[3,2,1]
    >
    > l=rev_subclass([1,2,3])
    > assert l[0]==3
    > assert list(l)==[3,2,1]


    Oh... I forgot one. assert l==[3,2,1] at this point doesn't pass
    either. "print l" outputs the wrong one([1,2,3]) as well.
     
    , Feb 23, 2005
    #2
    1. Advertising

  3. Jane Austine

    Fuzzyman Guest

    Fuzzyman, Feb 23, 2005
    #3
  4. Jane Austine

    Guest

    Fuzzyman wrote:
    > I guess print is using the __repr__ (or __str__ ?) methods of lsit -
    > which you will need to override as well.
    >
    > Regards,
    >
    > Fuzzy
    > http://www.voidspace.org.uk/python/index.shtml


    Thank you but the problem is that I have to express my intention in
    duplicate places -- __iter__(along with "next"), __str__, __eq__ and so
    on.

    p.s. the reason I'm not sticking to reversed or even reverse : suppose
    the size of the list is huge.
     
    , Feb 23, 2005
    #4
  5. Jane Austine

    Nick Coghlan Guest

    wrote:
    > p.s. the reason I'm not sticking to reversed or even reverse : suppose
    > the size of the list is huge.


    Reversed is an iterator - it does NOT copy the list. In other words, reversed
    already does pretty much what you want.

    Cheers,
    Nick.

    --
    Nick Coghlan | | Brisbane, Australia
    ---------------------------------------------------------------
    http://boredomandlaziness.skystorm.net
     
    Nick Coghlan, Feb 23, 2005
    #5
  6. Jane Austine

    Fuzzyman Guest

    wrote:
    > Fuzzyman wrote:
    > > I guess print is using the __repr__ (or __str__ ?) methods of lsit

    -
    > > which you will need to override as well.
    > >
    > > Regards,
    > >
    > > Fuzzy
    > > http://www.voidspace.org.uk/python/index.shtml

    >
    > Thank you but the problem is that I have to express my intention in
    > duplicate places -- __iter__(along with "next"), __str__, __eq__ and

    so
    > on.
    >


    If you are sublassing the built in types I guess you will have to
    change all methods that have changed..

    an alternative wuld be to not subclass object and override __getattr__
    :

    class rev_wrap:
    def __init__(self,l):
    self.l=l
    def __getitem__(self,i):
    return self.l[-i-1]
    def __getattr__(self, attr):
    return getattr(self.l, attr)

    Regards,

    Fuzzy
    http://www.voidspace.org.uk/python/index.shtml

    > p.s. the reason I'm not sticking to reversed or even reverse :

    suppose
    > the size of the list is huge.
     
    Fuzzyman, Feb 23, 2005
    #6
  7. Jane Austine

    Kent Johnson Guest

    wrote:
    > p.s. the reason I'm not sticking to reversed or even reverse : suppose
    > the size of the list is huge.


    reversed() returns an iterator so list size shouldn't be an issue.

    What problem are you actually trying to solve?

    Kent

    >
     
    Kent Johnson, Feb 23, 2005
    #7
  8. Jane Austine

    Guest

    Kent Johnson wrote:
    > wrote:
    > > p.s. the reason I'm not sticking to reversed or even reverse :

    suppose
    > > the size of the list is huge.

    >
    > reversed() returns an iterator so list size shouldn't be an issue.
    >
    > What problem are you actually trying to solve?
    >
    > Kent
    >


    Oh, you are right.

    Actually, it's more complicated than simple reversion. The list order
    should be somewhat "twisted" and the list is big.

    For example,

    [1,2,3,4,5,6,7,8,9,10]

    --> [10,9,8,7,6,1,2,3,4,5]

    so __getitem__(self,i) => __getitem__(self,-i-1) if i<len(size)/2,
    otherwise __getitem__(self,i-len(size)/2)

    I'd like to have TwistedList class that takes in an original list and
    pretends as if it is twisted actually. However, I have to have
    duplicate codes here and there to make it act like a "list", say assert
    twisted_list == [10,9,...] and for each in twisted_list and etc.
     
    , Feb 23, 2005
    #8
  9. wrote:
    > Kent Johnson wrote:
    >
    >> wrote:
    >>
    >>>p.s. the reason I'm not sticking to reversed or even reverse :

    >
    > suppose
    >
    >>>the size of the list is huge.

    >>
    >>reversed() returns an iterator so list size shouldn't be an issue.
    >>
    >>What problem are you actually trying to solve?
    >>
    >>Kent
    >>

    >
    >
    > Oh, you are right.
    >
    > Actually, it's more complicated than simple reversion. The list order
    > should be somewhat "twisted" and the list is big.
    >
    > For example,
    >
    > [1,2,3,4,5,6,7,8,9,10]
    >
    > --> [10,9,8,7,6,1,2,3,4,5]
    >
    > so __getitem__(self,i) => __getitem__(self,-i-1) if i<len(size)/2,
    > otherwise __getitem__(self,i-len(size)/2)
    >
    > I'd like to have TwistedList class that takes in an original list and
    > pretends as if it is twisted actually. However, I have to have
    > duplicate codes here and there to make it act like a "list", say assert
    > twisted_list == [10,9,...] and for each in twisted_list and etc.
    >

    If you want a twisted 'view' of an existing list, then a wrapper makes most sense.

    If, however, you only need the twisted version, why not simply override
    list.__init__ (and extend, append etc... as required):

    >>> class rev_list(list):

    ... def __init__(self, iterable):
    ... list.__init__(self, iterable[::-1])
    ...
    >>> l = rev_list([1,2,3])
    >>> l

    [3, 2, 1]

    Michael
     
    Michael Spencer, Feb 23, 2005
    #9
  10. Jane Austine

    Guest

    Michael Spencer wrote:
    > wrote:
    > > Kent Johnson wrote:
    > >
    > >> wrote:
    > >>
    > >>>p.s. the reason I'm not sticking to reversed or even reverse :

    > >
    > > suppose
    > >
    > >>>the size of the list is huge.
    > >>
    > >>reversed() returns an iterator so list size shouldn't be an issue.
    > >>
    > >>What problem are you actually trying to solve?
    > >>
    > >>Kent
    > >>

    > >
    > >
    > > Oh, you are right.
    > >
    > > Actually, it's more complicated than simple reversion. The list

    order
    > > should be somewhat "twisted" and the list is big.
    > >
    > > For example,
    > >
    > > [1,2,3,4,5,6,7,8,9,10]
    > >
    > > --> [10,9,8,7,6,1,2,3,4,5]
    > >
    > > so __getitem__(self,i) => __getitem__(self,-i-1) if i<len(size)/2,
    > > otherwise __getitem__(self,i-len(size)/2)
    > >
    > > I'd like to have TwistedList class that takes in an original list

    and
    > > pretends as if it is twisted actually. However, I have to have
    > > duplicate codes here and there to make it act like a "list", say

    assert
    > > twisted_list == [10,9,...] and for each in twisted_list and etc.
    > >

    > If you want a twisted 'view' of an existing list, then a wrapper

    makes most sense.
    >
    > If, however, you only need the twisted version, why not simply

    override
    > list.__init__ (and extend, append etc... as required):
    >
    > >>> class rev_list(list):

    > ... def __init__(self, iterable):
    > ... list.__init__(self, iterable[::-1])
    > ...
    > >>> l = rev_list([1,2,3])
    > >>> l

    > [3, 2, 1]
    >
    > Michael


    Thank you but your advice doesn't fit in my case since I want to keep
    the memory usage and the initial time minimum. iterable[::-1] would
    build another list and it would take big memory and time during
    reversing if iterable were huge. (and the "iterable" wouldn't be
    garbage-collected because I want to keep a reference to it)
     
    , Feb 24, 2005
    #10
  11. Jane Austine

    Just Guest

    In article <>,
    wrote:

    > Thank you but your advice doesn't fit in my case since I want to keep
    > the memory usage and the initial time minimum. iterable[::-1] would
    > build another list and it would take big memory and time during
    > reversing if iterable were huge. (and the "iterable" wouldn't be
    > garbage-collected because I want to keep a reference to it)


    If your list contains numbers (or lists of numbers), consider using
    NumPy (Numeric) or Numarray, in which seq[::-1] will actually return a
    "view", and not a copy.

    Just
     
    Just, Feb 24, 2005
    #11
  12. Jane Austine

    Serge Orlov Guest

    wrote:
    > Thank you but your advice doesn't fit in my case since I want to keep
    > the memory usage and the initial time minimum. iterable[::-1] would
    > build another list and it would take big memory and time during
    > reversing if iterable were huge. (and the "iterable" wouldn't be
    > garbage-collected because I want to keep a reference to it)


    You need to implement __iter__ method to pass your assert statement:
    def __iter__(self):
    return reversed(self)

    With regards to style guide:

    1. write empty subclass
    class rev_subclass(list):
    pass

    2. print dir(rev_subclass) and write unit tests for every method. There
    are 41 of them :) There is also __doc__ attribute you need to override.

    3. Implement all the methods of rev_subclass to pass the tests.

    Serge.
     
    Serge Orlov, Feb 24, 2005
    #12
  13. Jane Austine

    Nick Coghlan Guest

    wrote:
    > Thank you but your advice doesn't fit in my case since I want to keep
    > the memory usage and the initial time minimum. iterable[::-1] would
    > build another list and it would take big memory and time during
    > reversing if iterable were huge. (and the "iterable" wouldn't be
    > garbage-collected because I want to keep a reference to it)


    1. Do you have benchmark results or a mathematical analysis to show that
    duplicating the list uses too much memory, or is too slow to startup?

    2. Do you have quantitative definitions for "too much" and "too slow", and
    rationale to back up those numbers?

    3. Do you have a benchmark to determine if attempting to reduce memory
    consumption and start-up time has a detrimental effect on run-time performance?

    If the answer to any of the above questions is 'no', then just do something like:

    from itertools import islice, chain
    def twisted_iter(orig):
    halfway, skip_middle = divmod(len(orig), 2)
    fwditr = islice(iter(orig), halfway + skip_middle, None)
    revitr = islice(reversed(orig), halfway, None)
    return chain(revitr, fwditr)

    Py> from itertools import islice, chain
    Py> def twisted_iter(orig):
    .... halfway, skip_middle = divmod(len(orig), 2)
    .... fwditr = islice(iter(orig), halfway + skip_middle, None)
    .... revitr = islice(reversed(orig), halfway, None)
    .... return chain(revitr, fwditr)
    ....
    Py> list(twisted_iter(range(10)))
    [4, 3, 2, 1, 0, 5, 6, 7, 8, 9]
    Py> list(twisted_iter(range(11)))
    [5, 4, 3, 2, 1, 0, 6, 7, 8, 9, 10]

    Since twisted_iter is actually a highly-optimised twisted view of the original
    list, you may want to just leave the original list alone, and create
    twisted_iter's when you want them.

    However, if you mainly use the twisted view, and only occasionally want the
    original view, then you can twist it once, store the result, discard the
    original, and twist it again to get the original back:

    Py> list(twistediter(list(twistediter(range(10)))))
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Py> list(twistediter(list(twistediter(range(11)))))
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    Those benchmarks I mentioned earlier will let you know which approach is best.

    No-optimisations-without-measurements-'ly,
    Nick.

    --
    Nick Coghlan | | Brisbane, Australia
    ---------------------------------------------------------------
    http://boredomandlaziness.skystorm.net
     
    Nick Coghlan, Feb 24, 2005
    #13
    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. Howard
    Replies:
    3
    Views:
    440
    Siemel Naran
    Jul 22, 2004
  2. Gabriel Cooper

    subclassing built-in types

    Gabriel Cooper, May 24, 2004, in forum: Python
    Replies:
    0
    Views:
    292
    Gabriel Cooper
    May 24, 2004
  3. Emiliano Molina

    Subclassing built-in types

    Emiliano Molina, Aug 25, 2004, in forum: Python
    Replies:
    3
    Views:
    309
    Emiliano Molina
    Aug 26, 2004
  4. MonkeeSage

    Subclassing built-in classes

    MonkeeSage, Oct 5, 2006, in forum: Python
    Replies:
    12
    Views:
    550
    Theerasak Photha
    Oct 11, 2006
  5. Alona
    Replies:
    2
    Views:
    653
Loading...

Share This Page