bugfix> python > 投稿

辞書をリテラルとして宣言するとき、特定のキーに期待する値をタイプヒントする方法はありますか?

それから、議論のために:Pythonでの辞書型付けに関する指針がありますか?辞書にタイプを混在させることは悪い習慣と考えられているのだろうかと思っています。

次に例を示します。

クラスの __init__ での辞書の宣言を検討する:

(免責事項:例では、いくつかの .elements エントリはおそらくクラス属性としてより適切ですが、それは例のためです)。

class Rectangle:
    def __init__(self, corners: Tuple[Tuple[float, float]], **kwargs):
        self.x, self.z = corners[0][0], corners[0][1]
        self.elements = {
            'front': Line(corners[0], corners[1]),
            'left': Line(corners[0], corners[2]),
            'right': Line(corners[1], corners[3]),
            'rear': Line(corners[3], corners[2]),
            'cog': calc_cog(corners),
            'area': calc_area(corners),
            'pins': None
        }

class Line:
    def __init__(self, p1: Tuple[float, float], p2: Tuple[float, float]):
        self.p1, self.p2 = p1, p2
        self.vertical = p1[0] == p2[0]
        self.horizontal = p1[1] == p2[1]

次のように入力すると

rec1 = Rectangle(rec1_corners, show=True, name='Nr1')
rec1.sides['f...

Pycharmは 'front' を提案します 私のために。さらに良いことは、私がするとき

rec1.sides['front'].ver...

Pycharmは .vertical を提案します

したがって、Pycharmはクラスの __init__ の辞書リテラル宣言からキーを記憶します 、およびそれらの値の予想されるタイプ。むしろ:リテラル宣言にある型のいずれかが値にあることを期待します-おそらく self.elements = {} # type: Union[type1, type2] を実行した場合と同じですするだろう。いずれにせよ、私はそれが非常に役立つと思います。

関数の出力にタイプヒントが含まれている場合、Pycharmはそれも考慮します。

そのため、 Rectangle で上記の例では、 pins Pin のリストですオブジェクト... pins の場合通常のクラス属性でしたが、

   self.pins = None  # type: List[Pin]

(必要なインポートが行われた場合)

辞書リテラル宣言で同じ型のヒントを与える方法はありますか?

以下はじゃない 私が探しているものを達成する:

Union[...] を追加リテラル宣言の最後にタイプヒントがありますか?

           'area': calc_area(corners),
            'pins': None
        }  # type: Union[Line, Tuple[float, float], float, List[Pin]]

タイプヒントをすべての行に追加します。

           'area': calc_area(corners),  # type: float
            'pins': None  # type: List[Pin]
        }

このようなことのベストプラクティスはありますか?

さらなる背景:

私はpycharmでpythonを使用し、タイピングを大いに活用しています。これは、作業を進めながら作業を予測および検証するのに役立つからです。新しいクラスを作成するとき、あまり頻繁に使用されないプロパティをディクショナリにスローして、オブジェクトが多くの属性で乱雑になるのを防ぐこともあります(これはデバッグモードで役立ちます)。

回答 2 件
  • TypedDictを探しています。現在はmypyのみの拡張機能ですが、近々正式に認可されたタイプにする計画があります。ただし、PyCharmがこの機能をサポートしているかどうかはまだわかりません。

    だから、あなたの場合、あなたはそうするでしょう:

    from mypy_extensions import TypedDict
    RectangleElements = TypedDict('RectangleElements', {
        'front': Line,
        'left': Line,
        'right': Line,
        'rear': Line,
        'cog': float,
        'area': float,
        'pins': Optional[List[Pin]]
    })
    class Rectangle:
        def __init__(self, corners: Tuple[Tuple[float, float]], **kwargs):
            self.x, self.z = corners[0][0], corners[0][1]
            self.elements = {
                'front': Line(corners[0], corners[1]),
                'left': Line(corners[0], corners[2]),
                'right': Line(corners[1], corners[3]),
                'rear': Line(corners[3], corners[2]),
                'cog': calc_cog(corners),
                'area': calc_area(corners),
                'pins': None
            }  # type: RectangleElements
    
    

    Python 3.6以降を使用している場合は、クラスベースの構文を使用して、これをよりエレガントに入力できます。

    ただし、特定のケースでは、ほとんどの人は、これらのデータを辞書ではなく通常のフィールドとして保存するだけだと思います。ただし、このアプローチの長所と短所については既に考えたことがあると思いますので、説明は省略します。

  • さらに調査した結果、これまでに見つけた最善の回避策は、クラス Pin を確認することでした。  ある種のプレースホルダーを返し、そのプレースホルダーをリテラル宣言の値として使用できます。

    したがって:

    class Pin:
        def __init__(self, **kwargs):
            if len(kwargs) == 0:
                return
    
    

    さて、OPの例では、私が望んでいたことを達成するために以下を行うことができました。

           ...
            'area': calc_area(corners),
            'pins': List[Pin(),]
        }
    
    

    ただし、エントリとして基本タイプ(複数可)がある場合、これは機能しません。

           ...
            'area': calc_area(corners),
            'pins': List[Pin(),]
            'color': None
            'lap_times': None
        }
    
    

    どこ color  文字列が必要です lap_times  フロート付きのリストが必要です...

    そのような場合、最善の回避策は

           ...
            'area': calc_area(corners),
            'pins': List[Pin(),]
            'color': 'blue'
            'lap_times': [0.,]
        }
        self.elements['color'], self.elements['lap_times'] = None, None
    
    

    これらはどちらも非常にエレガントではないようですので、私はまだ誰かがより良い何かを提案できることを望んでいます。

あなたの答え