Python Type Checker Comparison: Empty Container Inference
კომენტარები
Mewayz Team
Editorial Team
რატომ არღვევს ცარიელი კონტეინერები პითონის ტიპის ჩეკებს — და რა შეგიძლიათ გააკეთოთ ამის შესახებ
Python-ის ეტაპობრივი აკრეფის სისტემა მნიშვნელოვნად მომწიფდა მას შემდეგ, რაც PEP 484-მა შემოიტანა ტიპის მინიშნებები 2015 წელს. დღეს მილიონობით დეველოპერი ეყრდნობა სტატიკური ტიპის ჩეკებს შეცდომების დასაჭერად, სანამ ისინი წარმოებას მიაღწევენ. მაგრამ არსებობს ამ ტიპის სისტემის დახვეწილი, იმედგაცრუებული კუთხე, რომელიც ჯერ კიდევ გამოცდილ ინჟინრებსაც კი ერევა: რა ტიპისაა ცარიელი კონტეინერი? როდესაც თქვენ წერთ x = [] ანოტაციის გარეშე, თქვენი ტიპის გამშვები უნდა გამოიცნოს — და სხვადასხვა ქვები განსხვავებულად გამოიცნობენ. ეს განსხვავებები ქმნის რეალურ პრობლემებს გუნდებისთვის, რომლებიც ინარჩუნებენ დიდი კოდების ბაზებს, სადაც გადართვის ან გაერთიანების ტიპის გამშვები შეიძლება გამოავლინოს ასობით მოულოდნელი შეცდომა ღამით.
ეს სტატია ასახავს, თუ როგორ ამუშავებს ოთხი ძირითადი პითონის ტიპის შემოწმება - mypy, pyright, pytype და pyre - ცარიელი კონტეინერების დასკვნას, რატომ არ ეთანხმებიან ისინი და რა პრაქტიკული სტრატეგიები შეგიძლიათ დაიწეროთ ტიპის უსაფრთხო პითონის დასაწერად, თქვენი ხელსაწყოების არჩევანის მიუხედავად.
ძირითადი პრობლემა: ცარიელი კონტეინერები არსებითად ორაზროვანია
განიხილეთ პითონის ეს უვნებელი ხაზი: შედეგები = []. არის შედეგები სია[int]? სია[str]? სია[dict[str, ნებისმიერი]]? დამატებითი კონტექსტის გარეშე, ნამდვილად არ არის იმის ცოდნა. Python Runtime-ს არ აინტერესებს - სიები ბუნებით ჰეტეროგენულია - მაგრამ სტატიკური ტიპის შემმოწმებლებს სჭირდებათ კონკრეტული ტიპის მინიჭება ყველა ცვლადს, რომ შეასრულონ თავიანთი სამუშაო. ეს ქმნის ფუნდამენტურ დაძაბულობას პითონის დინამიურ მოქნილობასა და გარანტიებს შორის, რომლებსაც სტატიკური ანალიზი ცდილობს.
პრობლემა ერწყმის ლექსიკონებს და კომპლექტს. ცარიელი {} ფაქტობრივად გაანალიზებულია როგორც დიქტა და არა კომპლექტი, რომელიც ამატებს სინტაქსურ ორაზროვნებას ტიპის დონის გაურკვევლობის თავზე. და ჩასმული კონტეინერები — იფიქრეთ defaultdict(list) ან results = {k: [] for k კლავიშებში — აიყვანეთ დასკვნის ძრავები მათ ზღვრამდე. თითოეული ტიპის შემმოწმებელმა შეიმუშავა საკუთარი ევრისტიკა და განსხვავებები უფრო მნიშვნელოვანია, ვიდრე დეველოპერების უმეტესობა აცნობიერებს.
საწარმოო სისტემებში, რომლებიც ამუშავებენ რეალურ დატვირთვას - იქნება ეს CRM, რომელიც ამუშავებს კლიენტთა ჩანაწერებს, ინვოისის მოდული, რომელიც ქმნის ხაზის ერთეულებს, თუ ანალიტიკური მილსადენის აგრეგაციის მეტრიკა - ცარიელი კონტეინერები მუდმივად გამოჩნდება ინიციალიზაციის შაბლონებად. მათი ტიპების არასწორად მიღება არ იწვევს მხოლოდ გაფრთხილებებს; მას შეუძლია შენიღბოს ნამდვილი შეცდომები, რომლებიც გადის გაშვების დრომდე.
Mypy: გადადებული დასკვნა იმპლიციტური ნებისმიერით
Mypy, ყველაზე ძველი და ყველაზე გავრცელებული პითონის ტიპის შემმოწმებელი, შედარებით ლმობიერ მიდგომას იყენებს ცარიელი კონტეინერების მიმართ. როდესაც ის ხვდება x = [] ფუნქციის ფარგლებში, ის ცდილობს გადაიდოს ტიპის გადაწყვეტილება და დაასკვნა ელემენტის ტიპი შემდგომი გამოყენებისგან. თუ დაწერთ x = [], რასაც მოჰყვება x.append(42), mypy დაასკვნის list[int]. ეს „შეერთების“ სტრატეგია საოცრად კარგად მუშაობს მარტივი შემთხვევებისთვის, როდესაც კონტეინერი დასახლებულია იმავე ფარგლებში.
თუმცა, mypy-ის ქცევა მკვეთრად იცვლება კონტექსტისა და სიმკაცრის პარამეტრების მიხედვით. მოდულის ფარგლებს (უმაღლესი დონის კოდი), ან როდესაც კონტეინერი გადაეცემა სხვა ფუნქციას შევსებამდე, mypy ხშირად ბრუნდება სიაში[ნებისმიერი]. --strict დროშის ქვეშ, ეს იწვევს შეცდომას, მაგრამ ნაგულისხმევ რეჟიმში ის ჩუმად გადის. ეს ნიშნავს, რომ გუნდებს, რომლებიც მუშაობენ mypy მკაცრი რეჟიმის გარეშე, შეუძლიათ დააგროვონ ათობით იმპლიციტურად აკრეფილი კონტეინერი, რომლებიც მოქმედებენ როგორც გაქცევის ლუქები ტიპის სისტემისგან და ამარცხებენ მის მიზანს.
ერთი განსაკუთრებით დახვეწილი ქცევა: mypy ვერსიები 0.990-მდე ზოგჯერ ადგენს სიას[უცნობი] შიდა და შემდეგ აფართოებს სიაში[ნებისმიერი] დავალებისას. 0.990-ის შემდეგ დასკვნა გამკაცრდა, მაგრამ ცვლილებამ დაარღვია რეალური სამყაროს კოდების გასაოცარი რაოდენობა, რომლებიც ეყრდნობოდნენ დასაშვებ ქცევას ამის გაცნობიერების გარეშე. ეს განმეორებადი თემაა — ცარიელი კონტეინერების დასკვნაში ცვლილებები ყველაზე დამაბრკოლებელი ტიპის შემოწმების განახლებებს შორისაა, რადგან შაბლონები იმდენად გავრცელებულია.
Pyright: მკაცრი დასკვნა და "უცნობი" ტიპი
Pyright, შემუშავებული Microsoft-ის მიერ და აძლიერებს Pylance-ს VS Code-ში, ფუნდამენტურად განსხვავებულ ფილოსოფიურ პოზიციას იკავებს. იმის ნაცვლად, რომ ჩუმად დაბრუნდეს ნებისმიერ-ზე, pyright განასხვავებს უცნობს (ტიპი, რომელიც ჯერ არ არის განსაზღვრული) და ნებისმიერი (ტიპის შემოწმებაზე აშკარა უარის თქმა). როდესაც თქვენ წერთ x = [] pyright-ის მკაცრი რეჟიმით, ის ასკვნის სიას[უცნობი] და აცნობებს დიაგნოსტიკას, რაც აიძულებს მოგაწოდოთ ანოტაცია.
Pyright ასევე უფრო აგრესიულია შევიწროების ფარგლებში. თუ წერ:
- x = [] მოჰყვება x.append("hello") — pyright ასკვნის list[str]
- x = [] მოჰყვება x.append(1) შემდეგ x.append("hello") — pyright ასკვნის list[int | ქ]
- x = [] პირდაპირ გადაეცემა ფუნქციას, რომელიც ელოდება სიას[int] — pyright ასკვნის სიას[int] ზარის საიტის კონტექსტიდან
- x = [] დაბრუნდა ფუნქციიდან დაბრუნების ტიპის ანოტაციის გარეშე — pyright აცნობებს შეცდომას, ვიდრე გამოცნობას
ეს ორმხრივი დასკვნა (როგორც შემდგომი გამოყენების, ისე მოსალოდნელი ტიპების გამოყენებით ზარის საიტებიდან) pyright-ს უფრო ზუსტს ხდის, ვიდრე mypy ცარიელი კონტეინერებისთვის. კომპრომისი არის სიტყვიერება: pyright-ის მკაცრი რეჟიმი მიუთითებს დაახლოებით 30-40%-ით მეტ საკითხს ტიპიურ უცნობ კოდურ ბაზაზე, ვიდრე mypy-ის მკაცრი რეჟიმი, რამდენიმე ღია კოდის მიგრაციის ანგარიშების ანალიზის მიხედვით. გუნდებისთვის, რომლებიც ქმნიან კომპლექსურ backend სისტემებს - ვთქვათ, პლატფორმა, რომელიც მართავს 207 ურთიერთდაკავშირებულ მოდულს, რომლებიც მოიცავს CRM-ს, სახელფასო და ანალიტიკას - პირატის სიმკაცრე იჭერს ინტერფეისის დახვეწილ შეუსაბამობებს, რაც მსუბუქ დასკვნას გამოტოვებს.
Pytype და Pyre: The Less Traveled გზები
Google-ის pytype ალბათ ყველაზე პრაგმატულ მიდგომას იყენებს. იმის ნაცვლად, რომ მოითხოვოს ანოტაციები ან დაბრუნდეს ნებისმიერ-ზე, 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 →Meta's pyre, იმავდროულად, უფრო ახლოს არის mypy-ის ქცევასთან, მაგრამ უფრო მკაცრი დეფოლტებით. Pyre განიხილავს x = [] როგორც სიას[უცნობი] და მოითხოვს ანოტაციას უმეტეს კონტექსტში. pyre განასხვავებს თავის თავს ცარიელი ლექსიკონის ლიტერალები, რომლებიც გამოიყენება kwargs - ჩვეულებრივი ნიმუში ვებ ჩარჩოებში. Pyre-ს აქვს სპეციალური შემთხვევის ლოგიკა, რათა გამოიტანოს ლექსიკონის ტიპები საკვანძო სიტყვების არგუმენტების კონტექსტებიდან, რაც ამცირებს ანოტაციის დატვირთვას ჩარჩო-მძიმე კოდების ბაზებში. იმის გათვალისწინებით, რომ თანამედროვე ვებ აპლიკაციების უმეტესობა გულისხმობს ლექსიკონის ამოხსნის მძიმე გამოყენებას კონფიგურაციისა და მოთხოვნის განსახორციელებლად, ეს პრაგმატიზმი სარგებლობს.
რეალურ სამყაროზე გავლენა: როდესაც დასკვნის განსხვავება კბენს
ტიპის შემმოწმებლებს შორის განსხვავებები შესაძლოა აკადემიური ჩანდეს მანამ, სანამ მათ არ გამოიცნობთ წარმოების კოდების ბაზაში. განვიხილოთ საერთო ნიმუში ბიზნეს აპლიკაციებში: მონაცემთა სტრუქტურის ინიციალიზაცია, რომელიც დასახლებულია პირობითად.
ყველაზე სახიფათო ცარიელი კონტეინერები არ არის ტიპის Checkers-ის დროშა — ისინი ჩუმად გადიან დასკვნამდე ნებისმიერი ტიპის, რაც საშუალებას აძლევს შეუთავსებელი მონაცემების დაგროვებას გაფრთხილების გარეშე, სანამ ქვედა ნაკადის ფუნქცია არ გაფუჭდება გაშვების დროს TypeError, რომლის უკან დაბრუნება თითქმის შეუძლებელია.
კონკრეტული მაგალითი: ფინტექს სტარტაპის გუნდმა განაცხადა, რომ დახარჯა სამი დღე წარმოების პრობლემის გამართვისთვის სადაც ცარიელი სია, რომელიც ინიციალიზებულია გადახდის დამუშავების ფუნქციაში, დასახელდა, როგორც list[Any] mypy-ის მიერ. სია უნდა შეიცავდეს ათწილად ობიექტებს ვალუტის თანხებისთვის, მაგრამ კოდის ბილიკი ამატებდა float მნიშვნელობებს. მაიპის ლმობიერმა დასკვნამ ჩუმად დაუშვა. შეცდომა მხოლოდ მაშინ გამოჩნდა, როდესაც დამრგვალების შეცდომებმა float არითმეტიკაში გამოიწვია $0.01 შეუსაბამობა 12000 ინვოისის პარტიაში. მათ რომ გამოეყენებინათ pyright მკაცრი რეჟიმით, ან უბრალოდ ცარიელ სიას ანოტირებდნენ, როგორც სიას[ათწილადი], შეცდომა დაფიქსირდა განვითარების დროს.
Mewayz-ში, სადაც პლატფორმა ამუშავებს ინვოისს, სახელფასო გამოთვლებს და ფინანსურ ანალიტიკას 138,000+ მომხმარებლის ანგარიშზე, ასეთი ტიპის უსაფრთხოების ხარვეზი არ არის თეორიული — ეს არის განსხვავება სახელფასო ანგარიშების სწორ გაშვებასა და ძვირადღირებულ ხელახალი გამოთვლებს შორის. მკაცრი აკრეფის დისციპლინა კონტეინერის ინიციალიზაციის ირგვლივ არის ერთ-ერთი იმ „მოსაწყენი“ საინჟინრო პრაქტიკა, რომელიც ხელს უშლის წარმოების საინტერესო ინციდენტს.
საუკეთესო პრაქტიკა თავდაცვითი კონტეინერების ინიციალიზაციისთვის
მიუხედავად იმისა, თუ რომელი ტიპის შემმოწმებელს იყენებს თქვენი გუნდი, არსებობს კონკრეტული სტრატეგიები ცარიელი კონტეინერის გაურკვევლობის სრულად აღმოსაფხვრელად. მიზანია არასოდეს დაეყრდნოთ დასკვნას ცარიელი კონტეინერებისთვის — გახადეთ ტიპი მკაფიო, რათა თქვენი კოდი იყოს პორტატული ყველა საკონტროლოში და დაცულ იყოს დასკვნის ქცევის ცვლილებები ვერსიებს შორის.
- ყოველთვის ჩაწერეთ ცარიელი კონტეინერის ცვლადები. ჩაწერეთ შედეგები: list[int] = [] results = [] ნაცვლად. მცირე სიტყვიერების ღირებულება უმნიშვნელოა გამართვის დაზოგულ დროსთან შედარებით. ეს ერთი პრაქტიკა გამორიცხავს ცარიელი კონტეინერების დასკვნის საკითხთა დაახლოებით 80%-ს.
- გამოიყენეთ ქარხნული ფუნქციები რთული კონტეინერებისთვის. cache = {}-ის ნაცვლად, დაწერეთ ფუნქცია, როგორიცაა def make_cache() -> dict[str, list[UserRecord]]: დაბრუნება {}. დაბრუნების ტიპის ანოტაცია ხდის განკუთვნილ ტიპს ცალსახა და თვითდოკუმენტირებას.
- არატრივიალური ტიპებისთვის აკრიფეთ აკრეფილი კონსტრუქტორები ლიტერალებთან შედარებით. ჩაწერეთ ერთეულები: set[int] = set(), ვიდრე დაეყრდნოთ ნაკრების გაგების დასკვნას. ნაგულისხმევი და მთვლელისთვის ყოველთვის მიუთითეთ ტიპის პარამეტრი: counts: Counter[str] = Counter().
- დააკონფიგურირეთ თქვენი ტიპის შემმოწმებლის მკაცრი რეჟიმი ახალი კოდისთვის. ორივე mypy და pyright მხარს უჭერს თითო ფაილზე ან დირექტორიაში კონფიგურაციას. ჩართეთ ახალი მოდულების მკაცრი შემოწმება, ხოლო ძველი კოდის თანდათანობით მიგრაცია. ეს ხელს უშლის ახალი იმპლიციტურად აკრეფილი კონტეინერების დაგროვებას.
- დაამატეთ ტიპის შემმოწმებელი შედარება თქვენს CI მილსადენს. როგორც mypy, ასევე pyright-ის გაშვება თქვენს კოდების ბაზაზე ადრევე იჭერს დასკვნის განსხვავებას. თუ ნიმუში გადის ერთ შემოწმებას, მაგრამ ვერ ახერხებს მეორეს, ეს არის სიგნალი იმისა, რომ ტიპი საკმარისად მკაფიო არ არის.
დიდი სურათი: ტიპი შემოწმება როგორც გუნდური პრაქტიკა
ცარიელი კონტეინერის დასკვნა საბოლოო ჯამში არის პითონის ტიპის სისტემაში უფრო დიდი გამოწვევის მიკროსამყარო: დაძაბულობა მოხერხებულობასა და უსაფრთხოებას შორის. პითონის ფილოსოფია „ჩვენ ყველანი ვეთანხმებით ზრდასრულს“ მშვენივრად მუშაობს პროტოტიპებისა და სკრიპტებისთვის, მაგრამ წარმოების სისტემას, რომელიც ემსახურება ათასობით მომხმარებელს, უფრო ძლიერი გარანტიები სჭირდება. ის ფაქტი, რომ ოთხი ძირითადი ტიპის გამშვები არ ეთანხმება რაღაც ისეთივე ძირითადს, როგორიც არის [] ტიპი, ხაზს უსვამს, რომ პითონის აკრეფის ეკოსისტემა ჯერ კიდევ მწიფდება.
საინჟინრო გუნდებისთვის, რომლებიც ქმნიან რთულ პლატფორმებს — მიუხედავად იმისა, თქვენ მართავთ რამდენიმე მიკროსერვისს თუ ინტეგრირებულ სისტემას ასობით ურთიერთდაკავშირებული მოდულით, როგორიცაა Mewayz's business OS — პრაქტიკული რჩევა მარტივია: ნუ დაეყრდნობით დასკვნას ცარიელი კონტეინერებისთვის, შეარჩიეთ ტიპის შემმოწმებელი და მკაცრად დააკონფიგურირეთ იგი და მიაქციეთ ტიპის ანოტაციები, როგორც დოკუმენტაცია. სიის[ინვოისის]-ის ნაცვლად დახარჯული ხუთი წუთი []-ის ნაცვლად დაზოგავს საათის გამართვას, როდესაც თქვენი კოდის ბაზის მასშტაბები გახდება.
რადგან PEP 696 (ნაგულისხმევი ტიპის პარამეტრები) და PEP 695 (ტიპის პარამეტრის სინტაქსი) აგრძელებენ პითონის ახალ ვერსიებში ჩამოსვლას, აშკარად აკრეფის ერგონომიკა გაუმჯობესდება. უფსკრული "ანოტირებულ" და "უანოტაციურ" პითონს შორის შემცირდება. მაგრამ იმ დღემდე, კონტეინერების აშკარა ტიპები რჩება Python-ის დეველოპერის ხელსაწყოთა ნაკრების ერთ-ერთ ყველაზე მაღალ ROI პრაქტიკად - მცირე დისციპლინა, რომელიც იხდის კომბინირებულ პროცენტს ყველა მოდულში, ყველა სპრინტსა და წარმოების ყველა განლაგებაში.
შექმენით თქვენი ბიზნესის OS დღეს
დაწყებული შტატგარეშე მომუშავეებიდან დაწყებული სააგენტოებით დამთავრებული, Mewayz ახორციელებს 138000+ ბიზნესს 207 ინტეგრირებული მოდულით. დაიწყეთ უფასოდ, განაახლეთ, როცა გაიზრდებით.
შექმენითუფასოWe use cookies to improve your experience and analyze site traffic. Cookie Policy