Aşağıdakiler dışında Python başka bir dize eklemek için etkili bir yol istiyorum.
var1 = "foo"
var2 = "bar"
var3 = var1 + var2
Kullanılacak iyi bir yerleşik yöntem var mı?
Aşağıdakiler dışında Python başka bir dize eklemek için etkili bir yol istiyorum.
var1 = "foo"
var2 = "bar"
var3 = var1 + var2
Kullanılacak iyi bir yerleşik yöntem var mı?
Yanıtlar:
Bir dizeye yalnızca bir başvurunuz varsa ve sonuna kadar başka bir dizeyi birleştirirseniz, CPython şimdi bunu özel olarak ele alır ve dizeyi yerinde genişletmeye çalışır.
Nihai sonuç, operasyonun O (n) amortismana tabi tutulmasıdır.
Örneğin
s = ""
for i in range(n):
s+=str(i)
eskiden O (n ^ 2) idi, ama şimdi O (n).
Kaynaktan (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;
}
Ampirik olarak doğrulamak yeterince kolaydır.
$ python -m timeit -s "s = ''" "xrange (10) 'da: s + =' a '" 1000000 döngü, döngü başına en iyi 3: 1,85 usec $ python -m timeit -s "s = ''" "xrange'de (100): s + = 'a'" 10000 döngü, döngü başına en iyi 3: 16,8 usec $ python -m timeit -s "s = ''" "xrange (1000) 'de: s + =' a '" 10000 döngü, en iyi döngü başına 3: 158 usec $ python -m timeit -s "s = ''" "xrange'de (10000): s + = 'a'" 1000 döngü, döngü başına en iyi 3: 1,71 msn xrange'de (100000) i için $ python -m timeit -s "s = ''" ": s + = 'a'" 10 döngü, döngü başına en iyi 3: 14,6 ms $ python -m timeit -s "s = ''" "xrange'de (1000000): s + = 'a'" 10 döngü, döngü başına en iyi 3: 173 msn
Ancak bu optimizasyonun Python spesifikasyonunun bir parçası olmadığını belirtmek önemlidir . Bildiğim kadarıyla sadece cPython uygulamasında. Örneğin, pypy veya jython üzerinde yapılan aynı ampirik testler eski O (n ** 2) performansını gösterebilir.
$ pypy -m timeit -s "s = ''" "xrange'deki (10): s + = 'a'" 10000 döngü, döngü başına en iyi 3: 90,8 usec $ pypy -m timeit -s "s = ''" "xrange (100) 'de: s + =' a '" 1000 döngü, döngü başına en iyi 3: 896 usec $ pypy -m timeit -s "s = ''" "xrange (1000) 'de: s + =' a '" 100 döngü, en iyi 3: döngü başına 9,03 ms $ pypy -m timeit -s "s = ''" "xrange'de (10000): s + = 'a'" 10 döngü, döngü başına en iyi 3: 89,5 ms
Şimdiye kadar çok iyi, ama sonra,
$ pypy -m timeit -s "s = ''" "xrange'de (100000): s + = 'a'" 10 döngü, döngü başına en iyi 3: 12,8 sn
ikinci dereceden bile daha kötü. Yani pypy kısa dizelerle iyi çalışan bir şey yapıyor, ancak daha büyük dizeler için kötü performans gösteriyor.
PyString_ConcatAndDel
İşlevi alıntıladınız ancak yorumunu eklediniz _PyString_Resize
. Ayrıca, yorum gerçekten Big-O ile ilgili iddianızı oluşturmuyor
"".join(str_a, str_b)
Zamanından önce optimize etmeyin. Dize birleşmelerinin neden olduğu bir hız darboğazı olduğuna inanmak için bir nedeniniz yoksa, sadece +
ve ile sopa +=
:
s = 'foo'
s += 'bar'
s += 'baz'
Bununla birlikte, Java'nın StringBuilder'ı gibi bir şey hedefliyorsanız, standart Python deyimi bir listeye öğe eklemek ve daha sonra str.join
hepsini birleştirmek için kullanmaktır :
l = []
l.append('foo')
l.append('bar')
l.append('baz')
s = ''.join(l)
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))
Bu str1 ve str2'yi ayırıcılar olarak boşlukla birleştirir. Siz de yapabilirsiniz "".join(str1, str2, ...)
. str.join()
bir yinelenebilir alır, bu nedenle dizeleri bir listeye veya bir tuple koymak zorunda kalırsınız.
Bu yerleşik bir yöntem için gereken kadar verimlidir.
Yapma.
Diğer bir deyişle, çoğu durumda varolan bir dizeye eklemek yerine tüm dizeyi tek seferde oluşturmak daha iyidir.
Örneğin, şunları yapmayın: obj1.name + ":" + str(obj1.count)
Bunun yerine: kullanın "%s:%d" % (obj1.name, obj1.count)
Bunu okumak daha kolay ve daha verimli olacak.
"<div class='" + className + "' id='" + generateUniqueId() + "'>" + message_text + "</div>"
, o zaman daha az okunabilir ve hataya yatkın buluyorum"<div class='{classname}' id='{id}'>{message_text}</div>".format(classname=class_name, message_text=message_text, id=generateUniqueId())
Python 3.6 bize bir zevk olan f-stringleri verir :
var1 = "foo"
var2 = "bar"
var3 = f"{var1}{var2}"
print(var3) # prints foobar
Kıvırcık parantez içinde çoğu şeyi yapabilirsiniz
print(f"1 + 1 == {1 + 1}") # prints 1 + 1 == 2
Büyük bir dize oluşturmak için birçok ekleme işlemi yapmanız gerekiyorsa, StringIO veya cStringIO kullanabilirsiniz . Arayüz bir dosya gibidir. ie: write
metne metin ekleyebilirsiniz.
Sadece iki dize ekliyorsanız kullanın +
.
Temel olarak, hiçbir fark yok. Tek tutarlı trend, Python'un her sürümde yavaşladığı görülüyor ... :(
%%timeit
x = []
for i in range(100000000): # xrange on Python 2.7
x.append('a')
x = ''.join(x)
Python 2.7
1 döngü, en iyi 3: döngü başına 7,34 s
Python 3.4
1 döngü, en iyi 3: döngü başına 7,99 s
Python 3.5
1 loop, en iyisi 3: loop başına 8.48 s
Python 3.6
1 döngü, en iyi 3: döngü başına 9,93 s
%%timeit
x = ''
for i in range(100000000): # xrange on Python 2.7
x += 'a'
Python 2.7 :
1 loop, en iyisi 3: loop başına 7.41 s
Python 3.4
1 döngü, en iyisi 3: döngü başına 9,08 s
Python 3.5
1 loop, en iyisi 3: loop başına 8.82 s
Python 3.6
1 loop, en iyisi 3: loop başına 9.24 s
1.19 s
ve 992 ms
sırasıyla Python2.7
__add__ işlevi ile dize ekleme
str = "Hello"
str2 = " World"
st = str.__add__(str2)
print(st)
Çıktı
Hello World
str + str2
hala daha kısadır.
a='foo'
b='baaz'
a.__add__(b)
out: 'foobaaz'
a.__add__(b)
yazma ile aynıdır a+b
. Dizeleri +
işleç kullanarak birleştirdiğinizde , Python __add__
sol taraftaki dizede yöntemi sağ taraftaki dizeyi parametre olarak geçirerek çağırır .
"foo" + "bar" + str(3)