파이썬은 제네릭 / 템플릿 유형 시나리오를 어떻게 처리합니까? "BinaryTree.py"외부 파일을 만들고 이진 트리를 처리하도록하고 싶지만 모든 데이터 유형에 대해 사용하고 싶다고 가정 해 보겠습니다.
그래서 사용자 지정 개체의 유형을 전달하고 해당 개체의 이진 트리를 가질 수 있습니다. 이것은 파이썬에서 어떻게 이루어 집니까?
답변:
파이썬은 덕 타이핑을 사용 하므로 여러 유형을 처리하기 위해 특별한 구문이 필요하지 않습니다.
C ++ 배경 인 경우 템플릿 함수 / 클래스에서 사용되는 작업 T
이 구문 수준에서 특정 유형에 정의되어 T
있는 한 템플릿에서 해당 유형 을 사용할 수 있다는 것을 기억할 것입니다 .
따라서 기본적으로 동일한 방식으로 작동합니다.
그러나 명시적인 유형 검사 (일반적으로 권장되지 않음)를 작성하지 않는 한 이진 트리에 선택한 유형의 요소 만 포함하도록 강제 할 수 없습니다.
if isintance(o, t):
if not isinstance(o, t):
다른 대답은 완전히 괜찮습니다.
그러나 유형화 된 변형이 필요한 경우 Python 3.5 이후 기본 제공 솔루션이 있습니다.
일반 클래스 :
from typing import TypeVar, Generic
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 문서 .
실제로 이제 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 )
부모로 상속하는 여러 클래스 는 두 개의 개별 클래스에서 상속됩니다. 이를 극복하려면 새로운 내부 클래스를 생성하는 대신 다양한 형태의 내부 클래스를 저장하고 이전에 생성 된 내부 클래스를 반환하는 dict를 생성해야합니다. 이렇게하면 동일한 매개 변수를 가진 중복 유형이 생성되지 않습니다. 관심이 있다면 데코레이터 및 / 또는 메타 클래스를 사용하여보다 우아한 솔루션을 만들 수 있습니다.
Python은 동적으로 유형이 지정되기 때문에 많은 경우 객체 유형이 중요하지 않습니다. 무엇이든 받아들이는 것이 더 좋은 생각입니다.
내가 의미하는 바를 설명하기 위해이 트리 클래스는 두 가지 분기에 대해 무엇이든 허용합니다.
class BinaryTree:
def __init__(self, left, right):
self.left, self.right = left, right
그리고 다음과 같이 사용할 수 있습니다.
branch1 = BinaryTree(1,2)
myitem = MyClass()
branch2 = BinaryTree(myitem, None)
tree = BinaryTree(branch1, branch2)
foo
각 개체에 대해 메서드 를 호출하는 경우 컨테이너에 문자열을 넣는 것은 좋지 않습니다. 그것은 아니다 더 받아 들일 생각이 아무것도 . 그러나 컨테이너의 모든 객체가 class에서 파생되도록 요구하지 않는 것이 편리 합니다 HasAFooMethod
.
파이썬은 동적으로 입력되기 때문에 매우 쉽습니다. 사실, 어떤 데이터 유형과도 작동하지 않도록 BinaryTree 클래스에 대한 추가 작업을 수행해야합니다.
예를 들어, 개체를 key()
호출 하는 것과 같은 메서드에서 개체 내에서 사용 가능한 트리에 개체를 배치하는 데 사용되는 키 값을 원하는 경우 key()
입니다. 예를 들면 :
class BinaryTree(object):
def insert(self, object_to_insert):
key = object_to_insert.key()
object_to_insert 클래스의 종류를 정의 할 필요가 없습니다. 너무 오래 그것은이 같은 key()
방법을, 그것을 작동합니다.
예외는 문자열이나 정수와 같은 기본 데이터 유형으로 작업하려는 경우입니다. 일반 BinaryTree와 함께 작동하도록하려면 클래스에 래핑해야합니다. 너무 무겁게 들리고 실제로 문자열을 저장하는 추가 효율성을 원하면 죄송합니다. 파이썬이 잘하는 것은 아닙니다.
Integer
복싱 / 언 박싱 을 사용하는 Java에서와 같이 ).
다음 은 지저분한 구문을 피하기 위해 메타 클래스를 사용하고 -style 구문을 사용하는 이 답변 의 변형입니다 .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
다행히도 파이썬에서 제네릭 프로그래밍에 대한 노력이있었습니다. 라이브러리가 있습니다 : 일반
이에 대한 문서는 다음과 같습니다. http://generic.readthedocs.org/en/latest/
수년 동안 진행되지 않았지만 자신의 라이브러리를 사용하고 만드는 방법을 대략적으로 알 수 있습니다.
건배
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)
할 일
<? extends List<Number>>
)super
지원 추가?
지원 추가