Saya ingin cara yang efisien untuk menambahkan satu string ke yang lain dengan Python, selain yang berikut.
var1 = "foo"
var2 = "bar"
var3 = var1 + var2
Apakah ada metode bawaan yang baik untuk digunakan?
Saya ingin cara yang efisien untuk menambahkan satu string ke yang lain dengan Python, selain yang berikut.
var1 = "foo"
var2 = "bar"
var3 = var1 + var2
Apakah ada metode bawaan yang baik untuk digunakan?
Jawaban:
Jika Anda hanya memiliki satu referensi ke sebuah string dan Anda merangkai string yang lain sampai akhir, CPython sekarang memberikan kasus khusus ini dan mencoba untuk memperpanjang string di tempatnya.
Hasil akhirnya adalah bahwa operasi tersebut diamortisasi O (n).
misalnya
s = ""
for i in range(n):
s+=str(i)
dulu O (n ^ 2), tapi sekarang O (n).
Dari sumber (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;
}
Cukup mudah untuk memverifikasi secara empiris.
$ python -m timeit -s "s = ''" "untuk saya di xrange (10): s + = 'a'" 10.00000 loop, terbaik 3: 1,85 USD per loop $ python -m timeit -s "s = ''" "untuk saya di xrange (100): s + = 'a'" 10000 loop, terbaik 3: 16,8 usec per loop $ python -m timeit -s "s = ''" "untuk saya di xrange (1000): s + = 'a'" 10.000 loop, terbaik 3: 158 usec per loop $ python -m timeit -s "s = ''" "untuk saya di xrange (10000): s + = 'a'" 1000 loop, terbaik 3: 1,71 msec per loop $ python -m timeit -s "s = ''" "untuk saya di xrange (100000): s + = 'a'" 10 loop, terbaik 3: 14,6 msec per loop $ python -m timeit -s "s = ''" "untuk saya di xrange (1000000): s + = 'a'" 10 loop, terbaik 3: 173 msec per loop
Namun penting untuk dicatat bahwa optimasi ini bukan bagian dari spesifikasi Python. Itu hanya dalam implementasi cPython sejauh yang saya tahu. Pengujian empiris yang sama pada pypy atau jython misalnya dapat menunjukkan kinerja O (n ** 2) yang lebih lama.
$ pypy -m timeit -s "s = ''" "untuk saya di xrange (10): s + = 'a'" 10000 loop, terbaik dari 3: 90,8 USD per loop $ pypy -m timeit -s "s = ''" "untuk saya di xrange (100): s + = 'a'" 1000 loop, terbaik 3: 896 usec per loop $ pypy -m timeit -s "s = ''" "untuk saya di xrange (1000): s + = 'a'" 100 loop, terbaik 3: 9,03 msec per loop $ pypy -m timeit -s "s = ''" "untuk saya di xrange (10000): s + = 'a'" 10 loop, terbaik 3: 89,5 msec per loop
Sejauh ini bagus, tapi kemudian,
$ pypy -m timeit -s "s = ''" "untuk saya di xrange (100000): s + = 'a'" 10 loop, terbaik 3: 12,8 detik per loop
Aduh bahkan lebih buruk dari kuadrat. Jadi pypy melakukan sesuatu yang bekerja dengan baik dengan string pendek, tetapi berkinerja buruk untuk string yang lebih besar.
PyString_ConcatAndDel
fungsinya tetapi menyertakan komentar untuk _PyString_Resize
. Juga, komentar itu tidak benar-benar membuktikan klaim Anda tentang Big-O
"".join(str_a, str_b)
Jangan mengoptimalkan secara prematur. Jika Anda tidak memiliki alasan untuk percaya ada bottleneck cepat yang disebabkan oleh penggabungan string maka tetaplah dengan +
dan +=
:
s = 'foo'
s += 'bar'
s += 'baz'
Yang mengatakan, jika Anda bertujuan untuk sesuatu seperti StringBuilder Java, idiom Python kanonik adalah menambahkan item ke daftar dan kemudian gunakan str.join
untuk menggabungkan semuanya pada akhirnya:
l = []
l.append('foo')
l.append('bar')
l.append('baz')
s = ''.join(l)
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))
Itu bergabung dengan str1 dan str2 dengan spasi sebagai pemisah. Anda juga bisa melakukannya "".join(str1, str2, ...)
. str.join()
Dibutuhkan iterable, jadi Anda harus meletakkan string dalam daftar atau tuple.
Itu tentang seefisien yang didapat untuk metode builtin.
Jangan.
Artinya, untuk sebagian besar kasus, Anda lebih baik membuat seluruh string dalam sekali jalan daripada menambahkan ke string yang ada.
Misalnya, jangan lakukan: obj1.name + ":" + str(obj1.count)
Sebaliknya: gunakan "%s:%d" % (obj1.name, obj1.count)
Itu akan lebih mudah dibaca dan lebih efisien.
"<div class='" + className + "' id='" + generateUniqueId() + "'>" + message_text + "</div>"
, saya menemukan itu kurang mudah dibaca dan rawan kesalahan kemudian"<div class='{classname}' id='{id}'>{message_text}</div>".format(classname=class_name, message_text=message_text, id=generateUniqueId())
Jika Anda perlu melakukan banyak operasi tambahan untuk membangun string besar, Anda dapat menggunakan StringIO atau cStringIO. Antarmuka seperti file. yaitu: kamuwrite
menambahkan teks ke dalamnya.
Jika Anda hanya menambahkan dua string maka gunakan saja +
.
Pada dasarnya tidak ada perbedaan. Satu-satunya tren yang konsisten adalah bahwa Python tampaknya semakin lambat dengan setiap versi ... :(
%%timeit
x = []
for i in range(100000000): # xrange on Python 2.7
x.append('a')
x = ''.join(x)
Python 2.7
1 loop, terbaik 3: 7.34 s per loop
Python 3.4
1 loop, terbaik 3: 7.99 s per loop
Python 3.5
1 loop, terbaik 3: 8,48 s per loop
Python 3.6
1 loop, terbaik 3: 9,93 detik per loop
%%timeit
x = ''
for i in range(100000000): # xrange on Python 2.7
x += 'a'
Python 2.7 :
1 loop, terbaik 3: 7.41 s per loop
Python 3.4
1 loop, terbaik 3: 9,08 s per loop
Python 3.5
1 loop, terbaik 3: 8,82 detik per loop
Python 3.6
1 loop, terbaik 3: 9,24 s per loop
1.19 s
dan 992 ms
masing - masing di Python2.7
tambahkan string dengan fungsi __add__
str = "Hello"
str2 = " World"
st = str.__add__(str2)
print(st)
Keluaran
Hello World
str + str2
masih lebih pendek.
a='foo'
b='baaz'
a.__add__(b)
out: 'foobaaz'
a.__add__(b)
identik dengan menulis a+b
. Saat Anda menggabungkan string menggunakan +
operator, Python akan memanggil __add__
metode pada string di sisi kiri melewati string sisi kanan sebagai parameter.
"foo" + "bar" + str(3)