Jakiś powód, aby nie używać '+' do łączenia dwóch łańcuchów?

Popularna antypatterna w Pythonie polega na łączeniu sekwencji łańcuchów za pomocą + W pętli. Jest to złe, ponieważ interpreter Pythona musi utworzyć nowy obiekt string dla każdej iteracji, a kończy się to zajęciem kwadratowego czasu. (Najnowsze wersje CPython mogą najwyraźniej zoptymalizować to w niektórych przypadkach, ale inne implementacje nie mogą, więc programiści nie są zniechęcani do polegania na tym.) ''.join jest właściwy sposób, aby to zrobić.

/ Align = "left" / ( w tym tutaj na Stack Overflow ), że należy nigdy, przenigdy używać + do łączenia łańcuchów, ale zamiast tego zawsze używać ''.join lub formatu łańcuchów. Nie rozumiem, dlaczego tak jest, skoro łączy się tylko dwie struny. Jeśli moje zrozumienie jest poprawne, nie powinno to zająć kwadratowego czasu i myślę, że a + b jest czystsze i bardziej czytelne niż ''.join((a, b)) lub '%s%s' % (a, b).

Czy dobrą praktyką jest używanie + do łączenia dwóch łańcuchów? Czy jest jakiś problem, którym nie jestem o czym?

Author: Community, 2012-04-06

8 answers

Nie ma nic złego w połączeniu dwóch łańcuchów z +. Rzeczywiście jest łatwiejszy do odczytania niż ''.join([a, b]).

Masz jednak rację, że łączenie więcej niż 2 łańcuchów z + jest operacją O (N^2) (w porównaniu do O (N) dla join) i tym samym staje się nieefektywne. Nie ma to jednak nic wspólnego z użyciem pętli. Parzyste a + b + c + ... to O (n^2), z tego powodu każda konkatenacja tworzy nowy ciąg.

CPython2. 4 i wyżej spróbuj to złagodzić, ale nadal jest wskazane, aby użyj join podczas łączenia więcej niż 2 łańcuchów.

 124
Author: ggozad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-10 12:44:28

Operator Plus jest doskonałym rozwiązaniem do połączenia dwóch ciągów Pythona. Ale jeśli dodasz więcej niż dwa ciągi znaków (N > 25), możesz pomyśleć o czymś innym.

''.join([a, b, c]) trick to optymalizacja wydajności.

 49
Author: Mikko Ohtamaa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-06 18:01:05

Założenie, że nigdy, przenigdy nie należy używać + do konkatenacji łańcuchów, ale zamiast tego zawsze używać ".dołączenie może być mitem. To prawda, że użycie + tworzy niepotrzebne tymczasowe kopie niezmiennego obiektu string, ale innym nieczęsto cytowanym faktem jest to, że wywołanie join W pętli ogólnie dodałoby narzut function call. Weźmy twój przykład.

Stwórz dwie listy, jedną z linked SO a drugą większą sfabrykowaną

>>> myl1 = ['A','B','C','D','E','F']
>>> myl2=[chr(random.randint(65,90)) for i in range(0,10000)]

Pozwala utworzyć dwie funkcje, UseJoin i UsePlus aby używać odpowiednich funkcji join i +.

>>> def UsePlus():
    return [myl[i] + myl[i + 1] for i in range(0,len(myl), 2)]

>>> def UseJoin():
    [''.join((myl[i],myl[i + 1])) for i in range(0,len(myl), 2)]

Lets run timeit with the first list

>>> myl=myl1
>>> t1=timeit.Timer("UsePlus()","from __main__ import UsePlus")
>>> t2=timeit.Timer("UseJoin()","from __main__ import UseJoin")
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=100000)/100000)
2.48 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000)
2.61 usec/pass
>>> 

Mają prawie ten sam czas działania.

Lets use cProfile

>>> myl=myl2
>>> cProfile.run("UsePlus()")
         5 function calls in 0.001 CPU seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.001    0.001    0.001    0.001 <pyshell#1376>:1(UsePlus)
        1    0.000    0.000    0.001    0.001 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {range}


