Python կոդի 4 միլիոն տողերի տիպի ստուգման ուղին: Մաս 1

Այսօր ձեր ուշադրությանն ենք ներկայացնում նյութի թարգմանության առաջին մասը այն մասին, թե ինչպես է Dropbox-ը զբաղվում Python կոդի տիպերի կառավարմամբ։

Python կոդի 4 միլիոն տողերի տիպի ստուգման ուղին: Մաս 1

Dropbox-ը շատ է գրում Python-ում: Դա մի լեզու է, որը մենք չափազանց լայնորեն օգտագործում ենք, ինչպես հետին պլանային ծառայությունների, այնպես էլ աշխատասեղանի հաճախորդների հավելվածների համար: Մենք նաև շատ ենք օգտագործում Go, TypeScript և Rust, բայց Python-ը մեր հիմնական լեզուն է։ Հաշվի առնելով մեր մասշտաբները, և մենք խոսում ենք Python կոդի միլիոնավոր տողերի մասին, պարզվեց, որ նման կոդի դինամիկ մուտքագրումն անհարկի բարդացրեց դրա ըմբռնումը և սկսեց լրջորեն ազդել աշխատանքի արտադրողականության վրա: Այս խնդիրը մեղմելու համար մենք սկսել ենք աստիճանաբար անցնել մեր կոդը ստատիկ տիպի ստուգման՝ օգտագործելով mypy-ը: Սա, հավանաբար, Python-ի համար ամենահայտնի ինքնուրույն տիպի ստուգման համակարգն է: Mypy-ը բաց կոդով նախագիծ է, որի հիմնական մշակողները աշխատում են Dropbox-ում։

Dropbox-ը առաջին ընկերություններից մեկն էր, որն իրականացրեց ստատիկ տիպի ստուգում Python կոդում այս մասշտաբով: Mypy-ն այս օրերին օգտագործվում է հազարավոր նախագծերում: Այս գործիքն անհամար անգամ, ինչպես ասում են, «փորձարկվել է ճակատամարտում»։ Մենք երկար ճանապարհ ենք անցել՝ հասնելու այնտեղ, որտեղ գտնվում ենք: Ճանապարհին եղան բազմաթիվ անհաջող ձեռնարկումներ և անհաջող փորձեր։ Այս գրառումն ընդգրկում է Python-ում ստատիկ տիպերի ստուգման պատմությունը՝ սկսած իմ հետազոտական ​​նախագծի մի մասի իր ժայռոտ սկզբից մինչև մեր օրերը, երբ տիպի ստուգումն ու ակնարկը սովորական են դարձել Python-ով գրող անթիվ մշակողների համար: Այս մեխանիզմներն այժմ աջակցվում են բազմաթիվ գործիքներով, ինչպիսիք են IDE-ները և կոդերի անալիզատորները:

Կարդացեք երկրորդ մասը

Ինչու՞ է անհրաժեշտ տիպի ստուգումը:

Եթե ​​դուք երբևէ օգտագործել եք դինամիկ մուտքագրված Python-ը, կարող եք որոշակի շփոթություն ունենալ, թե ինչու է վերջերս այդքան աղմուկ բարձրացել ստատիկ մուտքագրման և mypy-ի շուրջ: Կամ գուցե ձեզ դուր է գալիս Python-ը հենց նրա դինամիկ մուտքագրման պատճառով, և այն, ինչ տեղի է ունենում, պարզապես վրդովեցնում է ձեզ: Ստատիկ մուտքագրման արժեքի բանալին լուծումների մասշտաբն է. որքան մեծ է ձեր նախագիծը, այնքան ավելի եք հակված դեպի ստատիկ մուտքագրում, և ի վերջո, այնքան ավելի շատ դրա կարիքն ունեք:

Ենթադրենք, որոշակի նախագիծը հասել է տասնյակ հազարավոր տողերի, և պարզվել է, որ դրա վրա աշխատում են մի քանի ծրագրավորողներ։ Նայելով նմանատիպ նախագծին, հիմնվելով մեր փորձի վրա, կարող ենք ասել, որ դրա կոդը հասկանալը կլինի ծրագրավորողներին արդյունավետ պահելու բանալին: Առանց տիպի անոտացիաների, դժվար է պարզել, օրինակ, թե ինչ արգումենտներ փոխանցել ֆունկցիային, կամ ինչ տեսակներ կարող է վերադարձնել ֆունկցիան: Ահա տիպիկ հարցեր, որոնց հաճախ դժվար է պատասխանել առանց տեսակի ծանոթագրությունների օգտագործման.

  • Կարո՞ղ է այս ֆունկցիան վերադառնալ None?
  • Ինչպիսի՞ն պետք է լինի այս փաստարկը: items?
  • Ո՞րն է հատկանիշի տեսակը id: int սա է, str, կամ գուցե ինչ-որ մաքսային տեսակ:
  • Արդյո՞ք այս փաստարկը պետք է լինի ցանկ: Հնարավո՞ր է դրան տուպլիկ փոխանցել։

