61 votes

Python sum, pourquoi pas les chaînes?

Python dispose d'une fonction intégrée sum, ce qui est équivalent à:

def sum2(iterable, start=0):
    return start + reduce(operator.add, iterable)

pour tous les types de paramètres, à l'exception des chaînes de caractères. Il travaille pour des nombres et des listes, par exemple:

 sum([1,2,3], 0) = sum2([1,2,3],0) = 6    #Note: 0 is the default value for start, but I include it for clarity
 sum({888:1}, 0) = sum2({888:1},0) = 888

Pourquoi les chaînes spécialement de côté?

 sum( ['foo','bar'], '') # TypeError: sum() can't sum strings [use ''.join(seq) instead]
 sum2(['foo','bar'], '') = 'foobar'

Je me rappelle des discussions dans la liste Python pour la raison, donc, une explication ou un lien vers un thread à l'expliquer, ce serait bien.

Edit: je suis conscient que la norme est de faire "".join. Ma question est pourquoi l'option de l'utilisation de la somme pour les chaînes a été interdit, et pas de bannissement était là pour, par exemple, des listes.

Edit 2: Bien que je crois que ce n'est pas nécessaire étant donné toutes les bonnes réponses que j'ai eu, la question est: Pourquoi la somme de travail sur un objet iterable contenant des nombres ou un objet iterable contenant des listes, mais pas un objet iterable contenant des chaînes de caractères?

49voto

rbp Points 8956

Python essaie de vous décourager de "réunir" les chaînes. Vous êtes censé se joindre à eux:

"".join(list_of_strings)

C'est beaucoup plus rapide, et utilise beaucoup moins de mémoire.

Un rapide benchmark:

$ python -m timeit -s 'import operator; strings = ["a"]*10000' 'r = reduce(operator.add, strings)'
100 loops, best of 3: 8.46 msec per loop
$ python -m timeit -s 'import operator; strings = ["a"]*10000' 'r = "".join(strings)'
1000 loops, best of 3: 296 usec per loop

Edit (pour répondre à l'OP de modification): pourquoi les chaînes étaient apparemment "montrés du doigt", je crois que c'est simplement une question d'optimisation pour un cas commun, ainsi que de l'application des bonnes pratiques: vous pouvez vous joindre à cordes beaucoup plus rapide ".rejoindre, de façon explicite interdisant les cordes sur sum attirera les débutants.

BTW, cette restriction a été mise en place "pour toujours", c'est à dire, depuis l' sum a été ajouté comme une fonction intégrée (rév. 32347)

27voto

u0b34a0f6ae Points 14874

Vous pouvez en fait utiliser sum(..) pour concaténer des chaînes, si vous utilisez l'objet de départ approprié! Bien sûr, si vous allez aussi loin, vous en avez déjà suffisamment compris pour utiliser "".join(..) toute façon.

 >>> class ZeroObject(object):
...  def __add__(self, other):
...   return other
...
>>> sum(["hi", "there"], ZeroObject())
'hithere'
 

17voto

HS. Points 5414

Voici la source: http://svn.python.org/view/python/trunk/Python/bltinmodule.c?revision=81029&view=markup

Dans la fonction builtin_sum, nous avons ce morceau de code:

      /* reject string values for 'start' parameter */
        if (PyObject_TypeCheck(result, &PyBaseString_Type)) {
            PyErr_SetString(PyExc_TypeError,
                "sum() can't sum strings [use ''.join(seq) instead]");
            Py_DECREF(iter);
            return NULL;
        }
        Py_INCREF(result);
    }
 

Alors ... c'est ta réponse.

C'est explicitement vérifié dans le code et rejeté.

14voto

unutbu Points 222216

De la docs :

Le moyen le plus rapide et le plus pratique pour concaténer une séquence de chaînes consiste à appeler '' .join (sequence).

En faisant en sorte que sum refuse d'opérer des chaînes, Python vous a encouragé à utiliser la bonne méthode.

11voto

dan04 Points 33306

Réponse courte: l'Efficacité.

Réponse longue: L' sum fonction de doit créer un objet pour chaque somme partielle.

Supposons que la quantité de temps nécessaire pour la création d'un objet est directement proportionnelle à la taille de ses données. Soit N indiquer le nombre d'éléments dans la séquence de la somme.

doubles sont toujours de la même taille, ce qui rend sums'exécute en temps O(1)×N = O(N).

int (anciennement long) est arbitraire de longueur. Soit M indiquer la valeur absolue du plus grand élément de la séquence. Ensuite, sum's des cas les pires temps d'exécution est lg(M) + lg(2M) + lg(3M) + ... + lg(NM) = N×lg(M) + lg(N!) = O(N log N).

Pour str (où M = la longueur de la chaîne la plus longue), le pire temps d'exécution est M + 2M + 3M + ... + NM = M×(1 + 2 + ... + N) = O(N2).

Ainsi, summing chaînes serait beaucoup plus lent que l' summing numéros.

str.join ne permet pas de répartir les objets intermédiaires. Il preallocates un tampon assez grande pour contenir la rejoint cordes, et copie les données de la chaîne. Il s'exécute en O(N) le temps, beaucoup plus vite qu' sum.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X