>>> cProfile.run("UseJoin()")
         5005 function calls in 0.029 CPU seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.015    0.015    0.029    0.029 <pyshell#1388>:1(UseJoin)
        1    0.000    0.000    0.029    0.029 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     5000    0.014    0.000    0.014    0.000 {method 'join' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {range}

I wygląda na to, że użycie Join powoduje niepotrzebne wywołania funkcji, które mogłyby zwiększyć obciążenie.

Wracając do pytania. Czy należy zniechęcać do stosowania + nad join we wszystkich przypadkach?

Uważam, że nie, rzeczy powinny być brane pod uwagę rozważanie

  1. Długość danego ciągu
  2. nr operacji konkatenacji.

A poza kursem w rozwoju przed-dojrzałej optymalizacji jest złem.

 8
Author: Abhijit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-06 13:24:42

Podczas pracy z wieloma osobami czasami trudno jest dokładnie wiedzieć, co się dzieje. Użycie ciągu formatowego zamiast konkatenacji może uniknąć jednej szczególnej irytacji, która zdarzyła się nam mnóstwo razy:

Powiedzmy, że funkcja wymaga argumentu, a ty piszesz ją oczekując, że otrzymasz ciąg znaków:

In [1]: def foo(zeta):
   ...:     print 'bar: ' + zeta

In [2]: foo('bang')
bar: bang

Więc ta funkcja może być używana dość często w całym kodzie. Twoi współpracownicy mogą dokładnie wiedzieć, co to robi, ale niekoniecznie być w pełni na bieżąco wewnętrzne i może nie wiedzieć, że funkcja oczekuje ciągu znaków. I tak mogą skończyć z tym:

In [3]: foo(23)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/izkata/<ipython console> in <module>()

/home/izkata/<ipython console> in foo(zeta)

TypeError: cannot concatenate 'str' and 'int' objects

Nie byłoby problemu, gdybyś po prostu użył ciągu formatu:

In [1]: def foo(zeta):
   ...:     print 'bar: %s' % zeta
   ...:     
   ...:     

In [2]: foo('bang')
bar: bang

In [3]: foo(23)
bar: 23

To samo dotyczy wszystkich typów obiektów, które definiują __str__, które mogą być również przekazywane:

In [1]: from datetime import date

In [2]: zeta = date(2012, 4, 15)

In [3]: print 'bar: ' + zeta
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/izkata/<ipython console> in <module>()

TypeError: cannot concatenate 'str' and 'datetime.date' objects

In [4]: print 'bar: %s' % zeta
bar: 2012-04-15

Więc tak: jeśli możesz użyć formatu string zrób to i skorzystaj z tego, co Python ma do zaoferowania.

 7
Author: Izkata,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-06 14:28:57

Zrobiłem szybki test:

import sys

str = e = "a xxxxxxxxxx very xxxxxxxxxx long xxxxxxxxxx string xxxxxxxxxx\n"

for i in range(int(sys.argv[1])):
    str = str + e

I czas:

mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py  8000000
8000000 times

real    0m2.165s
user    0m1.620s
sys     0m0.540s
mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py  16000000
16000000 times

real    0m4.360s
user    0m3.480s
sys     0m0.870s

Jest najwyraźniej Optymalizacja dla a = a + b przypadku. Nie wykazuje czasu O (N^2), Jak można podejrzewać.

Więc przynajmniej jeśli chodzi o wydajność, używanie + jest w porządku.

 3
Author: Michael Slade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-06 13:28:06

Zgodnie z Pythonem docs, używając str.join () zapewni zgodność wydajności w różnych implementacjach Pythona. Chociaż CPython optymalizuje kwadratowe zachowanie s = s + t, inne implementacje Pythona mogą tego nie robić.

Szczegóły implementacji CPython : Jeśli S I t są ciągami, niektóre Implementacje Pythona, takie jak CPython, mogą zazwyczaj wykonywać Optymalizacja dla zadań postaci s = s + T lub s + = T. gdy dotyczy, to optymalizacja sprawia, że kwadratowy czas pracy jest znacznie krótszy prawdopodobnie. Ta optymalizacja to zarówno Wersja, jak i implementacja zależny. W przypadku kodu wrażliwego na wydajność, lepiej jest użyć str.metoda join () zapewniająca spójną liniową konkatenację wydajność w różnych wersjach i implementacjach.

Typy sekwencji w Python docs (Zobacz notatkę stopki [6])

 3
Author: Duke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-04 12:49:41

Używam następujących w Pythonie 3.8

string4 = f'{string1}{string2}{string3}'
 2
Author: Lucas Vazquez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-18 15:04:38

''.join ([a, b]) jest lepszym rozwiązaniem niż +.

Ponieważ kod powinien być napisany w sposób, który nie zaszkodzi innym implementacjom Pythona (PyPy, Jython, IronPython, Cython, Psyco i tym podobne)

Forma a += b lub A = a + b jest krucha nawet w Cpythonie i w ogóle nie występuje w implementacjach , które nie używają refcounting (zliczanie odniesień jest techniką przechowywania liczby odniesień, wskaźników lub uchwytów do zasobu takie jak obiekt, blok pamięci, miejsce na dysku lub inny zasób )

Https://www.python.org/dev/peps/pep-0008/#programming-recommendations

 0
Author: muhammad ali e,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-29 12:50:32