Removing None objects from a sequence

Terry Reedy tjreedy at udel.edu
Sat Dec 13 01:02:24 CET 2008


Tim Chase wrote:
>> If you want to literally remove None objects from a list....(or 
>> mutable sequence)
>>
>> def deNone(alist):
>>    n=len(alist)
>>    i=j=0
>>    while i < n:
>>      if alist[i] is not None:
>>        alist[j] = alist[i]
>>        j += 1
>>      i += 1
>>    alist[j:i] = []
>>
>> blist=[None,1,None,2,None,3,None,None,4,None]
>> deNone(blist)
>> print(blist)
>>
>> # prints [1, 2, 3, 4]
> 
> ...wouldn't a cleaner way of doing this just be
> 
>   >>> blist=[None,1,None,2,None,3,None,None,4,None]

No, making a filtered copy that is then copied back before being deleted 
is algorithmically much messier.  My code does the minimum work 
necessary and is algorithmically cleaner.

>   >>> alist = blist
>   >>> blist[:] = [x for x in blist if x is not None]
>   >>> blist
>   [1, 2, 3, 4]
>   >>> alist
>   [1, 2, 3, 4]
> 
> By using the slice assignment, it leaves the blist referring to the same 
> list-object (as shown by the "alist" bit), and modifying it in place.  
> This reads a lot more cleanly in my estimation.
> 
> If the data-set is large, in 2.5+, you can just use a generator:
> 
>   blist[:] = (x for x in blist if x is not None)

Given that this works, that the setup for slice assignment does not mess 
up the co-routine iteration over the same loop, this is pretty good.  It 
amounts to deNone with the i-j loop separated into an i loop and a j 
loop in source and consumer co-routines.  One could argue that that is 
even better, even if written out as

def deNone2(alist):
   src = (item for item in alist if item is not None)
   j=0
   try:
     while True:
       alist[j] = next(src)
       j += 1
   except StopIteration:
     pass
   alist[j:] = []

tjr




More information about the Python-list mailing list