所以这是一个元类冲突,因为在Python 3.6中,typing的NamedTuple
和Generic
使用不同的元类(typing.NamedTupleMeta
和typing.GenericMeta
),这是Python无法处理的。恐怕除了从tuple
子类化并手动初始化值之外,没有其他解决方案:
T1 = TypeVar("T1")
T2 = TypeVar("T2")
class Group(tuple, Generic[T1, T2]):
key: T1
group: List[T2]
def __new__(cls, key: T1, group: List[T2]):
self = tuple.__new__(cls, (key, group))
self.key = key
self.group = group
return self
def __repr__(self) -> str:
return f'Group(key={self.key}, group={self.group})'
Group(1, [""])
由于PEP
560和
563,这已在Python 3.7中得到修复。
Python 3.7.0b2 (v3.7.0b2:b0ef5c979b, Feb 28 2018, 02:24:20) [MSC v.1912 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from __future__ import annotations
>>> from typing import *
>>> T1 = TypeVar("T1")
>>> T2 = TypeVar("T2")
>>> class Group(NamedTuple, Generic[T1, T2]):
... key: T1
... group: List[T2]
...
>>> g: Group[int, str] = Group(1, [""])
>>> g
Group(key=1, group=[''])
当然,在Python 3.7中,你可以使用数据类(dataclass),它们比较轻量级(且可变),但是具有类似的作用。
from dataclasses import dataclass, astuple
from typing import Generic, TypeVar, List
T1 = TypeVar('T1')
T2 = TypeVar('T2')
@dataclass
class Group(Generic[T1, T2]):
__slots__ = ("key", "group")
key: T1
group: List[T2]
def __iter__(self):
yield from astuple(self)
g: Group[int, str] = Group(1, ['hello', 'world'])
k, v = g
print(g)
我不确定python 3.7中类型检查器对我的解决方案/你的解决方案的处理效果如何,但我怀疑它可能不是无缝的。
编辑
我找到了另一种解决方案——创建一个新的元类
import typing
from typing import *
class NamedTupleGenericMeta(typing.NamedTupleMeta, typing.GenericMeta):
pass
class Group(NamedTuple, Generic[T1,T2], metaclass=NamedTupleGenericMeta):
key: T1
group: List[T2]
Group(1, [''])
NamedTuple
不支持任何其他基类。句号。NamedTuple
基类将所有使用委托给一个元类,该元类将运行collections.namedtuple()
并进行少量自定义,这意味着生成的类*仅继承自tuple
*。这是NamedTuple
的一个更广泛的问题,不仅限于Generic
。 - Martijn Pieters