Python에서 문자열을 비교하는 방법: 동일성과 동등성

2022. 11. 28. 19:24프로그래밍

728x90

 

 

How to Compare Strings in Python: Equality and Identity | The Renegade Coder

Once again, we're back with another Python topic. Today, we'll talk about how to compare strings in Python. Typically, I try to stay away from…

therenegadecoder.com

다시 한 번 또 다른 Python 주제로 돌아왔습니다.

오늘은 Python에서 문자열을 비교하는 방법을 이야기 해보겠습니다. 일반적으로는 문자열의 복잡성 때문에(예: 다른 언어, 구현 등) 문자열(비교)을 멀리하려고 합니다. 그렇지만, 오늘은 이런 위험을 감수하기로 결정했습니다. 이 글이 도움이 되길 바랄께요!

 

약간의 맛보기로 이 기사에서 기대할 수 있는 내용은 다음과 같습니다.

우리는 is와 마찬가지로 ==, <, <=, >= 및 >를 포함하여 파이썬에서 몇 가지 다른 비교 연산자를 살펴볼 것입니다.

또한 이러한 연산자를 사용하여 문자열을 비교하는 방법과 언제 사용하는 지에 대해 설명 할 것입니다.

좀 더 알고 싶다면 계속 읽어 주세요.

 

 

문제 설명

우리가 간단한 검색 엔진을 구축한다고 상상해 봅시다. 예를 들어 텍스트가 포함된 여러 파일에서 해당 문서의 특정 키워드를 검색 가능하도록 하고 싶습니다. 어떻게 할 수 있을까요?

 

이 검색 엔진의 핵심으로 우리는 문자열을 비교해야만 할 것입니다. 예를 들어, 시스템에서 Pittsburgh Penguins(예: Sidney Crosby)에 대한 뭔가를 검색하는 경우에 이 키워드가 포함된 문서를 찾아주어야 할 것입니다. 물론 일치 부분을 가지고 있는 지 여부를 확일 할 수 있는 방법까지 말입니다.

 

특히 우리는 두 문자열이 동일한지 비교하는 법을 알고 싶어 합니다. 예를 들어, "Sidney Crosby"는 "Sidney Crosby"와 같은 단어인가? 그럼 "sidney crosby"는 어떨까요? 아니면 "SiDnEy CrosBy"라면? 그래서, 파이썬에서 이 동일성을 (비교하는 것을) 포함하는 것은 무얼까요?

 

물론 등식만이 문자열을 비교하는 유일한 방법은 아닙니다. 예를 들어 문자열을 알파벳순/사전순으로 어떻게 비교할 수 있을까요? "Malkin"은 리스트에서 "Letang" 앞에 또는 뒤에 올까요?

 

이러한 주제 중 하나라도 흥미롭게 들린다면 당신은 운이 좋은 것입니다.

우리는 이 기사에서 모든 것을 다룰 것입니다.

 

솔루션

이 섹션에서는 문자열을 비교하는 몇 가지 다른 방법을 살펴볼 것입니다. 먼저 각 문자를 반복하여 일치하는지 확인하는 무차별 대입(brute force) 솔루션을 살펴 볼 것이며, 다음으로 무차별 대입(brute force) 솔루션을 추상화하는 비교 연산자를 소개 할 것입니다. 마지막으로 동등성에 대해 살펴 볼 것입니다.

Brute Force로 문자열 비교하기

문자열은 반복적이므로, 각 문자를 비교하기 위해 순환문을 작성하는 것은 어쩔 수가 없습니다.

penguins_87 = "Crosby"
penguins_71 = "Malkin"
is_same_player = True
for a, b in zip(penguins_87, penguins_71):
  if a != b:
    is_same_player = False
    break
 

이 예제에서는 두 문자열을 zip으로 묶고 일치하는 항목을 찾지 못할 때까지 각 문자 쌍을 반복 순환합니다.

우리는 순환문이 끝나기 전에 break 한다면, 일치하는 부분을 찾지 못한다는 것을 알 수 있습니다.

아니면 문자열이 "동일"합니다.

이렇게 하면 일부 문자열에 대한 동작하겠지만 특정 시나리오에서는 실패할 수 있습니다. 예를 들어 문자열 중 하나가 다른 것보다 길면 어떻게 될까요?

penguins_87 = "Crosby"
penguins_71 = "Malkin"
penguins_59 = "Guentzel"
 

결과적으로, zip()은 실제로 더 긴 문자열을 잘라버릴 것입니다. 이를 처리하기 위해 먼저 길이 확인을 고려할 수 있을 것입니다:

