[python] 파이썬에서 문자열 반전

reverse파이썬 str객체 에는 내장 함수 가 없습니다 . 이 방법을 구현하는 가장 좋은 방법은 무엇입니까?

매우 간결한 답변을 제공하는 경우 효율성을 자세히 설명하십시오. 예를 들어 str객체가 다른 객체로 변환 되는지 여부 등



답변

어때요?

>>> 'hello world'[::-1]
'dlrow olleh'

이것은 확장 슬라이스 구문입니다. 그것은 수행하여 작동합니다 [begin:end:step]– 시작 오프 종료 떠나 -1 단계를 지정하여,이 문자열은 반전.


답변

@ Paolo ‘s s[::-1]가 가장 빠릅니다. 느린 접근 방식 (더 읽기 쉽지만 논쟁의 여지가 있음)은 ''.join(reversed(s))입니다.


답변

문자열에 역 기능을 구현하는 가장 좋은 방법은 무엇입니까?

이 질문에 대한 나의 경험은 학문적입니다. 그러나 빠른 답변을 찾고있는 전문가라면 다음 단계를 따르는 조각을 사용하십시오 -1.

>>> 'a string'[::-1]
'gnirts a'

이상의 판독 가능 (하지만 느린 인해 반복자를 제공 할 때 형태를리스트에 가입하는 방법 이름 조회 및 사실) str.join:

>>> ''.join(reversed('a string'))
'gnirts a'

가독성과 재사용 성을 위해 슬라이스를 함수에 넣습니다.

def reversed_string(a_string):
    return a_string[::-1]

그리고:

>>> reversed_string('a_string')
'gnirts_a'

더 긴 설명

학술 박람회에 관심이 있으시면 계속 읽으십시오.

파이썬의 str 객체에는 내장 역 기능이 없습니다.

다음은 알아야 할 Python 문자열에 대한 몇 가지 사항입니다.

  1. 파이썬에서 문자열은 불변 입니다. 문자열을 변경해도 문자열은 수정되지 않습니다. 새로운 것을 만듭니다.

  2. 문자열은 슬라이스 가능합니다. 문자열을 슬라이스하면 주어진 증분에 의해 문자열의 한 지점에서 앞뒤로 다른 지점으로 새 문자열이 제공됩니다. 아래 첨자에서 슬라이스 표기법 또는 슬라이스 객체를 사용합니다.

    string[subscript]

아래 첨자는 중괄호 안에 콜론을 포함시켜 슬라이스를 만듭니다.

    string[start:stop:step]

중괄호 외부에서 슬라이스를 만들려면 슬라이스 객체를 만들어야합니다.

    slice_obj = slice(start, stop, step)
    string[slice_obj]

읽을 수있는 접근법 :

''.join(reversed('foo'))읽을 수는 있지만 str.join다른 호출 된 함수에서 문자열 메소드를 호출해야합니다 . 이것을 함수에 넣자. 다시 돌아올 것이다.

def reverse_string_readable_answer(string):
    return ''.join(reversed(string))

가장 뛰어난 접근 방식 :

리버스 슬라이스를 사용하는 것이 훨씬 빠릅니다.

'foo'[::-1]

그러나 조각이나 원래 저자의 의도에 익숙하지 않은 사람이 어떻게 이것을 더 읽기 쉽고 이해하기 쉽게 만들 수 있습니까? 아래 첨자 표기법 외부에서 슬라이스 객체를 만들어 설명적인 이름을 지정하고 첨자 표기법으로 전달해 봅시다.

start = stop = None
step = -1
reverse_slice = slice(start, stop, step)
'foo'[reverse_slice]

기능으로 구현

실제로 이것을 함수로 구현하려면 설명적인 이름을 사용하기에 의미가 명확하다고 생각합니다.

def reversed_string(a_string):
    return a_string[::-1]

사용법은 간단합니다.

reversed_string('foo')

선생님이 원하는 것 :

강사가 있으면 빈 문자열로 시작하여 이전 문자열에서 새 문자열을 작성하기를 원할 것입니다. while 루프를 사용하여 순수한 구문과 리터럴로이를 수행 할 수 있습니다.

def reverse_a_string_slowly(a_string):
    new_string = ''
    index = len(a_string)
    while index:
        index -= 1                    # index = index - 1
        new_string += a_string[index] # new_string = new_string + character
    return new_string