Եթե ​​նայեք հետևյալ տիպի ծանոթագրված կոդի հատվածին և փորձեք պատասխանել նմանատիպ հարցերին, կստացվի, որ սա ամենապարզ խնդիրն է.

class Resource:
    id: bytes
    ...
    def read_metadata(self, 
                      items: Sequence[str]) -> Dict[str, MetadataItem]:
        ...

  • read_metadata չի վերադառնում None, քանի որ վերադարձի տեսակը չէ Optional[…].
  • փաստարկ items տողերի հաջորդականություն է։ Այն չի կարող պատահականորեն կրկնվել:
  • Հատկանիշ id բայթերի շարան է:

Իդեալական աշխարհում կարելի է ակնկալել, որ բոլոր նման նրբությունները նկարագրված կլինեն ներկառուցված փաստաթղթերում (docstring): Բայց փորձը բազմաթիվ օրինակներ է տալիս այն փաստի, որ նման փաստաթղթերը հաճախ չեն նկատվում այն ​​օրենսգրքում, որի հետ դուք պետք է աշխատեք: Նույնիսկ եթե օրենսգրքում առկա են նման փաստաթղթեր, չի կարելի հույս դնել դրա բացարձակ ճշգրտության վրա: Այս փաստաթղթերը կարող են լինել անորոշ, ոչ ճշգրիտ և բաց թյուրիմացությունների համար: Խոշոր թիմերում կամ խոշոր նախագծերում այս խնդիրը կարող է չափազանց սուր դառնալ։

Չնայած Python-ը գերազանցում է նախագծերի վաղ կամ միջանկյալ փուլերում, ինչ-որ պահի հաջողված նախագծերը և Python օգտագործող ընկերությունները կարող են կանգնել կենսական հարցի առաջ.

Տիպերի ստուգման համակարգերը, ինչպիսին mypy-ն է, լուծում են վերը նշված խնդիրը՝ ծրագրավորողին տրամադրելով ֆորմալ լեզու՝ տեսակները նկարագրելու համար և ստուգելով, որ տիպի հայտարարագրերը համապատասխանում են ծրագրի իրականացմանը (և ըստ ցանկության՝ ստուգելով դրանց գոյությունը): Ընդհանուր առմամբ, կարելի է ասել, որ այս համակարգերը մեր տրամադրության տակ են դնում մանրակրկիտ ստուգված փաստաթղթերի պես մի բան:

Նման համակարգերի օգտագործումն ունի այլ առավելություններ, և դրանք արդեն բոլորովին աննշան են.

  • Տիպի ստուգման համակարգը կարող է հայտնաբերել որոշ փոքր (և ոչ այնքան փոքր) սխալներ: Տիպիկ օրինակն այն է, երբ նրանք մոռանում են մշակել արժեքը None կամ որևէ այլ հատուկ պայման:
  • Կոդի վերամշակումը շատ պարզեցված է, քանի որ տիպի ստուգման համակարգը հաճախ շատ ճշգրիտ է այն հարցում, թե ինչ կոդը պետք է փոխվի: Միևնույն ժամանակ, մենք կարիք չունենք 100% կոդերի ծածկույթի հույսին թեստերով, ինչը, ամեն դեպքում, սովորաբար իրագործելի չէ։ Խնդիրի պատճառը պարզելու համար մեզ պետք չէ խորամուխ լինել ստեկի հետքի խորքերը:
  • Նույնիսկ խոշոր նախագծերում mypy-ը հաճախ կարող է ամբողջական տիպի ստուգում կատարել վայրկյանի մի մասում: Իսկ թեստերի կատարումը սովորաբար տևում է տասնյակ վայրկյաններ կամ նույնիսկ րոպեներ: Տիպի ստուգման համակարգը ծրագրավորողին տալիս է ակնթարթային արձագանք և թույլ է տալիս ավելի արագ կատարել իր աշխատանքը: Նա այլևս կարիք չունի գրելու փխրուն և դժվար պահպանվող միավորի թեստեր, որոնք փոխարինում են իրական սուբյեկտներին ծաղրերով և կարկատաններով, պարզապես կոդի թեստի արդյունքներն ավելի արագ ստանալու համար:

