键入递归类和继承

  • 本文关键字:继承 递归 python mypy
  • 更新时间 :
  • 英文 :


我有以下类层次结构:

#!/usr/bin/env python3
from typing import List, Optional, Tuple, Type
class Attribute:
def __init__(self, name: bytes) -> None:
self._name = name
@property
def name(self) -> bytes:
return self._name
class Element:
def __init__(self, name: bytes, attributes: Tuple[Type['Attribute'], ...], elements: Tuple['Element', ...]) -> None:
self._name       = name
self._elements   = elements
self._attributes = attributes
@property
def name(self) -> bytes:
return self._name
@property
def elements(self) -> Tuple['Element', ...]:
return self._elements
@property
def attributes(self) -> Tuple[Type['Attribute'], ...]:
return self._attributes
class SubAttribute1(Attribute):
def __init__(self, name: bytes, field1: bytes) -> None:
super().__init__(name)
self._afield1 = field1
class SubElement1(Element):
def __init__(self, name: bytes, attributes: Tuple[Type[Attribute], ...], elements: Tuple['Element', ...], field1: bytes, field2: bytes) -> None:
super().__init__(name, attributes, elements)
self._field1 = field1
self._field2 = field2

if __name__ == '__main__':
subE  = SubElement1(b'name', None, None, b'', b'')
subA  = SubAttribute1(b'name', b'field1')
subE2 = SubElement1(b'name', (subA,), (subE,), b'', b'')
print(subE2.elements[0]._field1)
print(subE2.attributes[0]._afield1)
print(type(subE2.elements[0]))

我用基类Element和Attribute的子类来添加额外的字段。字段"elements"one_answers"attributes"应分别存储派生类对象。对于SubElement1 SubElement1(),elements存储一个具有SubElement1 obejcts的元组。所有工作都很好,但我得到了以下mypy错误:

question.py:45: error: Argument 2 to "SubElement1" has incompatible type "Tuple[SubAttribute1]"; expected "Tuple[Type[Attribute], ...]"
question.py:46: error: "Element" has no attribute "_field1"
question.py:47: error: "Type[Attribute]" has no attribute "_afield1"

如何更改代码以消除mypy错误?

这个问题很有趣,我认为PEP646的支持稍微好一点。

我假设python 3.10和目前最新发布的特定检查器版本,除非明确指定:mypy==0.991pyre-check==0.9.17pyright==1.1.281

使elements正确

首先,这里有一个(足够简单的)代码,它解析";元素";问题,但对属性没有帮助:

from typing import Generic, List, Optional, Sequence, Tuple, Type, TypeVar

_Self = TypeVar('_Self', bound='Element')

class Attribute:
def __init__(self, name: bytes) -> None:
self._name = name
@property
def name(self) -> bytes:
return self._name

class Element:
def __init__(self: _Self, name: bytes, attributes: tuple[Attribute, ...], elements: Sequence[_Self]) -> None:
self._name = name
self._elements = tuple(elements)
self._attributes = attributes
@property
def name(self) -> bytes:
return self._name
@property
def elements(self: _Self) -> Tuple[_Self, ...]:
return self._elements
@property
def attributes(self) -> Tuple[Attribute, ...]:
return self._attributes

class SubAttribute1(Attribute):
def __init__(self, name: bytes, field1: bytes) -> None:
super().__init__(name)
self._afield1 = field1

class SubElement1(Element):
def __init__(self: _Self, name: bytes, attributes: tuple[Attribute, ...], elements: Sequence[_Self], field1: bytes, field2: bytes) -> None:
super().__init__(name, attributes, elements)
self._field1 = field1
self._field2 = field2

if __name__ == '__main__':
subE  = SubElement1(b'name', tuple(), tuple(), b'', b'')
subA  = SubAttribute1(b'name', b'field1')
subE2 = SubElement1(b'name', (subA,), (subE,), b'', b'')
print(subE2.elements[0]._field1)
print(subE2.attributes[0]._afield1)  # E: "Attribute" has no attribute "_afield1"  [attr-defined]
print(type(subE2.elements[0]))

这给出了一个错误(在源代码中注释)。这是操场。

在最近的将来(甚至在mypymaster分支上工作,但在0.991上不工作),您将能够用from typing_extensions import Self替换_Self,并跳过对self参数的注释,如下所示:

# import from typing, if python >= 3.11
from typing_extensions import Self
class Element:
def __init__(self, name: bytes, attributes: tuple[Attribute, ...], elements: Sequence[Self]) -> None:
self._name = name
self._elements = tuple(elements)
self._attributes = attributes

你可以在这里试试——同样的1个错误。

变分attributes

现在您想要保留attributes类型——它们可能是异构的,因此需要PEP646才能继续。该类在未知数量的变量中变为泛型。pyrepyright声称支持这一点(mypy不支持,目前工作正在进行中)。pyre未能对下面的解决方案进行类型检查,出现了一些虚假错误。pyright成功(尽管我个人不喜欢它,所以不建议切换)。Pyright沙盒是非官方的,不是最新的,在这里不起作用——将其复制到本地,安装并运行pyright进行验证。

from typing import Generic, List, Optional, Sequence, Tuple, Type, TypeVar
from typing_extensions import Unpack, Self, TypeVarTuple
_Ts = TypeVarTuple('_Ts')