문자열은 불변 이기 때문에 이론적으로 나쁩니다. 따라서 문자열을 문자로 추가하는 것처럼 보일 때마다 new_string이론적으로 매번 새 문자열을 만듭니다! 그러나 CPython은 특정 경우에이를 최적화하는 방법을 알고 있으며,이 경우는 사소한 경우입니다.

모범 사례

이론적으로 더 나은 방법은 목록에서 하위 문자열을 수집하여 나중에 결합하는 것입니다.

def reverse_a_string_more_slowly(a_string):
    new_strings = []
    index = len(a_string)
    while index:
        index -= 1
        new_strings.append(a_string[index])
    return ''.join(new_strings)

그러나 CPython에 대한 아래의 타이밍에서 볼 수 있듯이 CPython은 문자열 연결을 최적화 할 수 있기 때문에 실제로 시간이 더 오래 걸립니다.

타이밍

타이밍은 다음과 같습니다.

>>> a_string = 'amanaplanacanalpanama' * 10
>>> min(timeit.repeat(lambda: reverse_string_readable_answer(a_string)))
10.38789987564087
>>> min(timeit.repeat(lambda: reversed_string(a_string)))
0.6622700691223145
>>> min(timeit.repeat(lambda: reverse_a_string_slowly(a_string)))
25.756799936294556
>>> min(timeit.repeat(lambda: reverse_a_string_more_slowly(a_string)))
38.73570013046265

CPython은 문자열 연결을 최적화하지만 다른 구현에서는 그렇지 않을 수 있습니다 .

… a + = b 또는 a = a + b 형식의 명령문에 대해 CPython의 내부 문자열 연결의 효율적인 구현에 의존하지 마십시오. 이 최적화는 CPython에서도 취약하며 (일부 유형에서만 작동) 재 계산을 사용하지 않는 구현에는 전혀 존재하지 않습니다. 라이브러리의 성능에 민감한 부분에서는 ”.join () 형식을 대신 사용해야합니다. 이렇게하면 다양한 구현에서 선형 시간으로 연결이 발생합니다.


답변

빠른 답변 (TL; DR)

### example01 -------------------
mystring  =   'coup_ate_grouping'
backwards =   mystring[::-1]
print backwards

### ... or even ...
mystring  =   'coup_ate_grouping'[::-1]
print mystring

### result01 -------------------
'''
gnipuorg_eta_puoc
'''

자세한 답변

배경

이 답변은 @odigity의 다음 우려를 해결하기 위해 제공됩니다.

와. Paolo가 제안한 솔루션에 의해 처음에는 깜짝 놀랐다. 그러나 그것은 첫 번째 의견을 읽었을 때 느꼈던 공포에 뒷좌석을 가져 갔다. 나는 그렇게 밝은 공동체가 그렇게 기본적인 방법으로 그러한 암호 방법을 사용하는 것이 좋은 생각이라고 생각하기에 너무 불안합니다. 왜 s.reverse ()가 아닌가?

문제

  • 문맥
    • 파이썬 2.x
    • 파이썬 3.x
  • 대본:
    • 개발자가 문자열을 변환하려고합니다
    • 변환은 모든 문자의 순서를 반대로하는 것입니다

해결책

함정

  • 개발자는 다음과 같은 것을 기대할 수 있습니다. string.reverse()
  • 새로운 관용어 (일명 ” pythonic “) 솔루션은 새로운 개발자가 읽지 못할 수 있습니다.
  • 개발자는 string.reverse()슬라이스 표기법을 피하기 위해 자신의 버전을 구현하려는 유혹을받을 수 있습니다 .
  • 슬라이스 표기법의 출력은 일부 경우에 반 직관적 일 수 있습니다.
    • 예를 들어 example02 참조
      • print 'coup_ate_grouping'[-4:] ## => 'ping'
      • 에 비해
      • print 'coup_ate_grouping'[-4:-1] ## => 'pin'
      • 에 비해
      • print 'coup_ate_grouping'[-1] ## => 'g'
    • 색인 생성의 다른 결과로 인해 [-1]일부 개발자가 벗어날 수 있습니다.

이론적 해석

파이썬은 특별한 상황을 알고 있습니다 : 문자열은 반복 가능한 타입입니다.

string.reverse()방법 을 배제하는 한 가지 근거 는 파이썬 개발자에게이 특별한 상황의 힘을 활용하도록 인센티브를주는 것입니다.

