파이썬 프로그래밍 - 컨테이너 유형 및 사용자 정의 유형 객체 정렬의 사용자 정의 시뮬레이션
Emulating container types컨테이너 타입 시뮬레이션
최근 캐시 도구의 소스 코드를 읽다 보면 프로토콜의 캐시 구현에서 다음과 같은 getitem과 setitem 메서드를 자주 접하게 됩니다:
def __getitem__(self, key):
try:
return self.__data[key]
except KeyError:
return self.__missing__(key)
def __setitem__(self, key, value):
maxsize = self.__maxsize
size = self.getsizeof(value)
if size > maxsize:
raise ValueError("value too large")
if key not in self.__data or self.__size[key] < size:
while self.__currsize + size > maxsize:
self.popitem()
if key in self.__data:
diffsize = size - self.__size[key]
else:
diffsize = size
self.__data[key] = value
self.__size[key] = size
self.__currsize += diffsize
이 두 가지 방법은 실제로 컨테이너를 시뮬레이션하는 기본 방법을 구현하며, 공식 웹사이트에서 확인할 수 있습니다:
Emulating container types
컨테이너 객체를 구현하기 위해 다음 메서드를 정의할 수 있습니다. 컨테이너는 일반적으로 sequences 또는 mappings 다른 컨테이너도 나타낼 수 있습니다. 첫 번째 메서드 세트는 시퀀스를 에뮬레이션하거나 매핑을 에뮬레이션하는 데 사용되며, 차이점은 시퀀스의 경우 허용되는 키가 는 시퀀스의 길이가 0 <= k < N 인 정수이거나 항목의 범위를 정의하는 slice 객체여야 한다는 점입니다. 또한 매핑은 메서드 키를 제공하는 것이 좋습니다. 매핑은 파이썬의 표준 메서드와 유사하게 동작하는 keys(), values(), items(), get(), clear(), setdefault(), pop(), popitem(), copy() 및 update() 메서드를 제공하는 것이 좋습니다. 의 표준 dictionary 객체와 유사하게 동작합니다. collections.abc 모듈은 MutableMapping abstract base class 제공하여 다음과 같이 도와줍니다. collections.abc 모듈은 __getitem__, __delitem__의 기본 집합에서 이러한 메서드를 생성하는 데 도움이 되는 MutableMapping 추상 베이스 클래스를 제공합니다 __delitem__. Mutable sequences should provide methods append(), count(), index(), extend(), insert(), pop(), remove(), reverse() and sort(), like Python standard 마지막으로 시퀀스 타입은 아래에 설명된 __add__, __iadd__, __rmul__ 메서드를 정의하여 덧셈과 곱셈을 구현해야 하며, 다른 __add__, __iadd__, __rmul__ __iadd__ 메서드를 정의해서는 안 됩니다. 다른 숫자 연산자를 정의해서는 안 됩니다. 매핑과 시퀀스 모두 __contains__ 메서드를 구현하여 __contains__ 하는 것이 좋습니다. 매핑의 경우 __iter__()는 객체의 키를 반복해야 하고, 시퀀스의 경우 __contains__ 메서드를 구현하여 컨테이너를 통한 효율적인 반복을 허용해야 합니다. 시퀀스의 경우 값을 반복해야 합니다.
출처: 파이썬 공식 문서 docs.python.org/3/reference...
다음에는 중복 값의 사전을 허용하지 않는 모의 컨테이너를 만들고 길이 가져오기 함수를 구현하는 예제로 일반적인 기능의 구현을 작성해 보겠습니다. 사전의 사용은 데모의 편의를위한 것이며 정수 키 유형의 사용을 권장하지 않으며 여기서 정수를 사용하는 이유는 키를 허용하는 컨테이너 유형을 시뮬레이션하는 것이 정수에 가장 적합하며 데모의 하위 부분을 위해 인터페이스 구현을 소개하지 않았습니다 collections.abc.MutableMapping, 기사 끝에는 코드의 인터페이스의 전체 구현이 수반됩니다.
setitem 메서드는 키-값 대응을 생성합니다.
__setitem__ 메서드는 일반적으로 두 개의 인수를 받는데, 하나는 키로, 다른 하나는 값으로 사용됩니다. 이 메서드를 구현하는 인스턴스 객체는 [키]=값을 사용하여 해당 값을 생성할 수 있습니다.
class MySelfDefineDict:
def __init__(self) -> None:
self.length: int = 0
self.data: dict[int, str] = {}
def __setitem__(self, key: int, value: str) -> None:
if value not in self.data.values():
self.data[key] = value
self.length += 1 # 다음은 길이 측정을 구현하는 편리한 방법입니다.
print("Added a set of key-value pairs")
else:
raise Exception("The value is exist") # 실제로는 예외를 사용자 정의하면 됩니다.
if __name__ == "__main__":
my_dict: MySelfDefineDict = MySelfDefineDict()
my_dict[1] = "233"
"""
: Added a set of key-value pairs
"""
getitem 메서드는 키의 값 내용을 가져옵니다.
__getitem__ 메서드는 키에 해당하는 값의 내용을 가져오는 데 사용되며, 일반적으로 위에 추가된 키 값이라는 인자 하나만 받습니다:
def __getitem__(self, key: int) -> str:
return self.data.get(key, "This key does not exist")
실행하면 다음과 같은 결과를 얻을 수 있습니다:
if __name__ == "__main__":
my_dict: MySelfDefineDict = MySelfDefineDict()
my_dict[1] = "233"
print(my_dict[1])
print(my_dict[3])
"""
:
Added a set of key-value pairs
233
This key does not exist
"""
delitem 메서드는 해당 키-값 쌍을 제거합니다.
델리템__은 삭제할 키를 지정하는 인수로 키를 사용할 수도 있습니다:
def __delitem__(self, key: int) -> None:
if key in self.data:
del self.data[key]
self.length -= 1
print("The key value was removed successfully")
else:
raise Exception("The value is exist")
결과와 함께 코드를 실행합니다:
if __name__ == "__main__":
my_dict: MySelfDefineDict = MySelfDefineDict()
my_dict[1] = "233"
del my_dict[1]
"""
:
Added a set of key-value pairs
The key value was removed successfully
"""
len메서드를 실행하여 길이를 가져올 수 있습니다.
렌()의 길이를 구하기 위해 __len__ 메서드를 구현할 수 있으며, 이는 비교적 간단하고 직접 첨부된 코드입니다:
def __len__(self) -> int:
return self.length
결과와 함께 코드를 실행합니다:
if __name__ == "__main__":
my_dict: MySelfDefineDict = MySelfDefineDict()
my_dict[1] = "233"
my_dict[2] = "2333"
del my_dict[1]
print(len(my_dict))
"""
:
Added a set of key-value pairs
Added a set of key-value pairs
The key value was removed successfully
1
"""
전체 구현collections.abc.MutableMapping
인터페이스를 구현하려면 위와 더불어 클래스 인스턴스가 이터러블 메서드 __iter__를 구현해야 하는데, 이는 이터레이터 프로토콜의 구현이 아니며 여기서는 사전 값을 직접 반복에 사용하고 있습니다:
def __iter__(self) -> list[str]:
return iter(self.data.values())
코드를 실행하여 가져옵니다:
if __name__ == "__main__":
my_dict: MySelfDefineDict = MySelfDefineDict()
my_dict[1] = "233"
my_dict[2] = "2333"
my_dict[3] = "23333"
my_dict[4] = "233333"
del my_dict[1]
print(len(my_dict))
for element in my_dict:
print(element, end=" ")
"""
:
Added a set of key-value pairs
Added a set of key-value pairs
Added a set of key-value pairs
Added a set of key-value pairs
The key value was removed successfully
3
2333 23333 233333
"""
전체 구현 코드는 아래와 같습니다:
import collections
class MySelfDefineDict(collections.abc.MutableMapping):
def __init__(self) -> None:
self.length: int = 0
self.data: dict[int, str] = {}
def __setitem__(self, key: int, value: str) -> None:
if value not in self.data.values():
self.data[key] = value
self.length += 1
print("Added a set of key-value pairs")
else:
raise Exception("The value is exist")
def __getitem__(self, key: int) -> str:
return self.data.get(key, "This key does not exist")
def __delitem__(self, key: int) -> None:
if key in self.data:
del self.data[key]
self.length -= 1
print("The key value was removed successfully")
else:
raise Exception("The value is exist")
def __len__(self) -> int:
return self.length
def __iter__(self) -> list[str]:
return iter(self.data.values())
사용자 지정 유형 개체 정렬
위의 내용을 보면 모의 컨테이너 형을 구현하는 것은 여전히 본질적으로 구현의 해당 프로토콜이며, 프로토콜이 충족되기만 하면 모의 컨테이너 형이고, 본질적으로 동일한지 여부는 중요하지 않으며, 파이썬에서 중요한 것은 형이 아니라 객체의 동작이라는 것을 쉽게 알 수 있습니다. 어떤 객체가 오리처럼 걷고, 헤엄치고, 꽥꽥거린다면 그 객체는 오리입니다. 즉, 객체의 동작을 통해 객체가 인터페이스를 준수하는지 여부를 알 수 있습니다. 따라서 사용자 정의 유형은 사용자 정의 유형의 객체 정렬을 구현하는 것과 같이 원하는 효과를 얻기 위해 다양한 프로토콜을 구현할 수 있을 뿐만 아니라 다음과 같은 기본 메서드를 구현할 수 있습니다.
정렬을 구현하는 기본 방법
식별자가 다른 클래스의 인스턴스를 비교하면 클래스가 __eq__ () 메서드를 정의하지 않는 한 일반적으로 불평등이 발생합니다.
클래스의 인스턴스는 __lt__, __gt__ 및 __eq__를 포함하여 충분히 많은 수의 메서드를 정의하도록 클래스가 정의되지 않는 한 동일한 클래스의 다른 인스턴스 또는 다른 유형의 객체와 함께 정렬할 수 없습니다 .
공식 파이썬 문서: docs.python.org/3/reference..._
| 매직 메소드 | 연산자(컴퓨팅) | 행동 |
|---|---|---|
__lt__(self, other) | < | 운영자 미달의 행동 |
__le__(self, other) | <= | 같지 않은 연산자의 동작 |
__eq__(self, other) | == | 등호 연산자의 동작 |
__ne__(self, other) | ! = | 연산자와 같지 않은 연산자의 동작 |
__gt__(self, other) | > | 보다 큰 연산자의 동작 |
__ge__(self, other) | >= | 등호 연산자보다 큰 연산자의 동작 |
정렬 예제
Person 클래스를 정의하고 보다 큼, 보다 작음, 보다 같음 매직 메서드를 구현합니다:
from typing import Self
class Person:
def __init__(self, name: str, age: int) -> None:
self.name: str = name
self.age: int = age
# 보다 작은 연산자 정의하기
def __lt__(self, other: Self) -> None:
return self.age < other.age
# 보다 작은 연산자 정의하기
def __le__(self, other: Self) -> None:
return self.age <= other.age
# 같음 연산자 정의하기
def __eq__(self, other: Self) -> None:
return self.age == other.age
검증을 실행할 메인 프로그램을 작성합니다:
if __name__ == "__main__":
# 일부 Person 객체 만들기
person1: Person = Person("Alice", 25)
person2: Person = Person("Bob", 30)
person3: Person = Person("Charlie", 20)
person4: Person = Person("Frank", 20)
# 정렬 함수를 사용하여 객체 목록 정렬하기
people: list[Person] = [person1, person2, person3, person4]
sorted_people: list[Person] = sorted(people)
# 정렬된 결과 출력
for person in sorted_people:
print(person.name, person.age, " ", end='')
실행 결과를 가져옵니다:
Charlie 20 Frank 20 Alice 25 Bob 30
출력에 인간적인 느낌을 더하려면 __repr__ 메서드를 작성할 수도 있습니다.
__repr__은 프로그램을 수정할 수 있는 객체의 문자열 표현을 정의하기 위한 파이썬의 특별한 마법 메서드입니다:
def __repr__(self) -> str:
return f"The age of {self.name} is {self.age}"
for person in sorted_people:
print(person)
실행하여 가져옵니다:
The age of Charlie is 20
The age of Frank is 20
The age of Alice is 25
The age of Bob is 30



