TL; DR: Jika Anda hanya mencari cara sederhana untuk menambahkan string, dan Anda tidak peduli dengan efisiensi:"foo" + "bar" + str(3)
Andrew
Jawaban:
609
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):
voidPyBytes_ConcatAndDel(registerPyObject**pv,registerPyObject*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){registerPyObject*v;registerPyBytesObject*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 */return0;}
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.
Menarik. Dengan "sekarang", maksud Anda Python 3.x?
Steve Tjoa
10
@Steve, Tidak. Ini setidaknya di 2,6 mungkin bahkan 2,5
John La Rooy
8
Anda telah mengutip PyString_ConcatAndDelfungsinya tetapi menyertakan komentar untuk _PyString_Resize. Juga, komentar itu tidak benar-benar membuktikan klaim Anda tentang Big-O
Winston Ewert
3
selamat atas eksploitasi fitur CPython yang akan membuat kode merayapi implementasi lainnya. Saran yang buruk.
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.joinuntuk menggabungkan semuanya pada akhirnya:
l =[]
l.append('foo')
l.append('bar')
l.append('baz')
s =''.join(l)
Saya tidak tahu apa implikasi kecepatan membangun string Anda sebagai daftar dan kemudian bergabung dengan mereka, tetapi saya menemukan itu umumnya cara terbersih. Saya juga telah sukses besar dengan menggunakan notasi% s dalam sebuah string untuk mesin templating SQL yang saya tulis.
richo
25
@Richo Menggunakan .join lebih efisien. Alasannya adalah bahwa string Python tidak dapat diubah, jadi berulang kali menggunakan s + = lebih akan mengalokasikan banyak string yang lebih besar secara berturut-turut. .join akan menghasilkan string terakhir dalam sekali jalan dari bagian-bagian penyusunnya.
Ben
5
@ Ben, ada peningkatan signifikan di bidang ini - lihat jawaban saya
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.
maaf tidak ada yang lebih mudah dibaca daripada (string + string) seperti contoh pertama, contoh kedua mungkin lebih efisien, tetapi tidak lebih mudah dibaca
JqueryToAddNumbers
23
@ExceptionSlayer, string + string cukup mudah diikuti. Tapi "<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())
Winston Ewert
Ini tidak membantu sama sekali ketika apa yang saya coba lakukan adalah setara dengan, katakanlah, "string. = Verifydata ()" PHP / perl atau sejenisnya.
Shadur
@ Safad, maksud saya adalah Anda harus berpikir lagi, apakah Anda benar-benar ingin melakukan sesuatu yang setara, atau apakah pendekatan yang sama sekali berbeda lebih baik?
Winston Ewert
1
Dan dalam hal ini jawaban untuk pertanyaan itu adalah "Tidak, karena pendekatan itu tidak mencakup kasus penggunaan saya"
Shadur
11
Python 3.6 memberi kita f-string , yang menyenangkan:
var1 ="foo"
var2 ="bar"
var3 = f"{var1}{var2}"print(var3)# prints foobar
Anda dapat melakukan hampir semua hal di dalam kurung kurawal
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 +.
itu sangat tergantung pada aplikasi Anda. Jika Anda menggunakan ratusan kata dan ingin menambahkan semuanya ke dalam daftar, .join()lebih baik. Tetapi jika Anda menyusun kalimat yang panjang, Anda lebih baik menggunakannya +=.
Kode itu bagus, tetapi akan membantu jika ada penjelasan yang menyertainya. Mengapa menggunakan metode ini daripada jawaban lain di halaman ini?
cgmb
11
Penggunaan 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)
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
dulu O (n ^ 2), tapi sekarang O (n).
Dari sumber (bytesobject.c):
Cukup mudah untuk memverifikasi secara empiris.
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.
Sejauh ini bagus, tapi kemudian,
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.
sumber
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+=
: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:sumber
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.
sumber
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.
sumber
"<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())
Python 3.6 memberi kita f-string , yang menyenangkan:
Anda dapat melakukan hampir semua hal di dalam kurung kurawal
sumber
Jika Anda perlu melakukan banyak operasi tambahan untuk membangun string besar, Anda dapat menggunakan StringIO atau cStringIO. Antarmuka seperti file. yaitu: kamu
write
menambahkan teks ke dalamnya.Jika Anda hanya menambahkan dua string maka gunakan saja
+
.sumber
itu sangat tergantung pada aplikasi Anda. Jika Anda menggunakan ratusan kata dan ingin menambahkan semuanya ke dalam daftar,
.join()
lebih baik. Tetapi jika Anda menyusun kalimat yang panjang, Anda lebih baik menggunakannya+=
.sumber
Pada dasarnya tidak ada perbedaan. Satu-satunya tren yang konsisten adalah bahwa Python tampaknya semakin lambat dengan setiap versi ... :(
Daftar
Python 2.7
Python 3.4
Python 3.5
Python 3.6
Tali
Python 2.7 :
Python 3.4
Python 3.5
Python 3.6
sumber
1.19 s
dan992 ms
masing - masing di Python2.7tambahkan string dengan fungsi __add__
Keluaran
sumber
str + str2
masih lebih pendek.sumber
a.__add__(b)
identik dengan menulisa+b
. Saat Anda menggabungkan string menggunakan+
operator, Python akan memanggil__add__
metode pada string di sisi kiri melewati string sisi kanan sebagai parameter.