Hoe voeg ik de ene string toe aan de andere in Python?

Ik wil een efficiënte manier om de ene string aan de andere toe te voegen in Python, behalve de volgende.

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

Is er een goede ingebouwde methode om te gebruiken?


Antwoord 1, autoriteit 100%

Als je maar één verwijzing naar een tekenreeks hebt en je voegt een andere tekenreeks aan het einde toe, dan maakt CPython dit nu speciaal en probeert de tekenreeks op zijn plaats uit te breiden.

Het eindresultaat is dat de operatie O(n) wordt afgeschreven.

bijv.

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

was vroeger O(n^2), maar nu is het O(n).

Van de bron (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;
}

Het is eenvoudig genoeg om empirisch te verifiëren.

$ python -m timeit -s"s=''" "voor i in xrange(10):s+='a'"
1000000 lussen, beste van 3: 1,85 usec per lus
$ python -m timeit -s"s=''" "voor i in xrange(100):s+='a'"
10000 loops, beste van 3: 16,8 usec per loop
$ python -m timeit -s"s=''" "voor i in xrange(1000):s+='a'"
10000 loops, beste van 3: 158 usec per loop
$ python -m timeit -s"s=''" "voor i in xrange (10000):s+='a'"
1000 lussen, beste van 3: 1,71 msec per lus
$ python -m timeit -s"s=''" "voor i in xrange (100000):s+='a'"
10 lussen, beste van 3: 14,6 msec per lus
$ python -m timeit -s"s=''" "voor i in xrange(1000000):s+='a'"
10 lussen, beste van 3: 173 msec per lus

Het is echter belangrijk op te merken dat deze optimalisatie geen deel uitmaakt van de Python-specificatie. Het is alleen in de cPython-implementatie voor zover ik weet. Dezelfde empirische tests op pypy of jython kunnen bijvoorbeeld de oudere O(n**2)-prestaties aantonen.

$ pypy -m timeit -s"s=''" "voor i in xrange(10):s+='a'"
10000 lussen, beste van 3: 90,8 usec per lus
$ pypy -m timeit -s"s=''" "voor i in xrange(100):s+='a'"
1000 loops, beste van 3: 896 usec per loop
$ pypy -m timeit -s"s=''" "voor i in xrange(1000):s+='a'"
100 lussen, beste van 3: 9,03 msec per lus
$ pypy -m timeit -s"s=''" "voor i in xrange (10000):s+='a'"
10 lussen, beste van 3: 89,5 msec per lus

Tot nu toe goed, maar dan,

$ pypy -m timeit -s"s=''" "voor i in xrange (100000):s+='a'"
10 lussen, beste van 3: 12,8 sec per lus

nog erger dan kwadratisch. Dus pypy doet iets dat goed werkt met korte snaren, maar slecht presteert voor grotere snaren.


Antwoord 2, autoriteit 46%

Niet voortijdig optimaliseren. Als je geen reden hebt om aan te nemen dat er een snelheidsprobleem is dat wordt veroorzaakt door aaneenschakeling van tekenreeksen, blijf dan gewoon bij + en +=:

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

Dat gezegd hebbende, als je iets als Java’s StringBuilder nastreeft, is het canonieke Python-idioom om items aan een lijst toe te voegen en vervolgens str.join te gebruiken om ze allemaal aan het einde samen te voegen:

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

Antwoord 3, autoriteit 7%

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

Dat verbindt str1 en str2 met een spatie als scheidingsteken. Je kunt ook "".join(str1, str2, ...) doen. str.join() neemt een iterable, dus je zou de strings in een lijst of een tuple moeten zetten.

Dat is ongeveer net zo efficiënt als mogelijk is voor een ingebouwde methode.


Antwoord 4, autoriteit 6%

Niet doen.

Dat wil zeggen, in de meeste gevallen kunt u beter de hele reeks in één keer genereren in plaats van deze aan een bestaande reeks toe te voegen.

Doe bijvoorbeeld niet: obj1.name + ":" + str(obj1.count)

In plaats daarvan: gebruik "%s:%d" % (obj1.name, obj1.count)

Dat is gemakkelijker te lezen en efficiënter.


Antwoord 5, autoriteit 2%

Python 3.6 geeft ons f-strings, die een genot:

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

Je kunt bijna alles doen binnen de accolades

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

Antwoord 6, autoriteit 2%

Als u veel toevoegbewerkingen moet uitvoeren om een ​​grote tekenreeks te maken, kunt u StringIO of cStringIO. De interface is als een bestand. dat wil zeggen: u write om er tekst aan toe te voegen.

Als je slechts twee strings toevoegt, gebruik dan gewoon +.


Antwoord 7

het hangt echt af van uw toepassing. Als je honderden woorden doorloopt en ze allemaal aan een lijst wilt toevoegen, is .join() beter. Maar als je een lange zin maakt, kun je beter += gebruiken.


Antwoord 8

In principe geen verschil. De enige consistente trend is dat Python met elke versie langzamer lijkt te worden… 🙁


Lijst

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

Python 2.7

1 lus, beste van 3: 7,34 s per lus

Python 3.4

1 lus, beste van 3: 7,99 s per lus

Python 3.5

1 lus, beste van 3: 8,48 s per lus

Python 3.6

1 lus, beste van 3: 9,93 s per lus


String

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

Python 2.7:

1 lus, beste van 3: 7,41 s per lus

Python 3.4

1 lus, beste van 3: 9,08 s per lus

Python 3.5

1 lus, beste van 3: 8,82 s per lus

Python 3.6

1 lus, beste van 3: 9,24 s per lus


Antwoord 9

strings toevoegen met de functie add

str1 = "Hello"
str2 = " World"
str3 = str.__add__(str2)
print(str3)

Uitvoer

Hello World

Antwoord 10

a='foo'
b='baaz'
a.__add__(b)
out: 'foobaaz'

LEAVE A REPLY

Please enter your comment!
Please enter your name here

two + six =

Other episodes