5

I am trying to copy the nested list a, but do not know how to do it without using the copy.deepcopy function.

a = [[1, 2], [3, 4]]

I used:

b = a[:]

and

b = a[:][:]

But they all turn out to be shallow copy.

Any hints?

13
  • 2
    Your second try does the same as the first. Commented Oct 21, 2011 at 5:03
  • 1
    @Serdalis That's a mostly-shallow copy. Imagine the list elements are something other than ints. b=a[:] does indeed make a new list, but its elements are references to the original items in a. For example: a=[[]];b=a[:];b[0].append(1);print a yields [[1]] because the first element in b is the same object as the first element in a. A deep copy would result in two different objects - you can try it yourself. Commented Oct 21, 2011 at 5:17
  • 5
    recursion can solve your problem Commented Oct 21, 2011 at 5:21
  • 1
    Because you're slicing a sequence again and again and again and... Commented Oct 21, 2011 at 5:23
  • 2
    Essentially, taking a deep copy (with full generality of the contents) is a very complicated operation. Even copy.deepcopy doens't get it right out of the box in many cases (you can provide your own copy and deepcopy methods to work around that). If you're implementing this yourself, you can only get something simple by making heavily limiting assumptions i.e. "I'm only copying lists that contain only builtin types". Commented Oct 21, 2011 at 6:02

5 Answers 5

9

My entry to simulate copy.deepcopy:

def deepcopy(obj):
    if isinstance(obj, dict):
        return {deepcopy(key): deepcopy(value) for key, value in obj.items()}
    if hasattr(obj, '__iter__'):
        return type(obj)(deepcopy(item) for item in obj)
    return obj

The strategy: iterate across each element of the passed-in object, recursively descending into elements that are also iterable and making new objects of their same type.

I make no claim whatsoever that this is comprehensive or without fault [1] (don't pass in an object that references itself!) but should get you started.

[1] Truly! The point here is to demonstrate, not cover every possible eventuality. The source to copy.deepcopy is 50 lines long and it doesn't handle everything.

Sign up to request clarification or add additional context in comments.

5 Comments

would go on a infinite loop on Python 3 strings (they have the __iter__ method). Also,this solution has poor performance and assumes the class always can handle its objects in the constructor.
@xiaohan2012 - happy to help! @Raymond - thanks! My favorite thing in the world about Python is that it ran the first time. @JBernardo - as I mentioned, that wasn't meant to be a comprehensive solution. I just wanted to give him a base to work off of. It has plenty of problems, like not handling the case where type(obj).__init__ can't make copies of obj. But in exchange for being only 6 lines which demonstrate the point, I can live with that. :-)
type(obj)(obj) works for approximately zero class instances, limiting its scope to built-in types. And it also doesn't copy any of the immutable types, (str(s) seems to just return s directly, which is sensible as strings are immutable). But since the only types that are both mutable and builtin are iterable, you may as well just return obj, as return type(obj)(obj) claims to be copying obj but in fact it will almost never do so.
@Ben: I write classes a lot of classes that can copy themselves. It's cool if you don't, but this would actually work on my codebase. But again: this was for demonstration purposes. I was explicit about that.
@Kirk That's far from normal. Look through all the classes in the standard library. I know I don't speak for all Python programmers, but I don't think I've ever seen a single class with constructor arguments that was mutilated so that all the later ones were optional and the first one could either be whatever it normally is, or an instance of this class (which triggers a copy). The type inspection required to do that is often claimed to be "unPythonic" due to its interference with duck typing. I just don't think that line is a good one to show new Python programmers.
7

You can use a LC if there's but a single level.

b = [x[:] for x in a]

2 Comments

So how about multiple nested list?
Switch to a recursive solution. Or suck it up and use deepcopy().
2

This is a complete cheat - but will work for lists of "primitives" - lists, dicts, strings, numbers:

def cheat_copy(nested_content):
  return eval(repr(nested_content))

There are strong security implications to consider for this - and it will not be particularly fast. Using json.dumps and loads will be more secure.

2 Comments

This is obviously dangerous if you can't trust the nested_content. It is also strangely appealing for being very readable and effective.
Eval is horribly dangerous in most uses ever. You could instead go for json dumps/json read for a safer version of it.
1

I found a way to do it using recursion.

def deep_copy(nested_content):
    if not isinstance(nested_content,list):
        return nested_content
    else:
        holder = []
        for sub_content in nested_content:
            holder.append(deep_copy(sub_content))
        return holder

2 Comments

The difference between your answer and mine is that your doesn't handle containers other than lists. If you look for nested_content.__iter__, you can use type(nested_content) to create a new object of the same type. Also, you're not actually making a copy of the contents of your lists - you're building new lists referencing the original items.
Yes, I see that. Your strategy is a little sophisticated for me, and of course, better and more generalized than mine.
-1

For the recursive version, you have to keep track of a secondary list and return each time.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.