간단히 말하면, 이는 단순히 다른 프로그래밍 언어의 배열과 마찬가지로 문자열의 각 개별 문자를 요소의 순차적 배열의 일부로 쉽게 조작 할 수 있음을 의미합니다.

이것이 어떻게 작동하는지 이해하기 위해 example02를 검토하면 좋은 개요를 제공 할 수 있습니다.

예 02

### example02 -------------------
## start (with positive integers)
print 'coup_ate_grouping'[0]  ## => 'c'
print 'coup_ate_grouping'[1]  ## => 'o' 
print 'coup_ate_grouping'[2]  ## => 'u' 

## start (with negative integers)
print 'coup_ate_grouping'[-1]  ## => 'g'
print 'coup_ate_grouping'[-2]  ## => 'n' 
print 'coup_ate_grouping'[-3]  ## => 'i' 

## start:end 
print 'coup_ate_grouping'[0:4]    ## => 'coup'    
print 'coup_ate_grouping'[4:8]    ## => '_ate'    
print 'coup_ate_grouping'[8:12]   ## => '_gro'    

## start:end 
print 'coup_ate_grouping'[-4:]    ## => 'ping' (counter-intuitive)
print 'coup_ate_grouping'[-4:-1]  ## => 'pin'
print 'coup_ate_grouping'[-4:-2]  ## => 'pi'
print 'coup_ate_grouping'[-4:-3]  ## => 'p'
print 'coup_ate_grouping'[-4:-4]  ## => ''
print 'coup_ate_grouping'[0:-1]   ## => 'coup_ate_groupin'
print 'coup_ate_grouping'[0:]     ## => 'coup_ate_grouping' (counter-intuitive)

## start:end:step (or start:end:stride)
print 'coup_ate_grouping'[-1::1]  ## => 'g'   
print 'coup_ate_grouping'[-1::-1] ## => 'gnipuorg_eta_puoc'

## combinations
print 'coup_ate_grouping'[-1::-1][-4:] ## => 'puoc'

결론

인지 부하는 파이썬에서 슬라이스 표기법 작품이 실제로 언어를 학습에 많은 시간을 투자하지 않으 일부 어답터와 개발자를 위해 너무 많이있을 수 있습니다 방법을 이해와 관련.

그럼에도 불구하고 일단 기본 원리가 이해되면 고정 문자열 조작 방법에 대한이 접근 방식의 힘이 상당히 유리할 수 있습니다.

다르게 생각하는 사람들에게는 람다 함수, 반복자 또는 간단한 일회성 함수 선언과 같은 대체 방법이 있습니다.

원하는 경우 개발자는 자신의 string.reverse () 메서드를 구현할 수 있지만 파이썬의 이러한 측면에 대한 이론적 근거를 이해하는 것이 좋습니다.

또한보십시오


답변

기존 답변은 Unicode Modifiers / grapheme 클러스터가 무시되는 경우에만 정확합니다. 나중에 그 문제를 다룰 것이지만 먼저 몇 가지 반전 알고리즘의 속도를 살펴보십시오.

여기에 이미지 설명을 입력하십시오

list_comprehension  : min:   0.6μs, mean:   0.6μs, max:    2.2μs
reverse_func        : min:   1.9μs, mean:   2.0μs, max:    7.9μs
reverse_reduce      : min:   5.7μs, mean:   5.9μs, max:   10.2μs
reverse_loop        : min:   3.0μs, mean:   3.1μs, max:    6.8μs

여기에 이미지 설명을 입력하십시오

list_comprehension  : min:   4.2μs, mean:   4.5μs, max:   31.7μs
reverse_func        : min:  75.4μs, mean:  76.6μs, max:  109.5μs
reverse_reduce      : min: 749.2μs, mean: 882.4μs, max: 2310.4μs
reverse_loop        : min: 469.7μs, mean: 577.2μs, max: 1227.6μs

리스트 이해 시간이reversed = string[::-1] )이 모든 경우에 가장 낮습니다 (오타를 수정 한 후에도).

문자열 반전

상식적으로 문자열을 반대로 바꾸려면 더 복잡합니다. 예를 들어, 다음 문자열을 사용하십시오 ( 갈색 손가락은 왼쪽 , 노란색 손가락은 위쪽 ). 이것들은 두 개의 grapheme이지만 3 개의 유니 코드 코드 포인트입니다. 추가는 스킨 수정 자 입니다.

example = "???"

