What is the preferred way to concatenate sequences in Python 3?

What is the preferred way to concatenate sequences in Python 3?

Now I am doing:

import functools import operator def concatenate(sequences): return functools.reduce(operator.add, sequences) print(concatenate([['spam', 'eggs'], ['ham']])) # ['spam', 'eggs', 'ham'] 

The need to import two separate modules for this seems awkward.

An alternative could be:

 def concatenate(sequences): concatenated_sequence = [] for sequence in sequences: concatenated_sequence += sequence return concatenated_sequence 

However, this is not true because you do not know that sequences are lists.

You can do:

 import copy def concatenate(sequences): head, *tail = sequences concatenated_sequence = copy.copy(head) for sequence in sequences: concatenated_sequence += sequence return concatenated_sequence 

But this seems like a terribly mistake - a direct call to copy? (I know that head.copy() works for lists and tuples, but copy not part of the ABC sequence, so you cannot rely on it ... what if you get the passed strings?). You must copy to prevent mutation if you receive a MutableSequence message. Moreover, this solution forces you to unpack the entire set of sequences first. Try again:

 import copy def concatenate(sequences): iterable = iter(sequences) head = next(iterable) concatenated_sequence = copy.copy(head) for sequence in iterable: concatenated_sequence += sequence return concatenated_sequence 

But come on ... it's a python! So ... What is the preferred way to do this?

+7
source share
2 answers

I would use itertools.chain.from_iterable() :

 import itertools def chained(sequences): return itertools.chain.from_iterable(sequences): 

or, since you noted this with you can use the new yield from syntax (look ma, no import!):

 def chained(sequences): for seq in sequences: yield from seq 

which iterators return (use list() on them if you need to materialize the complete list). In most cases, you do not need a sequence of concatenation sequences; in fact, you just want to iterate over them to process and / or search for something instead.

Note that for strings you should use str.join() instead of any of the methods described in my answer or your question:

 concatenated = ''.join(sequence_of_strings) 

Combined to quickly and correctly handle sequences, I would use:

 def chained(sequences): for seq in sequences: yield from seq def concatenate(sequences): sequences = iter(sequences) first = next(sequences) if hasattr(first, 'join'): return first + ''.join(sequences) return first + type(first)(chained(sequences)) 

This works for tuples, lists, and strings:

 >>> concatenate(['abcd', 'efgh', 'ijkl']) 'abcdefghijkl' >>> concatenate([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> concatenate([(1, 2, 3), (4, 5, 6), (7, 8, 9)]) (1, 2, 3, 4, 5, 6, 7, 8, 9) 

and uses the faster ''.join() for sequence of strings.

+5
source

Use itertools.chain.from_iterable .

 import itertools def concatenate(sequences): return list(itertools.chain.from_iterable(sequences)) 

A list call is only necessary if you need an actual new list, so skip it if you just repeat this new sequence once.

+1
source

All Articles