15

将字典声明为文字时,有没有办法输入提示我期望特定键的值是什么?

然后,讨论一下:在 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 并广泛使用打字,因为它可以帮助我预测和验证我的工作。当我创建新类时,有时我还会将一些不常用的属性放入字典中,以避免带有太多属性的对象混乱(这在调试模式下很有帮助)。

4

2 回答 2

14

您正在寻找TypedDict。它目前只是一个 mypy-only 扩展,但计划在不久的将来使其成为官方认可的类型。不过,我不确定 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+,则可以使用基于类的语法更优雅地键入它。

不过,在您的具体情况下,我认为大多数人只会将这些数据存储为常规字段而不是字典。不过,我相信您已经考虑过这种方法的利弊,所以我将跳过向您讲授它。

于 2018-06-28T13:05:55.430 回答
0

经过更多调查,到目前为止我能找到的最佳解决方法是确保该类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
    } 

where color需要一个字符串并 lap_times需要一个带有浮点数的列表...

在这种情况下,最好的解决方法是

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

    self.elements['color'], self.elements['lap_times'] = None, None

这些看起来都不是很优雅,所以我仍然希望有人能提出更好的建议。

于 2018-06-26T07:36:57.377 回答