web-dev-qa-db-fra.com

Comment ajouter une chaîne à une autre en Python?

Je veux un moyen efficace pour ajouter une chaîne à une autre en Python.

var1 = "foo"
var2 = "bar"
var3 = var1 + var2

Existe-t-il une bonne méthode intégrée à utiliser?

556
user469652

Si vous n'avez qu'une seule référence à une chaîne et que vous concaténez une autre chaîne jusqu'à la fin, CPython met maintenant ce cas en attente et tente d'étendre la chaîne à la place.

Le résultat final est que l'opération est amortie O (n).

par exemple.

s = ""
for i in range(n):
    s+=str(i)

l'habitude d'être O (n ^ 2), mais maintenant c'est O (n).

Depuis la source (bytesobject.c):

void
PyBytes_ConcatAndDel(register PyObject **pv, register PyObject *w)
{
    PyBytes_Concat(pv, w);
    Py_XDECREF(w);
}


/* The following function breaks the notion that strings are immutable:
   it changes the size of a string.  We get away with this only if there
   is only one module referencing the object.  You can also think of it
   as creating a new string object and destroying the old one, only
   more efficiently.  In any case, don't use this if the string may
   already be known to some other part of the code...
   Note that if there's not enough memory to resize the string, the original
   string object at *pv is deallocated, *pv is set to NULL, an "out of
   memory" exception is set, and -1 is returned.  Else (on success) 0 is
   returned, and the value in *pv may or may not be the same as on input.
   As always, an extra byte is allocated for a trailing \0 byte (newsize
   does *not* include that), and a trailing \0 byte is stored.
*/

int
_PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
{
    register PyObject *v;
    register PyBytesObject *sv;
    v = *pv;
    if (!PyBytes_Check(v) || Py_REFCNT(v) != 1 || newsize < 0) {
        *pv = 0;
        Py_DECREF(v);
        PyErr_BadInternalCall();
        return -1;
    }
    /* XXX UNREF/NEWREF interface should be more symmetrical */
    _Py_DEC_REFTOTAL;
    _Py_ForgetReference(v);
    *pv = (PyObject *)
        PyObject_REALLOC((char *)v, PyBytesObject_SIZE + newsize);
    if (*pv == NULL) {
        PyObject_Del(v);
        PyErr_NoMemory();
        return -1;
    }
    _Py_NewReference(*pv);
    sv = (PyBytesObject *) *pv;
    Py_SIZE(sv) = newsize;
    sv->ob_sval[newsize] = '\0';
    sv->ob_shash = -1;          /* invalidate cached hash value */
    return 0;
}

Il est assez facile de vérifier empiriquement.

 $ python -m timeit -s "s = ''" "pour i dans xrange (10): s + = 'a'" 
 1000000 boucles, meilleur des 3 : 1.85 usec par boucle 
 $ python -m timeit -s "s = ''" "" pour i dans xrange (100): s + = 'a' "
 10000 boucles, meilleur de 3: 16,8 usec par boucle 
 $ python -m timeit -s "s = ''" "pour i dans xrange (1000): s + = 'a'" 
 10000 boucles, au mieux de 3: 158 usec par boucle 
 $ python -m timeit -s "s = ''" "pour i dans xrange (10000): s + = 'a' "
 1000 boucles, meilleure de 3: 1,71 ms par boucle 
 $ python -m timeit -s" s = '' "" "pour i dans xrange ( 100000): s + = 'a' "
 10 boucles, meilleur de 3: 14,6 ms par boucle 
 $ python -m timeit -s" s = '' "" pour i in xrange (1000000): s + = 'a' "
 10 boucles, le meilleur de 3: 173 ms par boucle 

C'est important Cependant, notez que cette optimisation ne fait pas partie de la spécification Python. C'est seulement dans l'implémentation de cPython, autant que je sache. Les mêmes tests empiriques sur pypy ou jython, par exemple, pourraient montrer les performances plus anciennes de O (n ** 2).

 $ pypy -m timeit -s "s = ''" "pour i dans xrange (10): s + = 'a'" 
 10000 boucles, meilleur de 3: 90,8 usec par boucle 
 $ pypy -m timeit -s "s = ''" "pour i dans xrange (100): s + = 'a'" 
 1000 boucles, meilleur de 3: 896 usec par boucle 
 $ pypy -m timeit -s "s = ''" "pour i dans xrange (1000): s + = 'a'" 
 100 boucles, meilleur de 3: 9,03 msec par boucle 
 $ pypy -m timeit -s "s = ''" "pour i dans la plage (10000): s + = 'a'" 
 10 boucles, meilleur de 3: 89,5 msec par boucle 