IDE-ները և խմբագրիչները, ինչպիսիք են PyCharm-ը կամ Visual Studio Code-ը, օգտագործում են տիպի ծանոթագրությունների ուժը՝ ծրագրավորողներին տրամադրելու կոդի լրացում, սխալների ընդգծում և աջակցություն սովորաբար օգտագործվող լեզվական կառուցվածքներին: Եվ սրանք մուտքագրելու առավելություններից միայն մի քանիսն են: Որոշ ծրագրավորողների համար այս ամենը հիմնական փաստարկն է մեքենագրելու օգտին։ Սա մի բան է, որն օգուտ է բերում իրականացումից անմիջապես հետո: Տիպերի օգտագործման այս դեպքը չի պահանջում տիպերի ստուգման առանձին համակարգ, ինչպիսին է mypy-ը, չնայած հարկ է նշել, որ mypy-ն օգնում է տիպի ծանոթագրությունները պահպանել կոդի հետ համապատասխան:

Mypy-ի նախապատմություն

Mypy-ի պատմությունը սկսվել է Մեծ Բրիտանիայում՝ Քեմբրիջում՝ Dropbox-ին միանալուց մի քանի տարի առաջ: Ես ներգրավվել եմ որպես իմ դոկտորական հետազոտությունների մաս՝ ստատիկ տպագրված և դինամիկ լեզուների միավորման մեջ: Ինձ ոգեշնչել է Ջերեմի Սիեկի և Վալիդ Թահայի աստիճանական մուտքագրման մասին հոդվածը և Typed Racket նախագիծը: Ես փորձեցի գտնել միևնույն ծրագրավորման լեզուն տարբեր նախագծերի համար օգտագործելու ուղիներ՝ փոքր սկրիպտներից մինչև միլիոնավոր տողերից բաղկացած կոդերի հիմքեր: Միևնույն ժամանակ, ես ուզում էի ապահովել, որ ցանկացած մասշտաբի նախագծում ստիպված չլիներ գնալ չափազանց մեծ փոխզիջումների։ Այս ամենի կարևոր մասն էր անտիպ նախատիպի նախագծից աստիճանաբար անցնելու գաղափարը համապարփակ փորձարկված ստատիկ տպագրված պատրաստի արտադրանքի: Այս օրերին այս գաղափարները հիմնականում ընդունված են համարվում, սակայն 2010 թվականին դա խնդիր էր, որը դեռ ակտիվորեն ուսումնասիրվում էր:

Տիպի ստուգման իմ բնօրինակ աշխատանքը ուղղված չէր Python-ին: Փոխարենը ես օգտագործեցի փոքրիկ «տնական» լեզու Ալորե. Ահա մի օրինակ, որը թույլ կտա ձեզ հասկանալ, թե ինչի մասին է խոսքը (տեսակի ծանոթագրություններն այստեղ ընտրովի են).

def Fib(n as Int) as Int
  if n <= 1
    return n
  else
    return Fib(n - 1) + Fib(n - 2)
  end
end

Պարզեցված մայրենի լեզվի օգտագործումը գիտական ​​հետազոտություններում օգտագործվող ընդհանուր մոտեցում է: Դա այդպես է, հատկապես այն պատճառով, որ թույլ է տալիս արագ փորձեր կատարել, ինչպես նաև այն պատճառով, որ այն, ինչ կապ չունի հետազոտության հետ, կարելի է հեշտությամբ անտեսել: Իրական աշխարհի ծրագրավորման լեզուները հակված են լինել լայնածավալ երևույթներ՝ բարդ իրականացումներով, և դա դանդաղեցնում է փորձարկումները: Այնուամենայնիվ, պարզեցված լեզվի վրա հիմնված ցանկացած արդյունք մի փոքր կասկածելի է թվում, քանի որ այս արդյունքները ձեռք բերելիս հետազոտողը կարող է զոհաբերել լեզուների գործնական օգտագործման համար կարևոր նկատառումներ:

Իմ տիպի ստուգիչը Alore-ի համար շատ խոստումնալից տեսք ուներ, բայց ես ուզում էի փորձարկել այն իրական կոդով փորձարկելով, որը, կարելի է ասել, գրված չէր Alore-ով: Բարեբախտաբար ինձ համար, Alore լեզուն հիմնականում հիմնված էր նույն գաղափարների վրա, ինչ Python-ը: Բավականին հեշտ էր վերափոխել տառատեսակը, որպեսզի այն կարողանա աշխատել Python-ի շարահյուսության և իմաստաբանության հետ: Սա թույլ տվեց մեզ փորձել մուտքագրման ստուգում բաց կոդով Python կոդում: Բացի այդ, ես գրեցի տրանսփայլեր՝ Alore-ում գրված կոդը Python-ի փոխարկելու համար և օգտագործեցի այն թարգմանելու իմ տպաքանակի կոդը: Այժմ ես ունեի Python-ով գրված տիպերի ստուգման համակարգ, որն աջակցում էր Python-ի ենթաբազմությանը, ինչ-որ լեզվի: (Որոշ ճարտարապետական ​​որոշումներ, որոնք իմաստալից էին Alore-ի համար, վատ էին համապատասխանում Python-ին, և դա դեռ նկատելի է mypy կոդերի բազայի որոշ հատվածներում):

