[Tutor] reference instead of copy

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Sat Oct 18 04:04:13 EDT 2003



On Fri, 17 Oct 2003, Tim Johnson wrote:

> I have been using the following function (produced by help from this
> list (thanks!)) which returns a subset of a list.:

> def extract (oldlist, spacing,ndx=-1):
> 	if ndx > -1:
> 		return [oldlist[i][ndx] for i in range (len(oldlist)) if not i%spacing]
> 	else:
> 		return [oldlist[i] for i in range (len(oldlist)) if not i%spacing]


Hi Tim,


Do you mind if we take a quick sidetrip?  The complexity of the list
comprehension is slightly high --- let me see what this looks like with
explicit loops:

###
def extract (oldlist, spacing,ndx=-1):
    if ndx > -1:
        results = []
        for i in range(len(oldlist)):
            if not i % spacing:
                results.append(oldlist[i][ndx])
    else:
        results = []
        for i in range(len(oldlist)):
            if not i % spacing:
                results.append(oldlist[i])
    return results
###



Hmm... There's some common stuff here.  Let me rework it a little more.

###
def extract (oldlist, spacing,ndx=-1):
    results = []
    for i in range(len(oldlist)):
        if i % spacing == 0:
            if ndx > -1:
                results.append(oldlist[i][ndx])
            else:
                results.append(oldlist[i])
    return results
###


Does extract() really have to deal with grabbing specific subindices using
that 'ndx' value?  That's something that I think can be done outside of
extract().  If it's ok, we can yank it out violently:

###
def extract (oldlist, spacing):
    results = []
    for i in range(len(oldlist)):
        if i % spacing == 0:
            results.append(oldlist[i])
    return results
###


Oh!  There's one more simplification we can make if we know a little more
about the range function() --- range can actually take in a "skip" third
argument.  For example:

###
>>> range(0, 10, 3)
[0, 3, 6, 9]
###


If we take advantage of this feature, then that lets us cut out the
remainder check:

###
def extract (oldlist, spacing):
    results = []
    for i in range(0, len(oldlist), spacing):
        results.append(oldlist[i])
    return results
###


We can transform this back to its list-comprehension equivalent:

###
def extract(oldlist, spacing):
    return [oldlist[i] for i in range(0, len(oldlist), spacing)]
###


> # But let's suppose I wish to operate on this subset and have it
> # reflected in the original list. I can't do this with the above
> # configuration.


Very true.  The reason for this is because of the use of the list
comprehension: list comprehnsions generate fresh new lists.  For example:

###
>>> def makeListCopy(L):
...     return [x for x in L]
...
>>> pi = [3, 1, 3, 1, 5, 9, 2, 6]
>>> pi_copy = makeListCopy(pi)
>>> pi
[3, 1, 3, 1, 5, 9, 2, 6]
>>> pi_copy
[3, 1, 3, 1, 5, 9, 2, 6]
###


Although they look the same now, they're only clones at birth.

###
>>> del pi[2:]
>>> pi_copy[2] = 4
>>>
>>> pi
[3, 1]
>>>
>>> pi_copy
[3, 1, 4, 1, 5, 9, 2, 6]
###



> # What I need for this is and 'extract' function which retains
> # the original reference so that when I code:
> t[1] = 'three'
> # list 'test looks like this:
> [1, 2, 'three', 4, 5, 6, 7, 8, 9, 10]

Trying to do this with list comprehensions probably won't work: list
comprehensions are spiritually designed NOT to modify the original list.
*grin*

Instead, you may want do direct manipulations --- like indicing and del
--- instead, and you should have better results.  In your original code,
though, you did something like:

###
>>> test = [1,2,3,4,5,6,7,8,9,10]
>>> t = mylib.extract(test,2)
>>> print t
###

How about reassigning to 'test'?



Good luck!




More information about the Tutor mailing list