Как набрать подсказку словарь со значениями разных типов в Python - PullRequest
0 голосов
/ 26 июня 2018

При объявлении словаря как литерала, есть ли способ указать подсказке, какое значение я ожидаю для определенного ключа?

И затем, для обсуждения: существуют ли руководящие принципы по типизации словаря в 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...

Пихарм предложит мне '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]
        }

Есть ли лучший метод для такого рода вещей?

Еще немного предыстории:

Я работаю с python в pycharm и широко использую набор текста, поскольку это помогает мне прогнозировать и проверять мою работу по мере продвижения.Когда я создаю новые классы, я также иногда выбрасываю некоторые менее часто используемые свойства в словарь, чтобы избежать загромождения объекта слишком многими атрибутами (это полезно в режиме отладки).

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Вы ищете 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+, вы можете набратьвсе это более элегантно, используя синтаксис на основе классов .

В вашем конкретном случае, однако, я думаю, что большинство людей просто сохранят эти фрагменты данных как обычные поля вместо dict.Я уверен, что вы уже продумали все за и против этого подхода, поэтому я пропущу вам лекцию об этом.

0 голосов
/ 26 июня 2018

После еще одного исследования лучший обходной путь, который я смог найти до сих пор, - убедиться, что класс 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

Ни один из них не кажется очень изящным, поэтому я все еще надеюсь, что кто-то может предложить что-то лучшее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...