penguins_87 = "Crosby"
penguins_71 = "Malkin"
penguins_59 = "Guentzel"
is_same_player = len(penguins_87) == len(penguins_59)
if is_same_player:
  for a, b in zip(penguins_87, penguins_59):
    if a != b:
      is_same_player = False
      break
 

물론 추가 검사를 하더라도 이 솔루션은 약간 과도하고 오류가 발생하기 쉽습니다. 게다가 솔루션은 동일한 것인지만을 위해서만 작동합니다. 문자열이 사전순으로 다른 문자열보다 "작은"지 어떻게 확인할까요? 운 좋게도 아래에 또 다른 솔루션이 존재합니다.

 

비교 연산자로 문자열 비교

재미있는 사실: 문자열을 비교하기 위해 자체 문자열 동일성 코드를 작성할 필요가 없습니다. 결과적으로 몇 가지 핵심 연산자 ==, <, <=, >=, >와 같이 기본적으로 문자열과 함께 사용할 수 있습니다.

위의 Penguins 플레이어를 사용하여 직접 비교할 수 있습니다.

penguins_87 = "Crosby"
penguins_71 = "Malkin"
penguins_59 = "Guentzel"
 
penguins_87 == penguins_87  # True
penguins_87 == penguins_71  # False
penguins_87 >= penguins_71  # False
penguins_59 <= penguins_71  # True
 

이제 이러한 비교 연산자가 각 문자의 ASCII 표현 상에서 동작한다는 점에 유의하는 것이 중요합니다. 결과적으로 겉보기에 같은 문자열이지만 (결과는) 같다고 나오지 않을 수 있습니다:

penguins_87 = "Crosby"
penguins_87_small = "crosby"
 
penguins_87 == penguins_87_small  # False
 

"Crosby"와 "crosby"를 비교할 때 "c"와 "C"가 같지 않기 때문에 False가 되어버립니다.

ord('c')  # 99
ord('C')  # 67
 

 

당연히 이로 인해 이상한 동작이 발생할 수 있습니다. 예를 들어 "crosby"가 알파벳순으로 "Malkin"보다 앞에 오기 때문에 "crosby"가 "Malkin"보다 작다고 말할 수 있습니다. 불행하게도, 이 표현에 대한 파이썬 해석이 아닙니다:

penguins_87_small = "crosby"
penguins_71 = "Malkin"
 
penguins_87_small < penguins_71  # False
 

즉, 이러한 비교 연산자는 편리하지만 실제로는 대소문자를 구분하지 않는 비교를 수행하지 않습니다. 운 좋게도 두 문자열을 대문자 또는 소문자로 변환하는 것은 같이 사용할 수 있는  편법이 될 것입니다.

penguins_87_small = "crosby"
penguins_71 = "Malkin"
 
penguins_87_small.lower() < penguins_71.lower()
penguins_87_small.upper() < penguins_71.upper()
 

Python의 문자열은 대부분의 언어와 마찬가지로 변경할 수 없기 때문에 이러한 메서드는 실제로 내부 문자열을 변경(조작)하지 않습니다. 대신 새 객체를 반환합니다.

문자열은 본질적으로 복잡합니다. 이 문서의 솔루션이 예상대로 작동하지 않는 극단적인 경우가 있기 때문에 약간의 경고 드립니다. 결국 우리는 ASCII 문자로 이 기능을 겉핡기로만 해보았습니다. 영어 문자가 포함되지 않은 문자열(예: 🤐, 汉 등)을 가지고 놀아 보세요. 아마 해당 결과에 놀랄 수도 있을 것입니다.

 

동등성(Identity)으 로 문자열 비교

계속 진행하기 전에 문자열을 비교하는 또 다른 방법인 동등성(identity)도 중요하다고 생각 됩니다.

파이썬에서 ==는 객체를 비교하는 유일한 방법이 아닙니다.

우리는 또 is를 사용할 수 있습니다.

한번 볼까요:

 

penguins_87 = "Crosby"
penguins_71 = "Malkin"
penguins_59 = "Guentzel"
 
penguins_87 is penguins_87  # True
penguins_87 is penguins_71  # False
 

여기에서 이 솔루션과 이전 솔루션 간에 어떤 종류의 차이점이 있다고 보기는 어렵습니다. 결국 출력은 동일합니다. 그렇지만, 여기에 근본적인 차이가 있습니다. 같음(==)을 사용하여 문자열을 내용별로 비교하지만(즉, 문자별로). identity(is)를 사용하여 메모리의 위치(예: 주소/참조)로 문자열을 비교합니다.

