いくつかのフィールドを共有する名前付きタプルがいくつかあります。これらのタプルを受け入れ、共有フィールドとのみ対話することが保証されている関数があります。 mypyでそのようなコードをタイプチェックしたいと思います。
コードの例は次のとおりです。
from typing import NamedTuple
class Base(NamedTuple):
x: int
y: int
class BaseExtended(NamedTuple):
x: int
y: int
z: str
def DoSomething(Tuple: Base):
return Tuple.x + Tuple.y
base = Base(3, 4)
base_extended = BaseExtended(5, 6, 'foo')
DoSomething(base)
DoSomething(base_extended)
このコードでmypyを実行すると、予測可能なエラーが発生します。
mypy_example.py:20:エラー:「DoSomething」の引数1に互換性のないタイプ「BaseExtended」があります。期待される「ベース」
コードを構造化してmypyのタイプチェックを続ける方法はありませんか? NamedTuple継承の実装にバグがあるため、BaseからBaseExtendedを継承できません。
https://github.com/python/typing/issues/427
醜い「Union [Base、BaseExtended]」も使いたくありません。「List [Union [Base、BaseExtended]]」は「List [BaseExtended]」と等しくないため、リストをタイプチェックしようとすると壊れてしまうからです。 ] "バリアント/共変タイプに関するいくつかのmypy魔法による:
https://github.com/python/mypy/issues/3351
私はその考えを捨てるべきですか?
名前付きタプルの構築方法により、_typing.NamedTuple
_クラスからの継承はまだ不可能です。サブクラス化を機能させるには、_typing.NamedTupleMeta
_クラスを拡張する独自のメタクラスを作成する必要がありますが、それでも collections.namedtuple()
によって生成されたクラスは拡張用に構築されていません 。
代わりに、新しい dataclasses
module を使用してクラスを定義し、継承を実現します。
_from dataclasses import dataclass
@dataclass(frozen=True)
class Base:
x: int
y: int
@dataclass(frozen=True)
class BaseExtended(Base):
z: str
_
このモジュールはPython 3.7で新しく追加されましたが、 _pip install dataclasses
_ the backport on Python 3.6。
上記は、x
属性とy
属性を持つ2つの不変クラスを定義し、BaseExtended
クラスはもう1つの属性を追加します。 BaseExtended
はBase
の完全なサブクラスであるため、入力の目的でDoSomething()
関数の要件に適合します。
クラスは長さやインデックス作成をサポートしていないため、完全な名前のタプルではありませんが、_collections.abc.Sequence
_から継承する基本クラスを作成し、インデックスでフィールドにアクセスする2つのメソッドを追加することで、簡単に追加できます。 _order=True
_を@dataclass()
デコレータに追加すると、(名前付き)タプルと同じ方法でインスタンスが完全に注文可能になります。
_from collections.abc import Sequence
from dataclasses import dataclass, fields
class DataclassSequence(Sequence):
# make a dataclass Tuple-like by accessing fields by index
def __getitem__(self, i):
return getattr(self, fields(self)[i].name)
def __len__(self):
return len(fields(self))
@dataclass(frozen=True, order=True)
class Base(DataclassSequence):
x: int
y: int
_
MyPy まもなくdataclasses
を明示的にサポートします ;バージョン0.600では、dataclasses
モジュールのインポートを認識しないか、___new__
_メソッドが生成されるため、エラーが発生します。
Python 3.6以前では、 attrs
project をインストールして、同じ効果を実現することもできます。上記のシーケンス基本クラスは、attrs
を使用して次のようになります。
_from collections.abc import Sequence
import attr
class AttrsSequence(Sequence):
# make a dataclass Tuple-like by accessing fields by index
def __getitem__(self, i):
return getattr(self, attr.fields(type(self))[i].name)
def __len__(self):
return len(attr.fields(type(self)))
@attr.s(frozen=True, auto_attribs=True)
class Base(AttrsSequence):
x: int
y: int
_
dataclasses
はattrs
に直接基づいており、attrs
はより多くの機能を提供します。 mypyは、attrs
で生成されたクラスを完全にサポートします。
PEP 544 は、構造的サブタイピング(静的ダックタイピング)を可能にする型システムの拡張を提案しています。また、typing.NamedTuple
のランタイム実装は、おそらくPython 3.6.2 6月末に)すぐに改善されます(これもPyPIのtyping
を介してバックポートされます)。