Hacker News

Споредба на проверка на типот на Python: Заклучок за празен контејнер

Коментари

1 min read Via pyrefly.org

Mewayz Team

Editorial Team

Hacker News

Зошто празните контејнери ги кршат проверките за типови на Python - и што можете да направите за тоа

Системот за постепено пишување на Python значително созреа откако PEP 484 ги воведе советите за тип во 2015 година. Денес, милиони програмери се потпираат на статични проверки за да ги фатат грешките пред да стигнат до производството. Но, постои еден суптилен, фрустрирачки агол на типовиот систем што сè уште ги сопнува дури и искусните инженери: каков тип има празен контејнер? Кога пишувате x = [] без прибелешка, вашиот проверувач на тип треба да погоди - а различни дама погодуваат поинаку. Оваа дивергенција создава реални проблеми за тимовите кои одржуваат големи бази на кодови, каде што префрлувањето или комбинирањето на проверувачите на типот може да се појават стотици неочекувани грешки преку ноќ.

Оваа статија објаснува како четирите главни проверки на типот на Python - mypy, pyright, pytype и pyre - се справуваат со заклучоците за празниот контејнер, зошто тие не се согласуваат и кои практични стратегии можете да ги примените за да напишете безбеден за тип Python без оглед на вашиот избор на алатки.

Основниот проблем: празните контејнери се инхерентно двосмислени

Размислете за оваа безопасна линија на Python: резултати = []. Дали резултатите е листа[int]? листа[улица]? А листа[dict[str, Дали]]? Без дополнителен контекст, навистина нема начин да се знае. Времето на извршување на Python не се грижи - списоците се хетерогени по природа - но проверувачите на статички тип треба да доделат конкретен тип на секоја променлива за да ја завршат својата работа. Ова создава фундаментална тензија помеѓу динамичката флексибилност на Python и гаранциите што статичката анализа се обидува да ги обезбеди.

