Тъй като в Python string
не може да се променя, се чудя как да конкатенирам по-ефективно низ?
Мога да напиша така:
s += stringfromelsewhere
или така:
s = []
s.append(somestring)
later
s = ''.join(s)
Докато пишех този въпрос, намерих една добра статия, в която се говори по темата.
http://www.skymind.com/~ocrow/python_string/
Но тя е за Python 2.x., така че въпросът би бил дали нещо се е променило в Python 3?
Ако конкатенирате много стойности, тогава не. Присъединяването на списък е скъпо. Можете да използвате StringIO за това. Особено ако го изграждате в рамките на много операции.
from cStringIO import StringIO
# python3: from io import StringIO
buf = StringIO()
buf.write('foo')
buf.write('foo')
buf.write('foo')
buf.getvalue()
# 'foofoofoo'
Ако вече имате пълен списък, върнат ви от друга операция, тогава просто използвайте ''.join(aList)
От често задаваните въпроси за Python: Какъв е най-ефективният начин за обединяване на много низове?
обектите str и bytes са неизменни, затова конкатенирането на много низове заедно е неефективно, тъй като всяка конкатенация създава нов обект. В общия случай общите разходи за време за изпълнение са квадратични на общата дължина на низа.
За да се натрупат много стрингови обекти, препоръчителният идиом е да се поставят в списък и да се извика str.join() в края:
chunks = [] for s in my_strings: chunks.append(s) result = ''.join(chunks)
(друг достатъчно ефективен идиом е да се използва io.StringIO)
За натрупване на много обекти от байтове, препоръчителният идиом е да се разшири bytearray обект, като се използва конкатенация на място (оператор +=):
result = bytearray() for b in my_bytes_objects: result += b
Edit: Бях глупав и бях вмъкнал резултатите обратно, което създаваше впечатление, че добавянето към списък е по-бързо от cStringIO. Добавих и тестове за конкатиране на байт масиви/струни, както и втори кръг от тестове, използващи по-голям списък с по-големи низове. (Python 2.7.3)
Пример за тест на Python за големи списъци от низове
try:
from cStringIO import StringIO
except:
from io import StringIO
source = ['foo']*1000
%%timeit buf = StringIO()
for i in source:
buf.write(i)
final = buf.getvalue()
# 1000 loops, best of 3: 1.27 ms per loop
%%timeit out = []
for i in source:
out.append(i)
final = ''.join(out)
# 1000 loops, best of 3: 9.89 ms per loop
%%timeit out = bytearray()
for i in source:
out += i
# 10000 loops, best of 3: 98.5 µs per loop
%%timeit out = ""
for i in source:
out += i
# 10000 loops, best of 3: 161 µs per loop
## Repeat the tests with a larger list, containing
## strings that are bigger than the small string caching
## done by the Python
source = ['foo']*1000
# cStringIO
# 10 loops, best of 3: 19.2 ms per loop
# list append and join
# 100 loops, best of 3: 144 ms per loop
# bytearray() +=
# 100 loops, best of 3: 3.8 ms per loop
# str() +=
# 100 loops, best of 3: 5.11 ms per loop
Макар и донякъде остаряла, статията Code Like a Pythonista: Idiomatic Python препоръчва join()
вместо +
в този раздел. Както и PythonSpeedPerformanceTips в раздела си за конкатенация на низове, със следната забележка:
Точността на този раздел се оспорва по отношение на по-късните версии на Python. В CPython 2.5 конкатенацията на низове е доста бързо, въпреки че това може да не се отнася по същия начин за други Python имплементации. Вижте ConcatenationTestCode за обсъждане.