[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