Проблемот се соединува со речници и множества. Празен {} всушност се анализира како дикт, а не како множество, што додава синтаксичка двосмисленост на врвот на двосмисленоста на ниво на тип. И вгнездените контејнери — размислете за стандардно(листа) или резултати = {k: [] за k во клучеви — турнете ги моторите за заклучување до нивните граници. Секој тип проверувач има развиено своја хеуристика, а разликите се позначајни отколку што сфаќаат повеќето програмери.

Во производните системи кои обработуваат реални работни оптоварувања - без разлика дали се работи за CRM за ракување со записите на клиентите, модул за фактурирање што генерира ставки од линијата или аналитички цевковод за собирање метрика - празните контејнери постојано се појавуваат како обрасци за иницијализација. Грешката на нивните типови не произведува само предупредувања за ѓубре; може да ги маскира вистинските грешки кои се провлекуваат до времето на работа.

Mypy: Одложено заклучување со имплицитно било кое

Mypy, најстариот и најраспространет проверувач на типови Python, има релативно благ пристап кон празните контејнери. Кога ќе наиде на x = [] во опсегот на функцијата, се обидува да ја одложи одлуката за тип и да го заклучи типот на елементот од последователна употреба. Ако напишете x = [] проследено со x.append(42), mypy ќе заклучи list[int]. Оваа стратегија за „приклучување“ работи изненадувачки добро за едноставни случаи кога контејнерот е населен во истиот опсег.

Сепак, однесувањето на mypy драматично се менува во зависност од контекстот и поставките за строгост. Во опсегот на модулот (шифра на највисоко ниво) или кога контејнерот се пренесува на друга функција пред да се пополни, mypy често се враќа на листата[Секое]. Под знамето --strict, ова предизвикува грешка, но во стандардниот режим тивко поминува. Ова значи дека тимовите кои работат на mypy без строг режим може да акумулираат десетици имплицитно напишани контејнери кои делуваат како шрафови за бегство од типовиот систем, со што ќе ја поразат неговата намена.

Едно особено суптилно однесување: верзиите на mypy пред 0,990 понекогаш внатрешно заклучуваат листа[Непознато], а потоа се прошируваат на листа[Било] при задачата. По 0,990, заклучокот беше затегнат, но промената скрши изненадувачки број на бази на кодови од реалниот свет кои се потпираа на попустливото однесување без да го сфатат тоа. Ова е тема која се повторува - промените во заклучоците за празни контејнери се меѓу најнепушачите ажурирања за проверка на типот бидејќи шемите се толку сеприсутни.

Pyright: Strict Inference и типот „Непознат“

Pyright, развиен од Microsoft и го напојува Pylance во VS Code, зазема фундаментално поинаков филозофски став. Наместо тивко да се враќаме на Секое, pyright прави разлика помеѓу Непознат (тип што сè уште не е одреден) и Секое (експлицитно откажување од проверка на типот). Кога пишувате x = [] во строг режим на авторски права, тој заклучува список[Непознат] и известува за дијагностика, принудувајќи ве да дадете прибелешка.

Прајт е исто така поагресивен за стеснување во рамките на опсегот. Ако напишете:

  • x = [] проследено со x.append("hello") — pyright заклучува list[str]
  • x = [] проследено со x.append(1) потоа x.append("здраво") — pyright заклучува листа[int | ул]
  • x = [] се пренесува директно на функцијата што очекува листа[int] — pyright заклучува листа[int] од контекстот на страницата за повици
  • x = [] вратен од функција без прибелешка за тип на враќање — pyright известува за грешка наместо да погодува

Овој двонасочен заклучок (користејќи ги и последователната употреба и очекуваните типови од локациите за повици) го прави pyright значително попрецизен од mypy за празни контејнери. Размената е зборливост: строгиот режим на pyright означува приближно 30-40% повеќе проблеми на типична неозначена база на кодови во споредба со строгиот режим на mypy, според анализата од неколку извештаи за миграција со отворен код. За тимови кои градат сложени задни системи - да речеме, платформа која управува со 207 меѓусебно поврзани модули кои опфаќаат CRM, платен список и аналитика - строгоста на pyright фаќа суптилни несовпаѓања на интерфејсот што би ги пропуштило благите заклучоци.

Pytype и Pyre: The Less патувани патишта

Пајтипот на Google го има можеби најпрагматичниот пристап. Наместо да бара прибелешки или да се враќа на Секое, pytype користи анализа на целата програма за да следи како се користи контејнер преку границите на функциите. Ако креирате празна листа во една функција и ја префрлите на друга која додава цели броеви, pytype често може да заклучи list[int] без никакви прибелешки. Овој заклучок за вкрстена функција е пресметковно скап - pytype е значително побавен од mypy или pyright на големи бази на кодови - но произведува помалку лажни позитиви на незабележан код.

Pytype го воведува и концептот на „делумни типови“ за празни контејнери. Новосоздадениот [] добива делумен тип кој постепено се рафинира како што проверувачот наидува на поголема употреба. Ова е концептуално елегантно, но може да произведе збунувачки пораки за грешка кога делумниот тип не може целосно да се реши, како на пример кога празен контејнер тече низ неколку функции без воопшто да биде пополнет.

💡 DID YOU KNOW?

Mewayz replaces 8+ business tools in one platform

CRM · Invoicing · HR · Projects · Booking · eCommerce · POS · Analytics. Free forever plan available.

Start Free →
Во меѓувреме, кладата на Мета е поблиску до однесувањето на mypy, но со построги стандардни вредности. Pyre го третира x = [] како листа[непознат] и бара прибелешка во повеќето контексти. Она каде што pyre се разликува е во неговото ракување со празни речник буквални што се користат како kwargs - вообичаена шема во веб-рамките. Pyre има логика на специјални случаи за да заклучи типови на речник од контексти на аргументи на клучни зборови, намалувајќи го оптоварувањето на прибелешките во базите на кодови со тешки рамки. Со оглед на тоа што повеќето модерни веб-апликации вклучуваат голема употреба на распакување речник за конфигурација и справување со барањата, овој прагматизам дава дивиденди.

Влијание во реалниот свет: кога загрозува дивергенцијата на заклучоци

Разликите меѓу проверките на типови може да изгледаат академски додека не ги доживеете во производствена база на кодови. Размислете за вообичаен образец во деловните апликации: иницијализирање структура на податоци што се населува условно.

Најопасните празни контејнери не се знаменцето за проверка на типот - тие се оние што тивко поминуваат со заклучен тип Било кој, дозволувајќи им на некомпатибилните податоци да се акумулираат без предупредување додека не се сруши функцијата низводно при извршувањето со TypeError што е речиси невозможно да се пронајде.

Конкретен пример: тим од стартап за финална технологија пријави дека потрошил три дена за отстранување грешки на производствен проблем каде што празен список, иницијализиран во функција за обработка на плаќања, беше заклучен како листа[Секое] од mypy. Списокот требаше да содржи децимални објекти за валутни износи, но патеката на кодот додаваше float вредности. Попустливиот заклучок на Мајпи тивко го дозволи тоа. Грешката се појави само кога грешките во заокружувањето во пловечката аритметика предизвикаа несовпаѓање од 0,01 долари на серија од 12.000 фактури. Да користеле pyright во строг режим или едноставно да ја назначеле празната листа како листа[децимална], грешката би била фатена во времето на развој.

Во Mewayz, каде што платформата обработува фактурирање, пресметки на плати и финансиска аналитика на над 138.000 кориснички сметки, овој вид безбедносен јаз не е теоретски - тоа е разликата помеѓу точните исплаќања на платите и скапите повторни пресметки. Строгата дисциплина за пишување околу иницијализацијата на контејнерот е една од оние „досадни“ инженерски практики што спречува возбудливи инциденти во производството.

Најдобри практики за иницијализација на одбранбениот контејнер

Без оглед на тоа кој тип проверувач го користи вашиот тим, постојат конкретни стратегии за целосно елиминирање на двосмисленоста на празниот контејнер. Целта е никогаш да не се потпирате на заклучоци за празни контејнери - направете го типот експлицитен за вашиот код да биде пренослив низ сите дама и да биде имун на промени во однесувањето на заклучоците помеѓу верзиите.

  1. Секогаш забележете ги променливите за празни контејнери. Напишете резултати: листа[int] = [] наместо резултати = []. Малиот трошок за говорност е занемарлив во споредба со заштеденото време за отстранување грешки. Оваа единствена практика елиминира приближно 80% од проблемите со заклучоците за празни контејнери.
  2. Користете фабрички функции за сложени контејнери. Наместо кеш = {}, напишете функција како def make_cache() -> dict[str, list[UserRecord]]: врати {}. Прибелешката за типот на враќање го прави наменетиот тип недвосмислен и самодокументиран.
  3. Претпочитајте внесени конструктори наместо буквални за нетривијални типови. Напишете ставки: set[int] = set() наместо да се потпирате на заклучоци за разбирање на множества. За стандардно и бројач, секогаш давајте го параметарот тип: брои: Бројач[str] = Бројач().
  4. Конфигурирајте го строгиот режим на вашиот проверувач на типови за нов код. И mypy и pyright поддржуваат конфигурација по датотека или по директориум. Овозможете строга проверка на новите модули додека постепено го мигрирате наследниот код. Ова спречува акумулација на нови имплицитно напишани контејнери.
  5. Додајте споредба на проверувачот на типови на вашиот CI цевка. Работењето и mypy и pyright на вашата база на кодови рано ја фаќа дивергенцијата на заклучоците. Ако шемата помине една проверка, но не успее друга, тоа е сигнал дека типот не е доволно експлицитен.

Поголемата слика: Проверка на типови како тимска вежба

Заклучокот за празен контејнер е на крајот микрокосмос на поголем предизвик во системот на типот на Пајтон: тензијата помеѓу практичноста и безбедноста. Филозофијата на Python за „сите се согласуваме како возрасни“ работи прекрасно за прототипови и скрипти, но производствените системи кои опслужуваат илјадници корисници имаат потреба од посилни гаранции. Фактот дека четирите главни проверувачи на типови не се согласуваат за нешто толку основно како типот на [], нагласува дека екосистемот за пишување Python сè уште созрева.

За инженерските тимови кои градат сложени платформи - без разлика дали управувате со неколку микросервиси или интегриран систем со стотици меѓусебно поврзани модули како што е деловниот оперативен систем на Mewayz - практичниот совет е директен: не потпирајте се на заклучоци за празни контејнери, изберете проверка на типот и строго конфигурирајте го и третирајте ги прибелешките што се случуваат како машина за документација. Петте минути потрошени за пишување листа[фактура] наместо ќе ви заштедат часови на отстранување грешки кога вашата база на кодови ќе се размери.

Како што PEP 696 (стандардни параметри на типот) и PEP 695 (синтакса на параметарот на типот) продолжуваат да се спуштаат во поновите верзии на Python, ергономијата на експлицитно пишување ќе продолжи да се подобрува. Јазот помеѓу „забележан“ и „незабележан“ Python ќе се намали. Но, до тој ден, експлицитните типови на контејнери остануваат една од практиките со највисок рентабилност во пакетот алатки на развивачите на Python - мала дисциплина која плаќа сложена камата за секој модул, секој спринт и секое распоредување на производството.

Изградете го вашиот бизнис оперативен систем денес

Од хонорарци до агенции, Mewayz напојува над 138.000 бизниси со 207 интегрирани модули. Започнете бесплатно, надградете кога ќе пораснете.

Креирај