Իրականում, իմ տիպային համակարգի կողմից աջակցվող լեզուն այս պահին չի կարող կոչվել Python. այն Python-ի տարբերակ էր՝ պայմանավորված Python 3 տիպի անոտացիայի շարահյուսության որոշ սահմանափակումներով:

Այն նման էր Java-ի և Python-ի խառնուրդի.

int fib(int n):
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Այդ ժամանակ իմ գաղափարներից մեկն էր օգտագործել տիպային անոտացիաները՝ կատարելագործելու կատարողականությունը՝ կազմելով այս տեսակի Python-ը C, կամ գուցե JVM բայթկոդ: Ես հասա կոմպիլյատորի նախատիպ գրելու փուլին, բայց հրաժարվեցի այս գաղափարից, քանի որ տիպի ստուգումն ինքնին բավականին օգտակար էր:

Ես ավարտեցի իմ նախագիծը Սանտա Կլարայում PyCon 2013-ում ներկայացնելով: Այս մասին ես խոսեցի նաև Գվիդո վան Ռոսումի հետ՝ ցմահ պիթոնի բարերար բռնապետի հետ: Նա համոզեց ինձ թողնել իմ սեփական շարահյուսությունը և հավատարիմ մնալ ստանդարտ Python 3-ի շարահյուսությանը: Python 3-ն աջակցում է ֆունկցիաների անոտացիաներին, այնպես որ իմ օրինակը կարող է վերաշարադրվել, ինչպես ցույց է տրված ստորև, ինչը հանգեցնում է սովորական Python ծրագրի:

def fib(n: int) -> int:
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Ես ստիպված էի որոշակի փոխզիջումների գնալ (նախ ուզում եմ նշել, որ ես հորինել եմ իմ շարահյուսությունը հենց այս պատճառով): Մասնավորապես, Python 3.3-ը, որն այն ժամանակվա լեզվի ամենավերջին տարբերակն էր, չէր աջակցում փոփոխական անոտացիաներին: Ես Գուիդոյի հետ էլեկտրոնային փոստով քննարկեցի նման ծանոթագրությունների շարահյուսական ձևավորման տարբեր հնարավորություններ: Մենք որոշեցինք օգտագործել տիպի մեկնաբանությունները փոփոխականների համար: Սա ծառայեց նախատեսված նպատակին, բայց ինչ-որ չափով դժվար էր (Python 3.6-ը մեզ ավելի գեղեցիկ շարահյուսություն տվեց).

products = []  # type: List[str]  # Eww

Տիպային մեկնաբանությունները նույնպես օգտակար են եղել Python 2-ին աջակցելու համար, որը չունի ներկառուցված աջակցություն տիպի ծանոթագրությունների համար.

f fib(n):
    # type: (int) -> int
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Պարզվեց, որ այս (և այլ) փոխզիջումներն իրականում նշանակություն չունեն. ստատիկ մուտքագրման առավելությունները նշանակում էին, որ օգտվողները շուտով մոռացան ոչ կատարյալ շարահյուսության մասին: Քանի որ տիպային ստուգված Python կոդում հատուկ շարահյուսական կոնստրուկցիաներ չեն օգտագործվել, գոյություն ունեցող Python գործիքներն ու կոդի մշակման գործընթացները շարունակել են նորմալ աշխատել, ինչը ծրագրավորողների համար շատ ավելի հեշտ է դարձնում նոր գործիքը սովորելը:

Գվիդոն նաև համոզեց ինձ միանալ Dropbox-ին իմ ավարտական ​​թեզն ավարտելուց հետո: Այստեղից է սկսվում mypy պատմության ամենահետաքրքիր մասը:

Շարունակելի…

Հարգելի ընթերցողներ: Եթե ​​դուք օգտագործում եք Python, խնդրում ենք պատմել մեզ այս լեզվով ձեր մշակած նախագծերի մասշտաբի մասին:

Python կոդի 4 միլիոն տողերի տիպի ստուգման ուղին: Մաս 1
Python կոդի 4 միլիոն տողերի տիպի ստուգման ուղին: Մաս 1

Source: www.habr.com

Добавить комментарий