5 ms·
Python: copying a list the right way
- mjtokelly 18y agoNice, clear explanation of something that drives Python beginners crazy--especially if it's their first programming language. It would have been worth mentioning the 'copy' module. 'copy.copy' for shallow copies of any object, 'copy.deepcopy' for recursive copies.
- JabavuAdams 18y agoI'm guessing, but a function that can copy any object is unlikely to be as efficient as one that works with known types. Of course, even if true, that may not matter. See, I'm all about the weasel words today.
- cstejerean 18y agoIt would be a little faster. The first part of copy.copy looks like def copy(x): """Shallow copy operation on arbitrary Python objects. See the module's __doc__ string for more info. """ cls = type(x) copier = _copy_dispatch.get(cls) if copier: return copier(x) # more after this point but it's not relevant for lists So calling copy.copy on a list over using list() will check for the type, look up the type in a dictionary after which it will proceed as if you called list() yourself.
- JabavuAdams 18y agoMental memo. :)
- palish 18y agoI'm not sure either... A hand-written .clone() method would always beat the generic solution of course, but it seems like the tradeoff is that you spend less time coding, which is a win.
- newt0311 18y agoI think addresses and pass-by-reference drive people crazy when they start
- ggrot 18y agoAgreed. The deep copy is much much better. Consider this code: >>> a = [1, [2, 3]] >>> b = list(a) # or a[:], they are identical >>> a[1].append(4) >>> a.append(5) >>> a [1, [2, 3, 4], 5] >>> b [1, [2, 3, 4]] If you had used copy.deepcopy, b would now be [1, [2, 3]] as intended.
- diN0bot 18y agothese are good points. of course, what with classes i very rarely have deep lists anyway. in fact, copying lists is seldom an issue i run into (though i always run a few tests at the prompt to make sure my understanding is right because mutability bugs can be hard to track down later).
- anuraggoel 18y agoDO NOT replace [:] with list() next time you see it. While the article explains some concepts well, [:] is no less pythonic than list(), though the latter might be more readable for people completely new to the language. In fact, it would be helpful to become very comfortable with python slices early on, because they allow you to easily manipulate any sequence, not just lists. For example, you can reverse a string using slices in one line: reversed_string = orig_string[::-1] Staying away from slices, you're leaving that power behind.
- marcus 18y agoAnother neat trick with slices is that you can do a partial update on a list. a = range(6) a[::2] = range(10,13) -> generates a with [10,1,11,3,12,5] I saw a very elegant implementation of Eratosthenes sieve based on that trick
- d0mine 18y agohttp://www.rosettacode.org/wiki/Sieve_of_Eratosthenes#Using_numpy http://www.rosettacode.org/wiki/Sieve_of_Eratosthenes#Using_... from numpy import bool_, nonzero, ones def primes_upto(limit): is_prime = ones(limit, dtype=bool_) for n in xrange(2, int(limit**0.5) + 1): if is_prime[n]: is_prime[n*n::n] = 0 return nonzero(is_prime)[0][2:]
- diN0bot 18y agothis starts to become as cryptic as the previously mentioned 'cryptic regex.' cryptic is anything that can stay in the head 'ram' of a normal programmer. stuff that typically needs to be written down to make sense. i went through the above code with pencil and paper and it made sense. now i can look at the code and it makes sense. the person who originally wrote the code had to go through those steps too (not necessarily on paper, but loading the process into head ram). same with regex. no big deal. i know about slices, and if i didn't that's what mentors are for---oh, that symbol? search for python slices! or a python book. at least python doesn't has less than a handful nonsearchable crypticness. the comment linking to the eratosthenes sieving explanation was helpful.
- snprbob86 18y agoIs this more immediately obvious? I would have expected list to be defined as list(*elements) and called like this list(1, 2, 3) I guess that this allows me to type help(list) and figure it out, but I would have done that anyway to identify an operator. Clearly the answer is a .clone() or .copy() method...
- ewiethoff 18y ago> Clearly the answer is a .clone() or .copy() method... if you want Python to smell like Java or Ruby. :->
- latortuga 18y agohttp://docs.python.org/library/copy.html http://docs.python.org/library/copy.html Seems to disagree with you.
- ewiethoff 18y agoThat's a module in the standard library. What I'm saying is, there's no copy or clone or dup method built into Python's base object, as there is in Ruby. And Python programmers are not instructed to define a clone method for a class, as in Java. A __copy__ or __deepcopy__, okay, but not copy or deepcopy. Hence, in Python you just don't see foo.clone() or foo.dup() or foo.copy() or foo.deepcopy(). Instead, you see copy.copy(foo) or copy.deepcopy(foo). Pythonic code often looks different than Ruby or Java, looks more stand-alone function-y than instance method-y.
- bdr 18y agoBy putting it in a module, you avoid having to add the method to every single class.
- ewiethoff 18y agoHmm, no. You have to add __copy__ and/or __deepcopy__ to every class.
- jodrellblank 18y agoThis is confusing for beginners and should be avoided. That doesn't follow. Beginners need to learn list slicing to get anywhere with Python, and by the time they've got through [1], [0:10], [2:], [:5], [:-1], [0:10:2] and so on then [:] is just another use in the same pattern.
- quantumhobbit 18y agoI have to admit that slices were the hardest part of learning python for me. Mostly because it took me awhile to find as clear an explanation of their "pass by value" behavior, to borrow C terminology. Slicing incredibly powerful, though and should be given greater attention in intro materials.
- palish 18y agoDrat, the only one I don't know is [0:10:2]. I guess it's back to The Python Tutorial for me. http://docs.python.org/tutorial/ http://docs.python.org/tutorial/ Followup: The tutorial didn't seem to mention what [x:y:z] does, so I checked: >>> range(15) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] >>> range(15)[0:10] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> range(15)[0:10:2] [0, 2, 4, 6, 8] >>> range(15)[0:10:3] [0, 3, 6, 9] So the third argument is the number of elements to skip (minus one) between each item in the new sequence. Neat.
- nadim 18y agoI also found this: http://www.python.org/doc/2.3.5/whatsnew/section-slices.html http://www.python.org/doc/2.3.5/whatsnew/section-slices.html These are "extended slices" and the third argument is the step. This link also explores deletion and the __getitem__ method: One can also now pass slice objects to the __getitem__ methods of the built-in sequences: >>> range(10).__getitem__(slice(0, 5, 2)) [0, 2, 4] Or use slice objects directly in subscripts: >>> range(10)[slice(0, 5, 2)] [0, 2, 4]
- limmeau 18y agoAnd while we're at confusion and beginners: at least them = things[:] looks different from them = (list) things; which in C-like languages usually does not make a copy of things. Using slices may avoid fallacies like "I know that int(x) is something like a cast of x to int, so list(x) is like a cast of x to list, which does nothing when x is a list".
- njharman 18y agoHuh, I've almost never seen [:], would never think of it. Do people really use that? I mean it takes a lot of work to make python cryptic but I guess if you're determined anything is possible. use deepcopy or list(). I don't even use [] or {} to create empties anymore. I much prefer explicit esp since there is proliferation of container types and it's silly, inconsistant, and confusing that dicts and lists have syntactic exceptions. newlist = list() newdict = dict() newdict = defaultdict(str) etc. And to anuraggoel. not using [:] is not avoiding slices. just as not using string += "ext" (esp in loop) is not avoiding strings.
- nostrademons 18y agoI always used [:], although now I think I'll switch to copy.copy or list() after reading this thread. (Actually, now that I work for BigCo, I'll have to check what their coding standards say about this.)
- thorax 18y agoI've also used list() because I find it just generally more readable.
- ivank 18y agoCreating empties with {} takes just 58% of the time as dict() [0.25 us, 0.39 us] because there's no need to LOAD_GLOBAL and CALL_FUNCTION. 'not not variable' is similarly faster than bool(variable). (not that I've run into this often in Python.)
- njharman 18y ago> takes just 58% of the time Whoopee fucking doo, really.
- yesimahuman 18y agoI thought this was going to be a talk on performance issues. I think most python people dealing with production code would understand either.
- pkrumins 18y ago<moody> "a[:] feels a bit too much like Perl" - doesn't feel like Perl at all!