Różnica między definiowaniem typowania. Dict i dykt?

106

Ćwiczę używanie podpowiedzi typu w Pythonie 3.5. Jeden z moich kolegów używa typing.Dict:

import typing


def change_bandwidths(new_bandwidths: typing.Dict,
                      user_id: int,
                      user_name: str) -> bool:
    print(new_bandwidths, user_id, user_name)
    return False


def my_change_bandwidths(new_bandwidths: dict,
                         user_id: int,
                         user_name: str) ->bool:
    print(new_bandwidths, user_id, user_name)
    return True


def main():
    my_id, my_name = 23, "Tiras"
    simple_dict = {"Hello": "Moon"}
    change_bandwidths(simple_dict, my_id, my_name)
    new_dict = {"new": "energy source"}
    my_change_bandwidths(new_dict, my_id, my_name)

if __name__ == "__main__":
    main()

Oba działają dobrze, wydaje się, że nie ma różnicy.

Zapoznałem się z typingdokumentacją modułu .

Pomiędzy typing.Dictlub dictktórego powinienem używać w programie?

joe
źródło
11
Zauważ, że Python w rzeczywistości nie wymusza wskazówek dotyczących typów. Są tylko wskazówkami , nie są używane w czasie wykonywania ani nawet kompilacji do wymuszania typów. Python może być typowany silnie (w przeciwieństwie do typowania słabego), jest również typowany dynamicznie (w przeciwieństwie do typowania ścisłego). Zobacz Czy Python jest silnie wpisany? . Narzędzia zewnętrzne, takie jak mypy, mogą korzystać z tych wskazówek, aby pomóc Ci napisać lepszy kod, jednak w procesie zwanym analizą statyczną.
Martijn Pieters
1
@MartijnPieters Uwielbiałem używać podpowiedzi typu w moim kodzie razem z MyPy i udawać, że mogę używać Pythona z bezpieczeństwem typów. Niestety dostałem A) kod, który nie działa na <3.4 i B) ludzie śmieją się ze mnie, ponieważ najwyraźniej podpowiedzi dotyczące typów są pośmiewiskiem. To naprawdę niefortunne.
kot
3
@cat: podpowiedzi typu zostały wprowadzone do Pythona przez pracownika Facebooka, ponieważ odnieśliśmy ogromny sukces, dodając tę ​​samą funkcję do PHP (patrz hack ). Ktoś, kto się śmieje, nigdy nie zbudował dużego projektu z więcej niż garstką inżynierów.
Martijn Pieters
3
@MartijnPieters Nie, def a(b: int) -> bool:to błąd składniowy w Pythonie 2.7 i myślę, że jest to również błąd składniowy w starszych wersjach Pythona 3.
kot
1
@cat: mówisz tutaj o adnotacjach funkcji , składni, która została dodana do Pythona 3.0. Tak więc jedyną wersją, w której jest to błąd składniowy, jest 2.7, dlatego mypy obsługuje umieszczanie tych informacji w komentarzach.
Martijn Pieters

Odpowiedzi:

148

Nie ma prawdziwej różnicy między używaniem zwykłego typing.Dicta dictnie.

Jednak typing.Dictjest to typ rodzajowy * , który pozwala określić typ kluczy i wartości zbyt , dzięki czemu jest bardziej elastyczny:

def change_bandwidths(new_bandwidths: typing.Dict[str, str],
                      user_id: int,
                      user_name: str) -> bool:

W związku z tym może się zdarzyć, że w pewnym momencie życia projektu zechcesz nieco dokładniej zdefiniować argument słownikowy, w którym to momencie rozszerzenie typing.Dictdo typing.Dict[key_type, value_type]będzie „mniejszą” zmianą niż zastąpienie dict.

Możesz uczynić to jeszcze bardziej ogólnym, używając Mappinglub MutableMappingtypów tutaj; ponieważ twoja funkcja nie musi zmieniać mapowania, trzymałbym się Mapping. A dictto jedno mapowanie, ale możesz utworzyć inne obiekty, które również będą zgodne z interfejsem mapowania, a Twoja funkcja może nadal działać z tymi:

def change_bandwidths(new_bandwidths: typing.Mapping[str, str],
                      user_id: int,
                      user_name: str) -> bool:

Teraz jesteś wyraźnie mówią inni użytkownicy tej funkcji, że kod nie będzie faktycznie zmieniają się new_bandwidthsmapowanie przekazany w.

Twoja rzeczywista implementacja po prostu oczekuje obiektu, który można wydrukować. Może to być implementacja testowa, ale w obecnym stanie kod nadal działałby, gdybyś był używany new_bandwidths: typing.Any, ponieważ każdy obiekt w Pythonie można wydrukować.


* : Uwaga: jeśli korzystasz z Pythona 3.7 lub nowszego, możesz używać go dictjako typu ogólnego, jeśli uruchomisz swój moduł z from __future__ import annotations, a od Pythona 3.9 dict(a także z innymi standardowymi kontenerami) obsługuje używanie jako typu ogólnego nawet bez tego dyrektywy .

Martijn Pieters
źródło
2
Przydatne dodatkowe przykłady to sytuacje, gdy wartości słownikowe mogą być różnych typów, np. {"name": "bob", "age" : 51}Czy byłoby to coś podobnego typing.Mapping[Union[str, int]? A co z zagnieżdżonym słownikiem {"person": {"name":"bob", "age": 51}, który mógłby być podobny typing.Mapping[str, typing.Mapping[Union[str, int]]? Używanie w Unionten sposób jest dla mnie kłopotliwe, ponieważ nie jest to ścisły schemat, ponieważ nie ma porządku. Może to w porządku, czy jest alternatywa?
Davos,
1
Nieważne co do Unionpytania, które widzę, to wciąż otwarta dyskusja github.com/python/typing/issues/28
Davos
1
Wydaje się, że jest to bardzo interesujące, przydatne i powiązane python.org/dev/peps/pep-0589
Greg Hilston
@GregHilston: tak naprawdę chodzi o to, jak ograniczyć klucze, które może zawierać słownik, i określić, jakie typy powinna mieć każda skojarzona wartość.
Martijn Pieters
1
@GregHilston: ah, tak, jest.
Martijn Pieters
26

typing.Dictjest ogólną wersją dict:

class typing.Dict(dict, MutableMapping[KT, VT])

Ogólna wersja dict. Wykorzystanie tego typu jest następujące:

def get_position_in_index(word_list: Dict[str, int], word: str) -> int:
     return word_list[word]

Tutaj możesz określić typ klucza i wartości w dict: Dict[str, int]

AKS
źródło