class Attribute:
def __init__(self, name: bytes) -> None:
self._name = name
@property
def name(self) -> bytes:
return self._name
class Element(Generic[Unpack[_Ts]]):
def __init__(self, name: bytes, attributes: tuple[Unpack[_Ts]], elements: Sequence[Self]) -> None:
self._name = name
self._elements = tuple(elements)
self._attributes = attributes
@property
def name(self) -> bytes:
return self._name
@property
def elements(self) -> Tuple[Self, ...]:
return self._elements
@property
def attributes(self) -> Tuple[Unpack[_Ts]]:
return self._attributes
class SubAttribute1(Attribute):
def __init__(self, name: bytes, field1: bytes) -> None:
super().__init__(name)
self._afield1 = field1
class SubElement1(Element[Unpack[_Ts]]):
def __init__(self, name: bytes, attributes: tuple[Unpack[_Ts]], elements: Sequence[Self], field1: bytes, field2: bytes) -> None:
super().__init__(name, attributes, elements)
self._field1 = field1
self._field2 = field2

if __name__ == '__main__':
subE  = SubElement1(b'name', tuple(), tuple(), b'', b'')
subA  = SubAttribute1(b'name', b'field1')
subE2 = SubElement1(b'name', (subA,), (subE,), b'', b'')
print(subE2.elements[0]._field1)
print(subE2.attributes[0]._afield1)
print(type(subE2.elements[0]))

Pyright0 errors, 0 warnings, 0 informationspyre错误:

ƛ Found 2 type errors!
t/t.py:15:14 Undefined or invalid type [11]: Annotation `Unpack` is not defined as a type.
t/t.py:15:14 Undefined or invalid type [11]: Annotation `_Ts` is not defined as a type.

即使有实验旗帜,mypy也会变得完全疯狂,如果你想看这个,请粘贴到mypy操场上。

均质attributes

然而,如果您的属性可以用同构序列表示(例如,SubElement1实例只能包含SubAttribute1),那么事情就简单多了,并且具有规则TypeVar的泛型就足够了:

from typing import Generic, List, Optional, Sequence, Tuple, Type, TypeVar

_Self = TypeVar('_Self', bound='Element')
_A = TypeVar('_A', bound='Attribute')

class Attribute:
def __init__(self, name: bytes) -> None:
self._name = name
@property
def name(self) -> bytes:
return self._name

class Element(Generic[_A]):
def __init__(self: _Self, name: bytes, attributes: Sequence[_A], elements: Sequence[_Self]) -> None:
self._name = name
self._elements = tuple(elements)
self._attributes = tuple(attributes)
@property
def name(self) -> bytes:
return self._name
@property
def elements(self: _Self) -> Tuple[_Self, ...]:
return self._elements
@property
def attributes(self) -> Tuple[_A, ...]:
return self._attributes

class SubAttribute1(Attribute):
def __init__(self, name: bytes, field1: bytes) -> None:
super().__init__(name)
self._afield1 = field1

class SubElement1(Element[SubAttribute1]):
def __init__(self: _Self, name: bytes, attributes: Sequence[SubAttribute1], elements: Sequence[_Self], field1: bytes, field2: bytes) -> None:
super().__init__(name, attributes, elements)
self._field1 = field1
self._field2 = field2

if __name__ == '__main__':
subE  = SubElement1(b'name', tuple(), tuple(), b'', b'')
subA  = SubAttribute1(b'name', b'field1')
subE2 = SubElement1(b'name', (subA,), (subE,), b'', b'')
print(subE2.elements[0]._field1)
print(subE2.attributes[0]._afield1)
print(type(subE2.elements[0]))

这是有效的。

奖金

你呈现的所有代码都被称为";用Python编写Java";(引文)。您绝对不需要具有简单属性访问的getter,因为您可以稍后添加它们。您不应该手工编写数据类——dataclasses标准模块会做得更好。因此,您的示例实际上简化为更加简洁和可维护的python:

from typing import Generic, Sequence, TypeVar
from typing_extensions import Self
from dataclasses import dataclass

_A = TypeVar('_A', bound='Attribute')

@dataclass
class Attribute:
name: bytes

@dataclass
class Element(Generic[_A]):
name: bytes
attributes: Sequence[_A]
elements: Sequence[Self]

# OK, if you need different names in constructor signature and class dict
class SubAttribute1(Attribute):
def __init__(self, name: bytes, field1: bytes) -> None:
super().__init__(name)
self._afield1 = field1


# But I'd really prefer
# @dataclass
# class SubAttribute1(Attribute):
#     field1: bytes
# And adjust calls below to use `field1` instead of `_afield1` - you try to expose it anyway
@dataclass
class SubElement1(Element[SubAttribute1]):
field1: bytes
field2: bytes

if __name__ == '__main__':
subE  = SubElement1(b'name', tuple(), tuple(), b'', b'')
subA  = SubAttribute1(b'name', b'field1')
subE2 = SubElement1(b'name', (subA,), (subE,), b'', b'')
print(subE2.elements[0].field1)
print(subE2.attributes[0]._afield1)
print(type(subE2.elements[0]))

它是有效的。好吧,很快就会工作了——目前mypy中不完全支持Self,检查它会导致内部错误(崩溃),我在这里报告了这一点。Pyright没有错误响应。UPDATE:在mypy主机上修复了错误,上面的例子进行了类型检查。

相关内容

  • 没有找到相关文章

最新更新