파이썬 문자열 비교: __eq__()
, 부등호, 알고리즘, 시간 복잡도, 대소문자 무시, 리스트 비교
이번 포스팅은 파이썬 문자열 비교 와 관련해서 자주 찾는 내용을 정리해보았습니다.
1. __eq__(
) 메서드
__eq__()
메서드는 파이썬의 특수 메서드 중 하나로, 두 객체가 같은지 여부를 비교하는 데 사용됩니다.
이 메서드는 ==
연산자를 사용하여 두 객체를 비교할 때 알아서 호출됩니다.
파이썬 문자열 비교 연산도 ==
연산자를 사용하므로 이 메서드를 호출합니다.
즉, "Hello" == "hello"
를 실행하면 파이썬은 사실 내부적으로 "Hello".__eq__("hello")
를 호출하는 것입니다.
만약, 위 코드처럼 실행하면, 다음과 같이 두 문자열은 다르다는 결과가 나옵니다.
str1 = 'Hello'
str2 = 'hello'
print(str1 == str2) # 출력: False
그 이유는, 파이썬의 문자열을 나타내는 내장 클래스 str
내부에 정의한 __eq__()
메서드가 대소문자를 구분하기 때문입니다.
이처럼 ==
연산자를 사용하는 모든 파이썬 내장 클래스는 __eq__()
메서드를 클래스 내부에 정의했다고 볼 수 있습니다.
우리도 어떤 클래스의 __eq__()
메서드를 새롭게 정의해서 ==
연산자의 동작을 커스텀할 수 있고요.
만약, 이 메서드가 없는 경우, 파이썬은 기본적으로 두 객체의 메모리 주소를 비교합니다.
다음 예제에서는 대소문자를 구분하지 않고 비교하는 새로운 문자열 클래스 CaseInsensitiveStr
를 정의합니다.
__eq__()
메서드가 어떻게 정의되었는지 확인해보세요.
class CaseInsensitiveStr:
def __init__(self, str_):
self.str_ = str_.lower()
def __eq__(self, other):
if isinstance(other, CaseInsensitiveStr):
return self.str_ == other.str_
elif isinstance(other, str):
return self.str_ == other.lower()
return False
str1 = CaseInsensitiveStr('Hello')
str2 = CaseInsensitiveStr('hello')
print(str1 == str2) # 출력: True
2. 문자열 비교 시, 부등호 사용
파이썬에서 문자열을 비교할 때, 부등호는 다음의 2가지 용도로 사용할 수 있습니다.
- 사전식 순서를 나타낸다
- 포함관계를 나타낸다
2.1. 사전식 순서를 나타내는 부등호
첫 번째부터 알아보겠습니다. 파이썬은 영문자 비교를 위해서 ASCII 값, 한글 비교를 위해서는 유니코드 값 (주로, utf-8
) 에 기반합니다.
만약 다음과 같이 알파벳 a
와 b
를 비교한다면, 사실은 이 두 문자의 아스키 값을 비교하는 것입니다.
ord()
함수는 특정 문자의 (아스키코드를 포함한) 유니코드 코드 포인트를 반환하는 내장 함수로 모든 문자의 숫자값을 알 수 있습니다.
print("a" == "b") # 이 비교는 사실
print(ord("a") == ord("a")) # 이 비교입니다.
이와 동일한 원리로, 문자열 비교에 부등호 >
, <
를 사용한다면, 유니코드 코드 포인트의 값을 비교하게 됩니다.
이 값은 알파벳, 한글 둘 다 사전순으로 크기가 커집니다. a
보다 z
가 크고, 대문자보다 소문자가 크며, ㄱ
보다 ㅎ
이 큽니다.
print("a" < "z") # 출력: True
print("A" < "a") # 출력: True
print("강" < "한") # 출력: True
특히 한글은 자모음 하나하나가 유니코드를 갖기도 하고, 조합된 문자 하나가 유니코드를 갖기도 합니다.
즉, 같은 "한"
이라는 글자라도 유니코드 값이 다를 수 있습니다. 이 점 유의해주세요.
ord("ㅎ") # 출력: 12622
ord("한") # 출력: 54620
2.2. 포함 관계를 나타내는 부등호
두 번째 용도는 문자열 간의 포함관계입니다.
파이썬에서 둘 이상의 문자로 이루어진 문자열을 서로 비교하면, 가장 앞 요소부터 차례대로 하나씩 비교하게 됩니다. 만약, 한 문자열이 다른 문자열의 부분 문자열일 경우, 어느 순간 문자와 공백을 비교할 일이 생깁니다. 이 때, 길이가 더 긴 문자열이 큰 것으로 판별됩니다.
코드로 확인해봅니다.
print("나무" > "나") 출력: True
"나"
는 "나무
의 부분 문자열이기 때문에 항상 작은 값으로 판별됩니다.
이 내용을 언급하는 이유는 문자열 포함 관계에 부등호를 사용하라는 것이 아니라,파이썬의 문자열 비교 원리를 알 수 있기 때문입니다.
우리에겐 포함 여부를 판별하는 in
이라는 강력한 키워드가 있습니다.
3. 파이썬의 문자열 비교 알고리즘과 시간복잡도
파이썬 문자열 비교 알고리즘은 직관적입니다. 두 개의 문자열을 비교하기 위해서, 파이썬은 같은 인덱스에 위치한 문자 2개를 앞에서부터 비교합니다.
이 때문에 만약, "안녕하세요" == "안녕하세유"
처럼 최대한의 연산이 필요한 경우에 시간복잡도 O(n)
을 갖습니다.
4. 대소문자를 무시하거나, 무시하지 않는 비교 연산
섹션 1에서 언급했듯이, 파이썬의 str
클래스는 대소문자를 구분하도록 __eq__()
메서드를 구현해놓았습니다.
만약, 두 문자열의 대소문자를 구분하지 않고 비교하고자 하면 str
클래스의 upper()
메서드나 lower()
메서드를 활용할 수 있습니다.
이름 그대로 upper()
메서드는 특정 문자열의 문자를 모두 대문자로,
lower()
메서드는 특정 문자열의 문자를 소문자로 변환한 새 문자열을 반환합니다.
이 때문에, 대소문자를 무시하고 비교하기에 용이합니다.
예제 코드로 확인해보겠습니다.
lower_case = "happy"
mixed_case = "HaPPy"
print(lower_case == mixed_case) # 출력: False
print(lower_case.upper() == mixed_case.upper()) # 출력: True
이렇게 대소문자 구분 없이 파이썬 문자열 비교 결과를 얻을 수 있습니다.
5. 문자열 리스트 비교
파이썬 문자열과 리스트는 서로 데이터 타입이 다릅니다. 그래서, 그대로 비교하면 언제나 False
값이 나옵니다.
print(["a", "b", "c"] == "abc") # 출력: False
두 데이터 타입의 내용이 일치하는지 비교하기 위해서는 데이터 타입을 일치시켜줘야 합니다. 이를 위해, 다음 2가지 방법을 사용할 수 있습니다.
5.1. 문자열을 리스트로 변환하는 list() 함수
첫 번째 방법은 문자열을 리스트로 변환합니다. 파이썬의 list()
함수는 다양한 데이터 타입을 리스트로 변환하는 간편한 함수입니다.
다음과 같이 사용합니다.
print(list("abc")) # 출력: ["a", "b", "c"]
print(["a", "b", "c"] == list("abc")) # 출력: True
데이터 타입이 같아졌기 때문에, 두 리스트의 요소를 비교해서 True
값을 반환했습니다.
5.2. 리스트를 문자열로 변환하는 join() 메서드
또 다른 방법으로, 리스트를 문자열로 변환한 후 비교합니다.
이를 위해서는, join()
메서드를 사용합니다. 이 메서드는 리스트의 모든 요소를 하나의 문자열로 합칩니다.
print("".join(["a", "b", "c"])) # 출력: "abc"
print("".join(["a", "b", "c"]) == "abc") # 출력: True
같은 문자열 타입이 되어서, 내용을 비교할 수 있었습니다.
6. 마치며
지금까지 파이썬 문자열 비교 관련 자주 찾는 내용 5가지를 살펴보았습니다. 이 포스팅을 통해 궁금한 부분을 해결하길 바랍니다.