Python 3.6中的泛型NamedTuple

Python 3.6中的泛型NamedTuple,python,generics,python-3.6,typing,Python,Generics,Python 3.6,Typing,我正在尝试创建NamedTuple的通用版本,如下所示: T1 = TypeVar("T1") T2 = TypeVar("T2") class Group(NamedTuple, Generic[T1, T2]): key: T1 group: List[T2] g = Group(1, [""]) # expecting type to be Group[int, str] 但是,我得到以下错误: TypeError: metaclass conflict: the

我正在尝试创建NamedTuple的通用版本,如下所示:

T1 = TypeVar("T1")
T2 = TypeVar("T2")

class Group(NamedTuple, Generic[T1, T2]):
    key: T1
    group: List[T2]

g = Group(1, [""])  # expecting type to be Group[int, str]
但是,我得到以下错误:

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

我不知道还有什么其他方法可以实现我在这里要做的,或者这在某种程度上可能是键入机制中的一个错误。

因此这是一个元类冲突,因为在python 3.6中,键入
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, [""])  # --> Group(key=1, group=[""])

由于PEPs,这在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=[''])
当然,在Python3.7中,您可以只使用一个数据类,该数据类不那么轻量级(而且是可变的),但具有类似的用途

from dataclasses import dataclass, astuple
from typing import Generic, TypeVar, List

T1 = TypeVar('T1')
T2 = TypeVar('T2')

@dataclass
class Group(Generic[T1, T2]):

     # this stores the data like a tuple, but isn't required
     __slots__ = ("key", "group")

     key: T1
     group: List[T2]

     # if you want to be able to unpack like a tuple...
     def __iter__(self):
          yield from astuple(self)


g: Group[int, str] = Group(1, ['hello', 'world'])
k, v = g
print(g)

类型检查器在Python3.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, ['']) # --> Group(key=1, group=[''])

您可以进一步阐述,在这方面,您打算概括NamedTuple,这在我看来是相当普遍的。从您的代码片段中,我无法识别…特殊的
NamedTuple
不支持任何其他基类。句号。
NamedTuple
基类将所有使用委托给一个元类,该元类将运行
collections.NamedTuple()
,并进行少量自定义,这意味着生成的类只能从
tuple
继承。对于
NamedTuple
,这是一个更广泛的问题,不仅限于
Generic
。第二个解决方案看起来不错,但不幸的是它不能完全用于类型暗示:
g:Group[int,str]=Group(1,[“”])TypeError:“type”对象不可下标
Ah ok。如果你交换元类的基础呢?第一个是如何实现的?您仍然不能在
NamedTuple
子类中使用
Generic[…]
提示,因为由于非常相同的元类冲突,新生成的命名tuple类将不会有
\uu class\u getitem\uuu
钩子来生成具体的提示,如
Group[str,int]
工作。这是因为
NamedTuple
元类返回一个新的类对象,其中只有
tuple
作为基类,而不是
Generic
,并且记录可用typevars的类上必要的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu属性完全消失了。请在您的回答中更加明确,由于在使用该子类的所有模块中都需要该编译器开关,并且使用dataclass+
\Uuuu iter\Uuuu
方法解决方案,因此在执行分解赋值时,由于
astuple
的限制,生成的变量都是
Any
类型的(本期)但我想这是在可预见的未来我们能做的最好的了。。。在mypy中支持通用namedtuples的相关问题已经公开并解决了5年: