Как да използвате статична проверка на типа в Python 3.6

Автоматично улавя много често срещани грешки при кодирането

Едно от най-често срещаните оплаквания относно езика на Python е, че променливите са динамично въведени. Това означава, че декларирате променливи, без да им давате конкретен тип данни. Типовете се присвояват автоматично въз основа на данните, предадени в:

В този случай променливата President_name се създава като тип str, защото ние преминахме в низ. Но Python не знаеше, че това ще бъде низ, докато всъщност не изпълни този ред код.

За сравнение, език като Java е статично въведен. За да създадете същата променлива в Java, трябва да декларирате стринга изрично с тип String:

Тъй като Java знае предварително, че President_name може да съдържа само String, тя ще ви даде грешка при компилиране, ако се опитате да направите нещо глупаво като да запазите цяло число в него или да го прехвърлите във функция, която очаква нещо различно от String.

Защо трябва да се грижа за видове?

Обикновено е по-бързо да пишете нов код на динамично въведен език като Python, тъй като не е необходимо да изписвате всички декларации от типа на ръка. Но когато вашата кодова база започне да се увеличава, неизбежно ще се сблъскате с много грешки по време на изпълнение, които статичното писане би предотвратило.

Ето пример за невероятно често срещан вид грешка в Python:

Всичко, което правим, е да питаме потребителя за името му и след това да отпечатваме „Здравей, <първо име>!“. И ако потребителят не въведе нещо, искаме да отпечатаме „Здравей, UserFirstName!“ Като резервен.

Тази програма ще работи перфектно, ако я стартирате и въведете име ... НО тя ще се срине, ако оставите името празно:

Traceback (последно последно обаждане):
  Файл "test.py", ред 14, в 
    first_name = get_first_name (backback_name)
  Файл "test.py", ред 2, в get_first_name
    върнете full_name.split ("") [0]
AttributeError: 'dict' обект няма атрибут 'split'

Проблемът е, че dropback_name не е низ - това е речник. Така че извикването на get_first_name при dropback_name се проваля ужасно, защото няма функция .split ().

Това е проста и очевидна грешка за отстраняване, но това, което прави тази грешка коварна е, че никога няма да разберете, че съществува грешка, докато някой потребител не се случи да стартира програмата и остави името празно. Може да тествате програмата хиляда пъти сами и никога да не забележите тази проста грешка, защото винаги сте въвели име.

Статичното типиране предотвратява този вид грешки. Преди дори да се опитате да стартирате програмата, статичното въвеждане ще ви каже, че не можете да прехвърлите резервно име в get_first_name (), защото очаква str, но вие му давате Dict. Вашият код редактор може дори да подчертае грешката, докато пишете!

Когато този вид грешка се случи в Python, той обикновено не е в проста функция като тази. Грешката обикновено се заравя в няколко слоя надолу в кода и се задейства, тъй като предадените данни са малко по-различни от очакваното по-рано. За да отстраните грешката, трябва да пресъздадете данните на потребителя и да разберете къде се обърка. Толкова много време се губи отстраняване на грешки от тези лесно предотвратими бъгове.

Добрата новина е, че вече можете да използвате статично въвеждане в Python, ако искате. И както на Python 3.6, най-накрая има синтактичен синтаксис за деклариране на типове.

Оправяне на нашата програма за бъги

Нека актуализираме програмата за бъги, като декларираме типа на всяка променлива и всеки вход / изход на функция. Ето актуализираната версия:

В Python 3.6 декларирате тип променлива като тази:

име на променлива: тип

Ако присвоявате първоначална стойност, когато създавате променливата, тя е толкова проста като тази:

my_string: str = "Моята струнна стойност"

И декларирате входните и изходните типове на функция като тази:

def function_name (параметър1: тип) -> return_type:

Това е доста просто - само малка настройка към нормалния синтаксис на Python. Но сега, когато типовете са декларирани, вижте какво се случва, когато пусна проверка на типа:

$ mypy typing_test.py
test.py:16: грешка: Аргумент 1 до „get_first_name“ има несъвместим тип Dict [str, str]; очаква се "str"

Без дори да изпълни програмата, тя знае, че няма начин този ред 16 да работи! Можете да поправите грешката в момента, без да чакате потребител да я открие след три месеца.

И ако използвате IDE като PyCharm, той автоматично ще проверява типовете и ще ви покаже къде нещо не е наред, преди дори да натиснете „Run“:

Това е толкова лесно!

Още примери за синтаксис на въвеждане на Python 3.6

Декларирането на str или int променливи е просто. Главоболието се случва, когато работите с по-сложни типове данни като вложени списъци и речници. За щастие новият синтаксис на Python 3.6 за това не е твърде лошо - поне не за език, който добави писането като замислена.

Основният модел е да импортирате името на сложния тип данни от модула за писане и след това да прехвърлите вложени скоби в скоби.

Най-често срещаните сложни типове данни, които ще използвате, са Dict, List и Tuple. Ето как изглежда да ги използвате:

от въвеждане на Dict import, List
# Речник, където ключовете са низове и стойностите са вм

name_counts: Dict [str, int] = {
    „Адам“: 10,
    "Гуидо": 12
}
# Списък на цели числа

числа: Списък [int] = [1, 2, 3, 4, 5, 6]
# Списък, който съдържа диктове, че всеки притежава стринг ключ / int стойност

list_of_dicts: Списък [Dict [str, int]] = [
    {"key1": 1},
    {"key2": 2}
]

Кортежите са малко по-специални, защото ви позволяват да декларирате типа на всеки елемент поотделно:

от въвеждане на импортиране Tuple

my_data: Tuple [str, int, float] = ("Адам", 10, 5.7)

Вие също създавате псевдоними за сложни типове само като ги присвоите на ново име:

от въвеждане на списъка за импортиране, Tuple

LatLngVector = Списък [Tuple [float, float]]

точки: LatLngVector = [
    (25.91375, -60.15503),
    (-11.01983, -166.48477),
    (-11.01983, -166.48477)
]

Понякога вашите функции на Python може да са достатъчно гъвкави, за да се справят с няколко различни типа или да работят върху всеки тип данни. Можете да използвате типа Union, за да декларирате функция, която може да приема няколко типа, и можете да използвате Any, за да приемете каквото и да било.

Python 3.6 също така поддържа някои от фантастичните текстови неща, които може би сте виждали в други езици на програмиране като общи типове и потребителски дефинирани типове.

Пускане на проверка на типа

Докато Python 3.6 ви дава този синтаксис за деклариране на типове, в самия Python все още няма абсолютно нищо, което да прави нещо с тези декларации от тип. За да наложите действително проверка на типа, трябва да направите едно от две неща:

  1. Изтеглете контролера за тип mypy с отворен код и го стартирайте като част от тестовете на вашето устройство или работен процес за разработка.
  2. Използвайте PyCharm, който има вградена проверка на типа в IDE. Или ако използвате друг редактор като Atom, изтеглете собствен плъгин за проверка на типа.

Препоръчвам да направите и двете. PyCharm и mypy използват различни изпълнения за проверка на типа и всяка от тях може да хване неща, които другият няма. Можете да използвате PyCharm за проверка на типа в реално време и след това да стартирате mypy като част от тестовете на вашата единица като окончателна проверка.

Страхотен! Трябва ли да започна да пиша целия си Python код с декларации за тип?

Този синтаксис на деклариране на тип е много нов за Python - той работи напълно напълно само от Python 3.6. Ако покажете въведения си Python код на друг разработчик на Python, има голям шанс те да ви помислят за луд и дори да не вярват, че синтаксисът е валиден. А проверката на типа mypy все още е в процес на разработка и все още не твърди, че е стабилна.

Така че може да е малко рано да се занимавате с това за всичките си проекти. Но ако работите върху нов проект, в който можете да поставите Python 3.6 като минимално изискване, може да си струва да експериментирате с въвеждането. Той има потенциал да предотврати много грешки.

Едно от чистите неща е, че лесно можете да смесвате код с и с декларации от типа. Не е всичко или нищо. Така че можете да започнете с деклариране на типове в местата, които са най-ценни, без да променяте целия си код. И тъй като самият Python няма да направи нищо с тези типове по време на изпълнение, не можете случайно да нарушите програмата си, като добавите декларации за тип.

Благодаря за четенето! Ако се интересувате от машинно обучение (или просто искате да разберете какво е), разгледайте Моето машинно обучение е забавно! серия.

Можете също да ме последвате в Twitter на @ageitgey или да ме намерите на linkedin.