파이썬의 제네릭/템플릿?
파이썬은 일반/템플릿 유형 시나리오를 어떻게 처리합니까?예를 들어 외부 파일 "BinaryTree.py "을 만들고 이진 트리를 처리하도록 하고 싶지만 모든 데이터 유형에 대해 처리합니다.
그래서 저는 그것을 사용자 정의 개체의 유형으로 전달하고 그 개체의 이진 트리를 가질 수 있었습니다.파이썬에서 이것은 어떻게 됩니까?
다른 답변은 전혀 문제가 없습니다.
- 파이썬에서 제네릭을 지원하기 위해 특별한 구문이 필요하지 않습니다.
- Python은 André가 지적한 바와 같이 오리 타이핑을 사용합니다.
그러나 여전히 유형화된 변형을 원한다면 Python 3.5 이후의 내장 솔루션이 있습니다.
사용 가능한 유형 주석의 전체 목록은 Python 설명서에서 사용할 수 있습니다.
일반 클래스:
from typing import TypeVar, Generic, List
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self) -> None:
# Create an empty list with items of type T
self.items: List[T] = []
def push(self, item: T) -> None:
self.items.append(item)
def pop(self) -> T:
return self.items.pop()
def empty(self) -> bool:
return not self.items
# Construct an empty Stack[int] instance
stack = Stack[int]()
stack.push(2)
stack.pop()
stack.push('x') # Type error
일반 기능:
from typing import TypeVar, Sequence
T = TypeVar('T') # Declare type variable
def first(seq: Sequence[T]) -> T:
return seq[0]
def last(seq: Sequence[T]) -> T:
return seq[-1]
n = first([1, 2, 3]) # n has type int.
정적 유형 확인:
소스 코드를 분석하려면 mypy 또는 Pyre(Meta/FB에서 개발)와 같은 정적 유형 검사기를 사용해야 합니다.
mypy 설치:
python3 -m pip install mypy
소스 코드(예: 특정 파일)를 분석합니다.
mypy foo.py
또는 디렉토리:
mypy some_directory
mypy는 유형 오류를 감지하고 인쇄할 것입니다.위에 제공된 스택 예제의 구체적인 출력:
foo.py:23: error: Argument 1 to "push" of "Stack" has incompatible type "str"; expected "int"
참고 자료: 제네릭 및 mypy 실행에 대한 mypy 문서
Python은 오리 타이핑을 사용하기 때문에 여러 유형을 처리하는 데 특별한 구문이 필요하지 않습니다.
C++ 배경을 가진 사용자라면 템플릿 함수/클래스에 사용되는 연산이 특정 유형에 정의되어 있는 한 이를 기억할 것입니다.T
에서) 을 사용할 수 .T
템플릿에 있습니다.
기본적으로 동일한 방식으로 작동합니다.
- 이진 트리에 삽입할 항목 유형에 대한 계약을 정의합니다.
- 본 계약을 문서화합니다(즉, 클래스 문서).
- 계약서에 지정된 작업만 사용하여 이진 트리 구현
- 즐거운 시간 되세요.
그러나 명시적인 유형 검사를 작성하지 않는 한(일반적으로 권장되지 않음) 이진 트리에 선택한 유형의 요소만 포함되도록 강제할 수 없습니다.
실제로 이제 Python 3.5+에서 제네릭을 사용할 수 있습니다.PEP-484 및 입력 모듈 설명서를 참조하십시오.
제 관행에 따르면, 특히 Java Generics에 익숙한 사용자에게는 원활하고 명확하지 않지만 여전히 사용할 수 있습니다.
파이썬에서 제네릭 타입을 만드는 것에 대한 좋은 생각을 떠올린 후, 저는 같은 아이디어를 가진 다른 사람들을 찾기 시작했지만 찾을 수 없었습니다.자, 여기 있습니다.제가 이것을 사용해 봤는데 잘 작동합니다.파이썬에서 유형을 매개 변수화할 수 있습니다.
class List( type ):
def __new__(type_ref, member_type):
class List(list):
def append(self, member):
if not isinstance(member, member_type):
raise TypeError('Attempted to append a "{0}" to a "{1}" which only takes a "{2}"'.format(
type(member).__name__,
type(self).__name__,
member_type.__name__
))
list.append(self, member)
return List
이제 이 제네릭 형식에서 형식을 파생할 수 있습니다.
class TestMember:
pass
class TestList(List(TestMember)):
def __init__(self):
super().__init__()
test_list = TestList()
test_list.append(TestMember())
test_list.append('test') # This line will raise an exception
이 솔루션은 단순하고 한계가 있습니다.제네릭 형식을 만들 때마다 새 형식이 만들어집니다. 따서클, 여래스다상다음니속합을 상속받는 다중 입니다.List( str )
부모가 두 개의 분리된 클래스에서 상속받는 것처럼.이를 극복하기 위해서는 새로운 것을 만드는 것이 아니라 내부 클래스의 다양한 형태를 저장하고 이전에 만들어진 내부 클래스를 반환하는 딕트를 만들어야 합니다.이렇게 하면 매개 변수가 동일한 중복 유형이 생성되지 않습니다.관심이 있는 경우 장식가 및/또는 메타 클래스를 사용하여 보다 우아한 솔루션을 만들 수 있습니다.
다음은 메타 클래스를 사용하여 지저분한 구문을 피하고 다음을 사용하는 이 답변의 변형입니다.typing
-스타일의List[int]
구문:
class template(type):
def __new__(metacls, f):
cls = type.__new__(metacls, f.__name__, (), {
'_f': f,
'__qualname__': f.__qualname__,
'__module__': f.__module__,
'__doc__': f.__doc__
})
cls.__instances = {}
return cls
def __init__(cls, f): # only needed in 3.5 and below
pass
def __getitem__(cls, item):
if not isinstance(item, tuple):
item = (item,)
try:
return cls.__instances[item]
except KeyError:
cls.__instances[item] = c = cls._f(*item)
item_repr = '[' + ', '.join(repr(i) for i in item) + ']'
c.__name__ = cls.__name__ + item_repr
c.__qualname__ = cls.__qualname__ + item_repr
c.__template__ = cls
return c
def __subclasscheck__(cls, subclass):
for c in subclass.mro():
if getattr(c, '__template__', None) == cls:
return True
return False
def __instancecheck__(cls, instance):
return cls.__subclasscheck__(type(instance))
def __repr__(cls):
import inspect
return '<template {!r}>'.format('{}.{}[{}]'.format(
cls.__module__, cls.__qualname__, str(inspect.signature(cls._f))[1:-1]
))
이 새로운 메타클래스를 사용하면 링크에 연결된 답변의 예제를 다음과 같이 다시 작성할 수 있습니다.
@template
def List(member_type):
class List(list):
def append(self, member):
if not isinstance(member, member_type):
raise TypeError('Attempted to append a "{0}" to a "{1}" which only takes a "{2}"'.format(
type(member).__name__,
type(self).__name__,
member_type.__name__
))
list.append(self, member)
return List
l = List[int]()
l.append(1) # ok
l.append("one") # error
이 접근 방식에는 몇 가지 좋은 이점이 있습니다.
print(List) # <template '__main__.List[member_type]'>
print(List[int]) # <class '__main__.List[<class 'int'>, 10]'>
assert List[int] is List[int]
assert issubclass(List[int], List) # True
파이썬은 동적으로 입력되기 때문에 이것은 매우 쉽습니다.실제로 BinaryTree 클래스가 데이터 유형과 함께 작동하지 않도록 하려면 추가 작업을 수행해야 합니다.
예를 들어, 다음과 같은 방법으로 객체 내에서 객체를 트리에 배치하는 데 사용되는 키 값을 원하는 경우key()
전화만 하면 됩니다.key()
를 들면 다음과 예:
class BinaryTree(object):
def insert(self, object_to_insert):
key = object_to_insert.key()
가 .object_to_insert인 클래스의.▁a만 있으면 .key()
방법, 효과가 있을 것입니다.
문자열이나 정수와 같은 기본 데이터 유형과 함께 사용하려는 경우에는 예외입니다.일반적인 이진 트리와 함께 작동하려면 클래스로 래핑해야 합니다.만약 그것이 너무 무겁게 들리고 실제로 문자열을 저장하는 추가적인 효율성을 원한다면, 미안하지만, 그것은 Python이 잘하는 것이 아닙니다.
Python 2를 사용하거나 Java 코드를 다시 작성하고자 하는 경우.그들의 것은 이것에 대한 진정한 해결책이 아닙니다.제가 밤에 하는 일은 다음과 같습니다: https://github.com/FlorianSteenbuck/python-generics 저는 아직 컴파일러를 받지 못해서 당신은 현재 그렇게 사용하고 있습니다.
class A(GenericObject):
def __init__(self, *args, **kwargs):
GenericObject.__init__(self, [
['b',extends,int],
['a',extends,str],
[0,extends,bool],
['T',extends,float]
], *args, **kwargs)
def _init(self, c, a, b):
print "success c="+str(c)+" a="+str(a)+" b="+str(b)
작업관리
- 컴파일러
- Class working 및 유형 작업)
<? extends List<Number>>
) - 더하다
super
support - 더하다
?
support - 코드 정리
내장된 컨테이너가 어떻게 작동하는지 보세요. dict
그리고.list
등에는 원하는 유형의 이기종 요소가 포함되어 있습니다.예를 들어, 다음과 같이 정의하면insert(val)
당신의 나무에 대한 기능, 그것은 어느 시점에서 다음과 같은 것을 할 것입니다.node.value = val
나머지는 파이썬이 처리할 겁니다
다행히 파이썬에서 일반 프로그래밍을 위한 노력이 있었습니다.라이브러리가 있습니다: 일반
http://generic.readthedocs.org/en/latest/ 에 대한 설명서가 있습니다.
그것은 수년간 발전하지 않았지만, 여러분은 어떻게 도서관을 사용하고 만들 것인지 대략적인 생각을 할 수 있습니다.
건배.
언급URL : https://stackoverflow.com/questions/6725868/generics-templates-in-python
'programing' 카테고리의 다른 글
SQL 쿼리에 대한 기본 스키마 설정 (0) | 2023.06.25 |
---|---|
pthread_create 및 마지막 인수로 정수 전달 (0) | 2023.06.20 |
3항 연산자가 R에 존재합니까? (0) | 2023.06.20 |
SpringBoot 1.4에서 SpringMVC 슬라이스 테스트 문제 (0) | 2023.06.20 |
유형 스크립트의 정적 메서드에서 클래스 유형 인수에 액세스하기 위한 해결 방법 (0) | 2023.06.20 |