이를 실제로 확인하기 위해 몇 가지 같은 문자열을 만들어 보겠습니다.

 

penguins_87 = "Crosby"
penguins_87_copy = "Crosby"
penguins_87_clone = "Cros" + "by"
penguins_8 = "Cros"
penguins_7 = "by"
penguins_87_dupe = penguins_8 + penguins_7
 
id(penguins_87)        # 65564544
id(penguins_87_copy)   # 65564544
id(penguins_87_clone)  # 65564544
id(penguins_87_dupe)   # 65639392 Uh Oh!
 

처음 세 가지 예에서 Python 인터프리터는 해당 문자열들이 동등성을 가진다는 것을 알 수 있었고 인터프리터는 두 복제본을 위한 공간을 만들지 않았습니다. 대신 후자의 두 penguins_87_copy와 penguins_87_clone에 동일한 ID를 부여했습니다. 결과적으로 처음 세 문자열 중 하나를 == 또는 is와 비교하면 동일한 결과를 얻을 수 있습니다:

penguins_87 == penguins_87_copy == penguins_87_clone  # True
penguins_87 is penguins_87_copy is penguins_87_clone  # True
 

마지막 문자열 penguins_87_dupe에서는 약간의 문제가 발생합니다. 내가 아는 한, 인터프리터는 런타임 전에는 표현식의 값이 무엇인지 알 수 없습니다. 결과적으로 "Crosby"가 이미 존재한다는 사실에도 불구하고 결과 문자열에 대한 새 위치를 생성합니다. 위에서 비교 체인을 수정하면 우리는 다른 결과를 얻을 것입니다:

penguins_87 == penguins_87_copy == penguins_87_clone == penguins_87_dupe # True
penguins_87 is penguins_87_copy is penguins_87_clone is penguins_87_dupe # False
 

 

여기서 중요한 점은 문자열이 같은지 비교할 때 ==만 사용한다는 것입니다(해당 문제에 대한 모든 객체). 결국 Python 인터프리터가 동등한 문자열을 올바르게 식별하고 동일한 ID를 제공한다는 보장이 없습니다. 즉, ID에 대해 두 문자열을 비교해야 하는 경우 이 방법을 사용합니다.

도전과제

일반적으로 각 솔루션별 성능을 확인해야 하지만 모두 비슷하지는 않습니다. 대신 도전과제에 뛰어 들 준비는 되었다고 봅니다.

이제 Python에서 문자열을 비교하는 방법을 알았으므로 그 지식을 사용하여 간단한 문자열 정렬 알고리즘을 작성할 수 있다고 생각합니다. 이 도전과제에서는 ASCII 문자열과 대소문자 구분을 가정할 수 있습니다. 그러나 필요에 따라 솔루션을 자유롭게 최적화할 수 있습니다. 내가 관심을 갖는 것은 이 기사에서 논의된 연산자를 사용하는 것입니다.

시작을 위해 샘플 목록이 필요한 경우 다음은 Pittsburgh Penguins의 현재 포워드 명단입니다(알파벳 역순으로 정렬됨):

penguins_2019_2020 = [
  'Tanev',
  'Simon',
  'Rust',
  'McCann',
  'Malkin',
  'Lafferty',
  'Kahun',
  'Hornqvist',
  'Guentzel',
  'Galchenyuk',
  'Di Pauli',
  'Crosby',
  'Blueger',
  'Blandisi',
  'Bjugstad',
  'Aston-Reese'
]
 

완료되면 아래 의견란에 솔루션을 등록해 주십시오. 그런 다음 Python에서 문자열 목록을 정렬하는 방법이라는 제목의 기사로 이동하여 몇 가지 영리한 솔루션을 확인하십시오.

약간의 요약

이것으로 모두 끝났습니다. 전체 솔루션을 아래에서 확인 해 보세요.

penguins_87 = "Crosby"
penguins_71 = "Malkin"
penguins_59 = "Guentzel"
 
# Brute force comparison (equality only)
is_same_player = len(penguins_87) == len(penguins_59)
if is_same_player:
  for a, b in zip(penguins_87, penguins_59):
    if a != b:
      is_same_player = False
      break
 
# Direct comparison
penguins_87 == penguins_59  # False
penguins_87 > penguins_59  # False
penguins_71 <= penguins_71  # True
 
# Identity checking
penguins_87 is penguins_87  # True
penguins_71 is penguins_87  # False
 

이상.

 

728x90