Jusqu'ici tout va bien, mais alors,

 $ pypy -m timeit -s "s = ''" "pour i dans xrange (100000): s + = 'a'" 
 10 boucles, au mieux en 3: 12,8 secondes par boucle 

aïe pire que le quadratique. Donc, pypy fait quelque chose qui fonctionne bien avec des chaînes courtes, mais qui fonctionne mal pour les grandes chaînes.

573
John La Rooy

Ne pas optimiser prématurément. Si vous n'avez aucune raison de penser que les concaténations de chaînes ont un goulot d'étranglement au niveau de la vitesse, il vous suffit de vous en tenir à + et +=:

s  = 'foo'
s += 'bar'
s += 'baz'

Cela dit, si vous visez quelque chose comme StringBuilder de Java, l'idiome canonique Python consiste à ajouter des éléments à une liste, puis à utiliser str.join pour les concaténer à la fin:

l = []
l.append('foo')
l.append('bar')
l.append('baz')

s = ''.join(l)
272
John Kugelman

Ne pas.

C'est-à-dire que dans la plupart des cas, il est préférable de générer la chaîne entière en une fois plutôt que de l'ajouter à une chaîne existante.

Par exemple, ne faites pas: obj1.name + ":" + str(obj1.count)

Au lieu de cela: utilisez "%s:%d" % (obj1.name, obj1.count)

Ce sera plus facile à lire et plus efficace.

38
Winston Ewert
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))

Cela joint str1 et str2 avec un espace comme séparateur. Vous pouvez également faire "".join(str1, str2, ...). str.join() prend une valeur itérable, vous devez donc placer les chaînes dans une liste ou un tuple.

C'est à peu près aussi efficace que possible pour une méthode intégrée.

36
Rafe Kettler

Si vous devez effectuer de nombreuses opérations d’ajout pour construire une chaîne longue, vous pouvez utiliser StringIO ou cStringIO. L'interface est comme un fichier. c'est-à-dire que vous write pour y ajouter du texte.

Si vous ajoutez seulement deux chaînes, utilisez simplement +.

10
Laurence Gonsalves

cela dépend vraiment de votre application. Si vous parcourez des centaines de mots et que vous souhaitez tous les ajouter à une liste, .join() est préférable. Mais si vous composez une longue phrase, il vaut mieux utiliser +=.

9
Ramy

Python 3.6 nous donne f-strings , qui sont un délice:

var1 = "foo"
var2 = "bar"
var3 = f"{var1}{var2}"
print(var3)                       # prints foobar

Vous pouvez faire presque tout à l'intérieur des accolades

print(f"1 + 1 == {1 + 1}")        # prints 1 + 1 == 2
7
Trenton

En gros, pas de différence. La seule tendance constante est que Python semble ralentir à chaque version ... :(


Liste

%%timeit
x = []
for i in range(100000000):  # xrange on Python 2.7
    x.append('a')
x = ''.join(x)

Python 2.7

1 boucle, meilleur de 3: 7.34 s par boucle

Python 3.4

1 boucle, meilleur de 3: 7,99 s par boucle

Python 3.5

1 boucle, meilleur de 3: 8.48 s par boucle

Python 3.6

1 boucle, le meilleur de 3: 9.9 s par boucle


Chaîne

%%timeit
x = ''
for i in range(100000000):  # xrange on Python 2.7
    x += 'a'

Python 2.7:

1 boucle, meilleur de 3: 7.41 s par boucle

Python 3.4

1 boucle, meilleur de 3: 9.08 s par boucle

Python 3.5

1 boucle, le meilleur de 3: 8.82 s par boucle

Python 3.6

1 boucle, meilleur de 3: 9.24 s par boucle

5
ostrokach
a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'
3
Rahul Shrivastava

ajoute des chaînes avec _ ADD _ function

str = "Hello"
str2 = " World"
st = str.__add__(str2)
print(st)

Sortie

Hello World
3
saigopi