[Python-ideas] add a list.swap() method

Steven D'Aprano steve at pearwood.info
Wed Jun 24 02:19:45 CEST 2009


On Wed, 24 Jun 2009 06:23:31 am Kristján Valur Jónsson wrote:
> Hello there.
> Please see my feature request: http://bugs.python.org/issue6326
>
> The idea is to speed up the swapping of list elemenst so that
> a.swap(b)
> is equivalent to
> a[:], b[:] = b[:], a[:]
> but without all the overhead of creating slices, assigning them and
> so forth. In particular, it can help reduce the cost of creating 
> instances of list subclasses, from raw lists:
>
> class mylist(list):
>     def first(self):
>                  return self[0]
>
> m = mylist(source_list)

Is that overhead really significant enough that it needs a special 
operation to avoid it? In the tracker, you claim a significant 
performance gain, but I'd like to hear more details.

> This certainly creates m, but does so by copying source_list.  

Only the pointers, not the objects pointed to. But I'm sure you already 
know this :)


> If all 
> we want to do is turn source_list into a mylist instance, it is much
> quicker to: m = mylist()
> m.swap(source_list)

This has a side-effect of turning source_list into an empty mylist. I 
don't like that one bit.


> See the above issue for initial comments, especially concerns on how
> this can bypass all kind of insertion semantics of list subclasses.

Can you address those concerns?

One problem with swap() being a list method is that it allows people to 
arbitrarily swap the contents of lists around, breaking subclass 
invariants. I have an alternative suggestion: put the swap into 
list.__new__, so it can only be done at list initialisation time.

list.__new__(cls, source) currently returns an empty list of type cls, 
which is then initialised by list.__init__(self, source), presumably by 
copying source. Perhaps list.__new__ could take an additional argument 
that says "swap the contents of the new list to use source without 
copying".

That means, instead of writing:

a = mylist()
a.swap([1, 2, 3])

you would put the fast initialisation logic into the class:

class mylist(list):
    def __new__(cls, source):
        return list.__new__(cls, source, swap=True)
    def __init__(self, source):
        pass  # avoid calling list.__init__

Existing code should work unchanged if the default for swap is False.




-- 
Steven D'Aprano



More information about the Python-ideas mailing list