
Python: copying a list the right way - henryprecheur
http://henry.precheur.org/2009/2/11/Python%3A_copying_a_list_the_right_way.html
======
anuraggoel
DO 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.

~~~
palish
Programming shouldn't contain 'neat tricks'. (Incidentally, one of Python's
guiding principles is "explicit is better than implicit":
<http://www.python.org/dev/peps/pep-0020/>)

Every single instance of "reversed_string = orig_string[::-1]" should be
preceded with a comment saying "reverse the string", because it's not
immediately obvious what [::-1] does. You can _learn_ what it does, but it's
not _obvious_ , the same way a cryptic regex isn't obvious. So why not write
reversed_string = orig_string.reverse()? (Or reverse( orig_string ) if that's
more Pythonic.)

~~~
dood
But [:] and [::-1] are not neat tricks, they're basic features of the
language, and are immediately understandable once you've learnt about slices.
Practically the opposite of a cryptic regex.

The purpose of comments should not be teaching the reader the language, that
just makes the code noisy.

~~~
jpd
But regular expressions are not neat tricks, they're basic features of Perl,
and are immediately understandable once you've learned about them.

~~~
dood
Are you suggesting that a cryptic regex is as easy to read as [:] or [::-1]?
If you know python, that syntax is clear at a glance, dense regular
expressions are not comparable.

Basic language features don't need comments, difficult to read lines do.

~~~
thwarted
And that depends on if regular expressions are a basic language feature. The
proper way to "explain" or "comment" a complex regular expression is not to
include a single line comment near it, but to use the /x modifier that allows
you to "extend your pattern’s legibility by permitting whitespace and
comments". That is, of course, assuming you're using a language where regular
expressions are first class types and has syntax to support regular
expressions as literals (if you have to include your regular expression in a
quoted string, your language doesn't), and uses PCRE.

------
njharman
Huh, 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.

~~~
ivank
Creating 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
> takes just 58% of the time

Whoopee fucking doo, _really_.

------
mjtokelly
Nice, 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
I'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
It 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
Mental memo. :)

------
yesimahuman
I thought this was going to be a talk on performance issues. I think most
python people dealing with production code would understand either.

------
jodrellblank
_This 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.

~~~
palish
Drat, 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/>

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
I also found this: <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]_

------
snprbob86
Is 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
> Clearly the answer is a .clone() or .copy() method...

if you want Python to smell like Java or Ruby. :->

~~~
latortuga
<http://docs.python.org/library/copy.html>

Seems to disagree with you.

~~~
bdr
By putting it in a module, you avoid having to add the method to every single
class.

~~~
ewiethoff
Hmm, no. You have to add __copy__ and/or __deepcopy__ to every class.

~~~
astrec
Only to override the default implementation.

------
pkrumins
<moody> "a[:] feels a bit too much like Perl" - doesn't feel like Perl at all!