하지만 주어진 방법 중 하나 그것을 반대하는 경우, 당신이 얻을 갈색 손가락이 가리키는 , 노란색 손가락으로 왼쪽을 가리키는 . 그 이유는 “갈색”색상 수정자가 여전히 중간에 있고 그 이전의 모든 것에 적용되기 때문입니다. 그래서 우리는

  • U : 손가락 가리키는
  • M : 갈색 수정 제
  • L : 왼쪽을 가리키는 손가락

original: LMU
reversed: UML (above solutions)
reversed: ULM (correct reversal)

Unicode Grapheme Clusters 는 수정 자 코드 포인트보다 조금 더 복잡합니다. 운 좋게도 grapheme 을 처리하기위한 라이브러리가 있습니다 .

>>> import grapheme
>>> g = grapheme.graphemes("???")
>>> list(g)
['??', '?']

따라서 정답은

def reverse_graphemes(string):
    g = list(grapheme.graphemes(string))
    return ''.join(g[::-1])

또한 가장 느립니다.

list_comprehension  : min:    0.5μs, mean:    0.5μs, max:    2.1μs
reverse_func        : min:   68.9μs, mean:   70.3μs, max:  111.4μs
reverse_reduce      : min:  742.7μs, mean:  810.1μs, max: 1821.9μs
reverse_loop        : min:  513.7μs, mean:  552.6μs, max: 1125.8μs
reverse_graphemes   : min: 3882.4μs, mean: 4130.9μs, max: 6416.2μs

코드

#!/usr/bin/env python

import numpy as np
import random
import timeit
from functools import reduce
random.seed(0)


def main():
    longstring = ''.join(random.choices("ABCDEFGHIJKLM", k=2000))
    functions = [(list_comprehension, 'list_comprehension', longstring),
                 (reverse_func, 'reverse_func', longstring),
                 (reverse_reduce, 'reverse_reduce', longstring),
                 (reverse_loop, 'reverse_loop', longstring)
                 ]
    duration_list = {}
    for func, name, params in functions:
        durations = timeit.repeat(lambda: func(params), repeat=100, number=3)
        duration_list[name] = list(np.array(durations) * 1000)
        print('{func:<20}: '
              'min: {min:5.1f}μs, mean: {mean:5.1f}μs, max: {max:6.1f}μs'
              .format(func=name,
                      min=min(durations) * 10**6,
                      mean=np.mean(durations) * 10**6,
                      max=max(durations) * 10**6,
                      ))
        create_boxplot('Reversing a string of length {}'.format(len(longstring)),
                       duration_list)


def list_comprehension(string):
    return string[::-1]


def reverse_func(string):
    return ''.join(reversed(string))


def reverse_reduce(string):
    return reduce(lambda x, y: y + x, string)


def reverse_loop(string):
    reversed_str = ""
    for i in string:
        reversed_str = i + reversed_str
    return reversed_str


def create_boxplot(title, duration_list, showfliers=False):
    import seaborn as sns
    import matplotlib.pyplot as plt
    import operator
    plt.figure(num=None, figsize=(8, 4), dpi=300,
               facecolor='w', edgecolor='k')
    sns.set(style="whitegrid")
    sorted_keys, sorted_vals = zip(*sorted(duration_list.items(),
                                           key=operator.itemgetter(1)))
    flierprops = dict(markerfacecolor='0.75', markersize=1,
                      linestyle='none')
    ax = sns.boxplot(data=sorted_vals, width=.3, orient='h',
                     flierprops=flierprops,
                     showfliers=showfliers)
    ax.set(xlabel="Time in ms", ylabel="")
    plt.yticks(plt.yticks()[0], sorted_keys)
    ax.set_title(title)
    plt.tight_layout()
    plt.savefig("output-string.png")


if __name__ == '__main__':
    main()


답변

1. 슬라이스 표기법 사용

def rev_string(s):
    return s[::-1]

2. reversed () 함수 사용

def rev_string(s):
    return ''.join(reversed(s))

3. 재귀 사용

def rev_string(s):
    if len(s) == 1:
        return s

    return s[-1] + rev_string(s[:-1])


답변

덜 복잡한 방법은 다음과 같습니다.

string = 'happy'
print(string)

‘행복’

string_reversed = string[-1::-1]
print(string_reversed)

‘이파’

영어로 [-1 ::-1]은 다음과 같이 읽습니다.

“-1부터 시작하여 -1 단계까지 진행하십시오.”