Python 3.6中的通用NamedTuple [英] Generic NamedTuple in Python 3.6
问题描述
我正在尝试创建NamedTuple的通用版本,如下所示:
I'm trying to create a generic version of a NamedTuple, as follows:
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
我不确定在这里要做什么,或者在某种程度上可能是键入机制中的错误.
I'm not sure how else to achieve what I'm trying to do here, or if this might be a bug in the typing mechanism on some level.
推荐答案
所以这是一个元类冲突,因为在python 3.6中,键入NamedTuple
和Generic
使用不同的元类(typing.NamedTupleMeta
和typing.GenericMeta
), python无法处理.恐怕除了从tuple
继承并手动初始化值之外,没有其他解决方案:
So this is a metaclass conflict since in python 3.6 the typing NamedTuple
and Generic
use different metaclasses (typing.NamedTupleMeta
and typing.GenericMeta
), which python can't handle. I'm afraid there is no solution to this, other than to subclass from tuple
and manually initialise the values:
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=[""])
由于PEP 560 和 563 在python 3.7中已解决:
Due to PEPs 560 and 563 this is fixed in 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中,您只能使用轻量级(可变)但具有类似用途的数据类.
Of course in python 3.7 you can just use a dataclass which are less lightweight (and mutable) but serve similar purposes.
from dataclasses import dataclass, astuple
from typing import Generic, TypeVar, List
T1 = TypeVar('T1')
T2 = TypeVar('T2')
@dataclass
class Group(Generic[T1, T2]):
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)
尽管我尚未检查类型检查器在python 3.7中如何处理我的解决方案/您的解决方案.我怀疑这可能不是无缝的.
How well type checkers handle my solution / yours in python 3.7 though I haven't checked. I suspect it may not be seamless.
我找到了另一个解决方案-制作一个新的元类
I found another solution -- make a new metaclass
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=[''])
这篇关于Python 3.6中的通用NamedTuple的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!