Невже для Python нарешті зʼявилась бібліотека для побудови нативних UI.
Завдяки Toga можна робити нативні додатки для WIndows, Macos та Linux (нажаль лише GTK). В ідеалі написав один раз - працює одразу у всіх ОС (але це не точно). Також заявлено про підтримку Andoid та iOS, але я не перевіряв.
Toga - це частина проєкту BeeWare.
Завдяки Toga можна робити нативні додатки для WIndows, Macos та Linux (нажаль лише GTK). В ідеалі написав один раз - працює одразу у всіх ОС (але це не точно). Також заявлено про підтримку Andoid та iOS, але я не перевіряв.
Toga - це частина проєкту BeeWare.
👍2🔥1
Останнім часом намагаюсь знайти більше україномовного IT контенту. Але звісно ж що один у полі не воїн, тому створив awesome list на github - можливо хтось наткнеться та захоче його доповнити, ну або просто буде комусь корисним, мене це теж влаштує.
GitHub
GitHub - yakimka/awesome_ukrainian_it_people: Список україномовних youtube, telegram каналів, чатів, блогів, подкастів, тощо.
Список україномовних youtube, telegram каналів, чатів, блогів, подкастів, тощо. - yakimka/awesome_ukrainian_it_people
👍7🔥3
Python 3.13 буде дуже цікавим. Зокрема через те що 3.12 був нудний. Так вийшло тому що у 3.12 увійшло багато підкапотних штук котрі знадобляться core-девам у наступних релізах для того щоб зробити CPython більш швидким.
Зокрема однією з таких фіч буде JIT, котрий Гвідо обіцяє включити у наступний реліз мови. Доречі ця ініціатива дуже цікава з точки зору підходу до неї, бо чому досі пайтон не мав JIT, хоча здається що більшість мов у яких є інтерпретатор його мають (JavaScript, Java, Ruby, та навіть PHP).
Відповідь на це питання досить прагматична і якщо розуміти ким розроблюється Python то досить логічна.
"Класична" реалізація JIT потребує суттєвого ускладнення реалізації інтерпретатора, тому що потребує написання коду на asm й до того ж, якщо у подальшому будуть виявлятись помилки у інтерпретаторі, то доведеться виправляти їх не лише у С-коді, але й у асемблері. Все вищесказане дуже ускладнює підтримку й розробку CPython, що досить критично для проєкту котрий підтримують практично лише волонтери й у який не вкладаються гроші як у V8 компанією Google.
Добре що буквально минулоріч зʼявилась дослідницька робота "Copy-and-patch compilation: a fast compilation algorithm for high-level languages and bytecode" завдяки котрій можна отримати досить швидкий JIT з мінімальним ускладненям реалізації інтерпретатора (більш зрозумілий пост).
Brandt Bucher вже, за два місяці, зумів зробити робочу імплементацію. Вона має такий вплив на кодову базу CPython:
Build time:
- ~700 строк комплексного Python коду
- ~100 строк комплексного С коду
- LLVM залежність
Run time:
- ~300 строк зрозумілого С коду (написанного програмістом)
- ~3000 строк зрозумілого С коду (згенерованого)
- жодних нових залежностей
Тобто основна складність у реалізації JIT міститься у Python коді, що дуже важливо для підтримки проєкту. Ще дуже важливо що хоча мейнтейнери й додали залежність на LLVM - це build-time залежність, тобто для тих хто встановлює Python нічого не зміниться.
А ще, на відміну від "класичної" реалізації - якщо треба буде виправити баг у інтерпретаторі - його треба буде виправити лише один раз, решту згенерує автоматично LLVM.
Таким чином нова версія Python не стане складнішою у підтримці, але всеодно отримає JIT. Щодо очікуваного пришвидшення поки ніякої інформації немає, бо це лише підготовчі роботи, по суті це baseline JIT котрий поки ніякого пришвидшення не приніс, але у розробників є ще майже рік, тому здається що в них все має вийти.
Тут продублюю лінку на відео з поста Гвідо
Зокрема однією з таких фіч буде JIT, котрий Гвідо обіцяє включити у наступний реліз мови. Доречі ця ініціатива дуже цікава з точки зору підходу до неї, бо чому досі пайтон не мав JIT, хоча здається що більшість мов у яких є інтерпретатор його мають (JavaScript, Java, Ruby, та навіть PHP).
Відповідь на це питання досить прагматична і якщо розуміти ким розроблюється Python то досить логічна.
"Класична" реалізація JIT потребує суттєвого ускладнення реалізації інтерпретатора, тому що потребує написання коду на asm й до того ж, якщо у подальшому будуть виявлятись помилки у інтерпретаторі, то доведеться виправляти їх не лише у С-коді, але й у асемблері. Все вищесказане дуже ускладнює підтримку й розробку CPython, що досить критично для проєкту котрий підтримують практично лише волонтери й у який не вкладаються гроші як у V8 компанією Google.
Добре що буквально минулоріч зʼявилась дослідницька робота "Copy-and-patch compilation: a fast compilation algorithm for high-level languages and bytecode" завдяки котрій можна отримати досить швидкий JIT з мінімальним ускладненям реалізації інтерпретатора (більш зрозумілий пост).
Brandt Bucher вже, за два місяці, зумів зробити робочу імплементацію. Вона має такий вплив на кодову базу CPython:
Build time:
- ~700 строк комплексного Python коду
- ~100 строк комплексного С коду
- LLVM залежність
Run time:
- ~300 строк зрозумілого С коду (написанного програмістом)
- ~3000 строк зрозумілого С коду (згенерованого)
- жодних нових залежностей
Тобто основна складність у реалізації JIT міститься у Python коді, що дуже важливо для підтримки проєкту. Ще дуже важливо що хоча мейнтейнери й додали залежність на LLVM - це build-time залежність, тобто для тих хто встановлює Python нічого не зміниться.
А ще, на відміну від "класичної" реалізації - якщо треба буде виправити баг у інтерпретаторі - його треба буде виправити лише один раз, решту згенерує автоматично LLVM.
Таким чином нова версія Python не стане складнішою у підтримці, але всеодно отримає JIT. Щодо очікуваного пришвидшення поки ніякої інформації немає, бо це лише підготовчі роботи, по суті це baseline JIT котрий поки ніякого пришвидшення не приніс, але у розробників є ще майже рік, тому здається що в них все має вийти.
Тут продублюю лінку на відео з поста Гвідо
🔥2❤1
Так ось, Python 3.13. У наступній версії ще мають завести API для сабінтерпретаторів. Чому я на це чекаю? Тому що здається це буде той тип мультипроцесінгу котрим вже можна буде користуватись. У кожного сабінтерпретатора свій GIL, тобто ми зможемо паралельно запускати CPU-bound обчислення на декількох ядрах одразу. Ми правда й зараз це можемо робити через модуль
Доречі у 3.12 цю фічу вже можна помацати якщо імпортувати модуль
Щодо очевидних проблем їх реалізації. Якщо розробники зроблять апі якогось
Контекст:
- Python 3.12 Preview: Subinterpreters
- Python 3.12 Subinterpreters: A New Era of Concurrency
multiprocessing але сабінтерпретатори запускаються швидше ніж новий процес (але звісно повільніше ніж новий тред) і судячи з еспериментів Eric Snow мають менше накладних витрат.Доречі у 3.12 цю фічу вже можна помацати якщо імпортувати модуль
_xxsubinterpreters, але біжати писати продакшн код звісно не треба. Зараз виконувати у сабінтерпретаторі можна лише строки (ну тобто працює мийже як eval) і у 3.13 якраз мають доробити апі щоб можна було виконувати функції у окремому інтерпретаторі, а може додадуть й новий екзекутор до concurrent.futures. Взагалі схоже на те що сабінтерпретатори можуть розвиватись як горутини у Golang, бо shared пам'яті у них нема, а дані передавати можна будет через os.pipe, ну тобто напрошується якийсь механизм типу контекста.Щодо очевидних проблем їх реалізації. Якщо розробники зроблять апі якогось
SubInterpreterPool то їм треба буде очищати стейт сабінтерпретатора поміж викликами, бо якщо цього не робити, то те що буде виконуватись у окремому інтерпретаторі не буде детерміністичним, бо результат може залежати від порядку виконання функцій. Мене також турбує, як вирішать питання імпорту модулів. Тобто коли я хочу запустити функцію у сабінтерпретаторі, яка використовує імпортований модуль, як його автоматично імпортувати на старті? Я сподіваюся, що вони знайдуть розумне рішення, оскільки вимога робити імпорти прямо у функції була б зовсім не найкращим варіантом.Контекст:
- Python 3.12 Preview: Subinterpreters
- Python 3.12 Subinterpreters: A New Era of Concurrency
🔥3
import __hello__
Так ось, Python 3.13. У наступній версії ще мають завести API для сабінтерпретаторів. Чому я на це чекаю? Тому що здається це буде той тип мультипроцесінгу котрим вже можна буде користуватись. У кожного сабінтерпретатора свій GIL, тобто ми зможемо паралельно…
Доречі ось дуже класний пост по темі. Там і про історію і про порівняння сабінтерпретаторів з тредами, процесами, корутинами, про PEP703, про механізми комунікації і тд.
Ну і вишенкою - запуск web фреймворків у сабінтерпретаторах, з no-gil Python (автор заюзав 3.13 альфа із спеціальним флагом). Із цікавого - Django поки так запустити не вийде, тому що Джанго використовує модуль
https://tonybaloney.github.io/posts/sub-interpreter-web-workers.html
Ну і вишенкою - запуск web фреймворків у сабінтерпретаторах, з no-gil Python (автор заюзав 3.13 альфа із спеціальним флагом). Із цікавого - Django поки так запустити не вийде, тому що Джанго використовує модуль
zoneinfo, бо у модулі datetime у С коді використовується глобальний шейред стейт, але з fastapi та flask вже вийшло погратись.https://tonybaloney.github.io/posts/sub-interpreter-web-workers.html
👍1🔥1
А ви ж вже бачили Textual? Це такий фреймворк для написання TUI програм. А я навіть спробував - мені інколи треба дивитись ревізії у CouchDB, але робити це через WEB-морду, чи через API не дуже зручно, а бачити діф між двома ревізіями там взагалі не можна.
Тому я згадав що десь чув про новий модний фреймворк для написання TUI і за пару днів склепав для себе апку. Спочатку щось не дуже добре виходило, але коли я збагнув що треба юзати такий самий підхід як при написанні Vuejs застосунків, то справа пішла значно швидше. Бо як виявилось фішка тут та сама - робиш "тупий компонент" котрий нічого не знає про бізнес-логіку, а шарить лише за відображення, а вже стейтом і логікою рулиш рівнем вище. Звісно якщо б я спочатку прочитав рідмі то одразу б побачив фразу "Textual adds interactivity to Rich with an API inspired by modern web development" яка б мене на ці думки б й наштовхнула, але хто ж ті рідмі читає, правда?
Тож у мене тепер є інструмент для швидкої розробки гарних TUI-апок, за допомогою яких можна трохи підвищити свою продуктивність. Доречі у екосистемі цього фреймворку є тулза для снапшот тестування pytest-textual-snapshot яка дуже непогано працює.
PS. а про те що у стдлібі пайтону є модуль для діфів знали? 🤓
Тому я згадав що десь чув про новий модний фреймворк для написання TUI і за пару днів склепав для себе апку. Спочатку щось не дуже добре виходило, але коли я збагнув що треба юзати такий самий підхід як при написанні Vuejs застосунків, то справа пішла значно швидше. Бо як виявилось фішка тут та сама - робиш "тупий компонент" котрий нічого не знає про бізнес-логіку, а шарить лише за відображення, а вже стейтом і логікою рулиш рівнем вище. Звісно якщо б я спочатку прочитав рідмі то одразу б побачив фразу "Textual adds interactivity to Rich with an API inspired by modern web development" яка б мене на ці думки б й наштовхнула, але хто ж ті рідмі читає, правда?
Тож у мене тепер є інструмент для швидкої розробки гарних TUI-апок, за допомогою яких можна трохи підвищити свою продуктивність. Доречі у екосистемі цього фреймворку є тулза для снапшот тестування pytest-textual-snapshot яка дуже непогано працює.
PS. а про те що у стдлібі пайтону є модуль для діфів знали? 🤓
🔥2
Історія йде по спіралі
До появи ангулярів, реактів та вью сайтики робили на шаблонах. У Джанзі був (і зараз є) свій движок шаблонів, була Jinja, у Ruby on Rails - ERB, у Laravel - Blade. "Фронтенд" писали або самі бекенд розробники, або верстальщики, котрі щось-там розуміли за шаблони. Коли треба був інтерактив, типу кнопочки "Додати в обране" без перезагрузки сторінки - писали скрипти на ванільному JS, але частіш на jquery - робиш запит на бекенд, бекенд там щось опрацьовує, а у відповідь відправляє шматок html, який ти через js вставляєш на сторінку. Але у такому сетапі багато красівостей, котрі хочуть дизайнери, не намалюєш, бо проєкт на jquery, де багацько логіки перетворюється на лапшу зі швидкістю світла. Хоча я на початку свого програмерського шляху працював у фірмі, де ідеєю-фікс було зробити свій продуктовий каталог з фільтрами і пошуком без оновлення сторінок на jquery - воно навіть якось працювало 😄
Потім зʼявились ангуляри з реактами, фронденд розробники стали вже справжніми програмістами, котрі писали свої окремі апки для клієнтів, а дані ганялись по ендпоінтам у json форматі. Осьо воно так працює і досі.
Але зараз звідусіль можна почути, що те, як воно живе, наразі терпіти більш не можна і давайте все писати на htmx. Чули про таке? Ось я у своїй бульбашці про нього чую кожен день на ютубах, редітах, твітерах та подкастах. То що ж воно таке?
htmx - це така js-бібліотека, котра дозволяє викинути на мороз фронтендерів і писати інтерактивні сайтики на шаблонах, як у 2010 році. Ну тобто, тобі взагалі не треба думати про js - просто пишеш
Доречі, у нас у ua домені є маркетплейс, котрий це використовує, не htmx правда, а свою власну аналогічну лібу TwinSpark - це kasta.ua. Зайдіть, відкрийте консоль розробника та подивіться на дані які ганяються мережею. Між іншим, через такий мінімалістичний підхід PageSpeed показує ~66 поінтів на мобайлі, хоча, якщо викинути усю аналітичну мішуру, то буде мабуть усі 90 😅.
Який висновок? Та хз, просто приємно було згадати про часи коли jquery був кращим і єдиним js фреймворком 😂. Ось вам бонусом, якщо щось зацікавило:
- Як працює TwinSpark
- Twinspark.Morph — оновлення HTML без втрати стану
- інтервью з розробником htmx
До появи ангулярів, реактів та вью сайтики робили на шаблонах. У Джанзі був (і зараз є) свій движок шаблонів, була Jinja, у Ruby on Rails - ERB, у Laravel - Blade. "Фронтенд" писали або самі бекенд розробники, або верстальщики, котрі щось-там розуміли за шаблони. Коли треба був інтерактив, типу кнопочки "Додати в обране" без перезагрузки сторінки - писали скрипти на ванільному JS, але частіш на jquery - робиш запит на бекенд, бекенд там щось опрацьовує, а у відповідь відправляє шматок html, який ти через js вставляєш на сторінку. Але у такому сетапі багато красівостей, котрі хочуть дизайнери, не намалюєш, бо проєкт на jquery, де багацько логіки перетворюється на лапшу зі швидкістю світла. Хоча я на початку свого програмерського шляху працював у фірмі, де ідеєю-фікс було зробити свій продуктовий каталог з фільтрами і пошуком без оновлення сторінок на jquery - воно навіть якось працювало 😄
Потім зʼявились ангуляри з реактами, фронденд розробники стали вже справжніми програмістами, котрі писали свої окремі апки для клієнтів, а дані ганялись по ендпоінтам у json форматі. Осьо воно так працює і досі.
Але зараз звідусіль можна почути, що те, як воно живе, наразі терпіти більш не можна і давайте все писати на htmx. Чули про таке? Ось я у своїй бульбашці про нього чую кожен день на ютубах, редітах, твітерах та подкастах. То що ж воно таке?
htmx - це така js-бібліотека, котра дозволяє викинути на мороз фронтендерів і писати інтерактивні сайтики на шаблонах, як у 2010 році. Ну тобто, тобі взагалі не треба думати про js - просто пишеш
hx- атрибути у html, а воно все за тебе саме зробить - сходить на бекенд за шматочком html та вставить його на сторінку. Прикольна штука, фігачиш собі шаблончики, вони чудово кешуються, а дані запрошуються лише за необхідністю. Правда якщо стейкхолдери захочуть якихось космічних фіч на фронтенді, щоб все воно блимало і свистіло тоді доведеться щось вигадувати додаткове. А якщо воно справді масово стрельне? Куди всі фронтендери дінуться? 😅Доречі, у нас у ua домені є маркетплейс, котрий це використовує, не htmx правда, а свою власну аналогічну лібу TwinSpark - це kasta.ua. Зайдіть, відкрийте консоль розробника та подивіться на дані які ганяються мережею. Між іншим, через такий мінімалістичний підхід PageSpeed показує ~66 поінтів на мобайлі, хоча, якщо викинути усю аналітичну мішуру, то буде мабуть усі 90 😅.
Який висновок? Та хз, просто приємно було згадати про часи коли jquery був кращим і єдиним js фреймворком 😂. Ось вам бонусом, якщо щось зацікавило:
- Як працює TwinSpark
- Twinspark.Morph — оновлення HTML без втрати стану
- інтервью з розробником htmx
👍4🔥1
Бажаєте прискорити свій Python скрипт до 1000Х не змінюючи жодного рядку коду? Щоправда не будь який, а лише той що використовує Pandas, ну і звісно лише якщо у вас є Nvidia GPU 😁
Matt Harrison твітнув що розробники у Nvidia зробили пакет которий дозволяє Pandas робити обчислення на GPU. Працює це як у Jupyter так і з консолі командою
Matt Harrison твітнув що розробники у Nvidia зробили пакет которий дозволяє Pandas робити обчислення на GPU. Працює це як у Jupyter так і з консолі командою
python -m cudf.pandas yournoscript.py. Ось такі справи 🙂🔥4🥱1
Ледь не забув - сьогодні ж перший день Advent of Code
Не впевнений що мого терпіння вистачить на весь місяць, але перший тиждень-два виконувати ці завдання доволі цікаво 🙂
Не впевнений що мого терпіння вистачить на весь місяць, але перший тиждень-два виконувати ці завдання доволі цікаво 🙂
👍1🔥1
Ліньки мені сьогодні щось писати, тому трохи поділюсь з вами каналами на ютубчику котрі дивлюсь. Але мої смаки специфічні, тому якщо шо - я попередив
anthonywritescode - це канал здебільшого про пайтон, який веде дуже крутий чувак, котрий зараз працює розробником в Sentry
CodeOpinion - тут про всяки архітектурні штучки у форматі коротких відосиків, досить пізнавально
Clear Code - тут мені подобається дивитись 8-годинні відоси про розробку ігор на pygame. Затягує, але не для кожного 😁
DaFluffyPotato - канал фултайм розробника ігор на pygame. Колись я таки перестану прокрастинувати і зроблю свою гру на цьому двіжку)
Coder Space - це скарб, чувак на пітоні витворяє усілякі штуки із графікою, особливо мені сподобалась серія відосів про відтворення DOOM на python 👍
Miziziziz - ще один канал про гейм-девелопмент, але про пайтон там нічого не має. Цікаво дивитись як автор робить різні ігри, бо ідеї для них у нього досить незвичайні
ThePrimeagen - навряд комусь відкрию Америку, але без цього каналу перелік був би не повний. Він працює у Netflix бтв
anthonywritescode - це канал здебільшого про пайтон, який веде дуже крутий чувак, котрий зараз працює розробником в Sentry
CodeOpinion - тут про всяки архітектурні штучки у форматі коротких відосиків, досить пізнавально
Clear Code - тут мені подобається дивитись 8-годинні відоси про розробку ігор на pygame. Затягує, але не для кожного 😁
DaFluffyPotato - канал фултайм розробника ігор на pygame. Колись я таки перестану прокрастинувати і зроблю свою гру на цьому двіжку)
Coder Space - це скарб, чувак на пітоні витворяє усілякі штуки із графікою, особливо мені сподобалась серія відосів про відтворення DOOM на python 👍
Miziziziz - ще один канал про гейм-девелопмент, але про пайтон там нічого не має. Цікаво дивитись як автор робить різні ігри, бо ідеї для них у нього досить незвичайні
ThePrimeagen - навряд комусь відкрию Америку, але без цього каналу перелік був би не повний. Він працює у Netflix бтв
👍6❤1🔥1
Хм, майже ніколи не мав необхідності використовувати collections.Counter, а сьогодні і в робочому проєкті заюзав бо воно прям лягало класно і у сьогоднішньому завданні Advent of Code також 🤔
Доречі маю звичку час від часу передивлятись доку стдліба пайтона, чи читати доки ліб котрими найчастіше користуюсь. Трапляється знаходити круті штуки про котрі не знав, чи забув.
Ось один з таких цікавих модулів - graphlib. Додали його нещодавно, у 3.9, я тоді реліз ноутс почитав і одразу ж про нього забув, допоки в черговий раз не поліз передивлятись доку.
Автори Piccolo-orm в захваті від такого доповнення 😁
Доречі маю звичку час від часу передивлятись доку стдліба пайтона, чи читати доки ліб котрими найчастіше користуюсь. Трапляється знаходити круті штуки про котрі не знав, чи забув.
Ось один з таких цікавих модулів - graphlib. Додали його нещодавно, у 3.9, я тоді реліз ноутс почитав і одразу ж про нього забув, допоки в черговий раз не поліз передивлятись доку.
Автори Piccolo-orm в захваті від такого доповнення 😁
👏3🔥2👍1
Нещодавно мав невеличку суперечку з другом стосовно того як по замовчуванню пайтон округлює числа з плаваючою точкою.
Контекст: якщо ви перший раз спробуєте попрацювати із вбудованою функцією round то вас може збентежити що вона працює не так як вас вчили округленню у школі (особливо якщо ви ніколи й на інших мовах не намагались цього зробити). У школі нас вчили що числа які закінчуються на .4 округлюються у меншу, а на .5 у більшу сторону, але пайтон робить щось інше round(1.5) == 2 але round(2.5) також 2.
Так що ж це коїться людоньки? Справа у стандартах. Доречі у 2 версії пайтон round робив це “математично”, але у 3 версії це змінили на метод Rounding half to even, цей метод також відомий як банківське округлення. Він описаний у стандарті IEEE 754 (Standard for Floating-Point Arithmetic) і працює так:
Цей спосіб згідно стандарту є дефолтним (принаймні у актуальній редакції на момент розробки 3 версії пайтон) 11 сторінка, параграф 4. Rounding:
Це значить, що коли число розташоване точно посередині між двома найближчими значеннями, обирається значення, у якого найменш значущий біт (LSB) дорівнює нулю. Іншими словами, воно округлює до найближчого парного числа. Тобто у випадку із 2.5:
1. Два найближчі цілі числа до 2.5 - це 2 і 3.
2. 2.5 знаходиться точно посередині між цими двома числами.
3. Згідно з правилом, ми перевіряємо LSB (найменш значущий біт) обох чисел 2 і 3.
3.1 Двійкове представлення 2 - це 10, де LSB дорівнює 0.
3.2 Двійкове представлення 3 - це 11, де LSB дорівнює 1.
4. Оскільки правило зазначає обирати число з LSB, який дорівнює нулю, 2.5 округлюється до 2.
Банківське округлення допомагає уникнути накопичення помилок, які можуть виникнути при систематичному округленні вгору або вниз і як наслідок допомагає досягти більшої точності в середньому по всіх значеннях. Його часто використовують у фінансах, статистиці та аналізі даних та у мовах програмування. Ось тут є гарне пояснення - Is "banker's rounding" really more numerically stable?
Цікавий факт: одним із перших його адаптував AppleScript, але після багатьох запитів "зробити округлення таким як я його вчив у школі" вони додали команду
Ще у мовах програмування також використовують Rounding half away from zero, тобто так як і вказано у назві - до першого числа дальшого від нуля. Це працює схоже на Rounding half to even але без винятку для парних чисел:
Rounding half up по іншому округлює числа зі знаком мінус:
Мені стало цікаво а як у інших, чи може пайтон тут один так вирішив виділитись. Ось що мені вдалось знайти - перелік мов по обраному дефолтному округленню:
Rounding half to even: Python3, .NET, C#, Haskell, R, Delphi
Rounding half away from zero: C, С++, Objective C, MATLAB, Swift, Perl, Go, Ruby, Rust, Python2
Rounding half up: PHP, JavaScript, Java
Тож що ми дізнались з цього тексту? По-перше це те що не всі слідують стандартам (і ще і дивуються). По-друге, що коли вас просять округлити число - завжди питайте яким методом. Ще мабуть розумним було б зберігати числа у тому виді у якому вони є, а округлювати лише тоді коли віддаєте ці дані у UI.
Ну і якщо вам раптом треба округлити число у пайтоні якимось іншим методом - ви завжди можете це зробити за допомогою стдліба
Лінки:
- Is "banker's rounding" really more numerically stable?
- How .NET's Math.Round has Nothing to do with Maths. And That's OK!
- Python 3.x rounding behavior
- IEEE Standard for Binary Floating-Point Arithmetic
Контекст: якщо ви перший раз спробуєте попрацювати із вбудованою функцією round то вас може збентежити що вона працює не так як вас вчили округленню у школі (особливо якщо ви ніколи й на інших мовах не намагались цього зробити). У школі нас вчили що числа які закінчуються на .4 округлюються у меншу, а на .5 у більшу сторону, але пайтон робить щось інше round(1.5) == 2 але round(2.5) також 2.
Так що ж це коїться людоньки? Справа у стандартах. Доречі у 2 версії пайтон round робив це “математично”, але у 3 версії це змінили на метод Rounding half to even, цей метод також відомий як банківське округлення. Він описаний у стандарті IEEE 754 (Standard for Floating-Point Arithmetic) і працює так:
23.5 => 24, −23.5 => −24
24.5 => 24, -24.5 => -24
Цей спосіб згідно стандарту є дефолтним (принаймні у актуальній редакції на момент розробки 3 версії пайтон) 11 сторінка, параграф 4. Rounding:
if the two nearest representable values are near, the one with its least significant bit zero shall be delivered
Це значить, що коли число розташоване точно посередині між двома найближчими значеннями, обирається значення, у якого найменш значущий біт (LSB) дорівнює нулю. Іншими словами, воно округлює до найближчого парного числа. Тобто у випадку із 2.5:
1. Два найближчі цілі числа до 2.5 - це 2 і 3.
2. 2.5 знаходиться точно посередині між цими двома числами.
3. Згідно з правилом, ми перевіряємо LSB (найменш значущий біт) обох чисел 2 і 3.
3.1 Двійкове представлення 2 - це 10, де LSB дорівнює 0.
3.2 Двійкове представлення 3 - це 11, де LSB дорівнює 1.
4. Оскільки правило зазначає обирати число з LSB, який дорівнює нулю, 2.5 округлюється до 2.
Банківське округлення допомагає уникнути накопичення помилок, які можуть виникнути при систематичному округленні вгору або вниз і як наслідок допомагає досягти більшої точності в середньому по всіх значеннях. Його часто використовують у фінансах, статистиці та аналізі даних та у мовах програмування. Ось тут є гарне пояснення - Is "banker's rounding" really more numerically stable?
Цікавий факт: одним із перших його адаптував AppleScript, але після багатьох запитів "зробити округлення таким як я його вчив у школі" вони додали команду
round 2.5 rounding as taught in school що є валідною командою у Apple Script 😁Ще у мовах програмування також використовують Rounding half away from zero, тобто так як і вказано у назві - до першого числа дальшого від нуля. Це працює схоже на Rounding half to even але без винятку для парних чисел:
23.5 => 24, −23.5 => −24
24.5 => 25, -24.5 => -25
Rounding half up по іншому округлює числа зі знаком мінус:
23.5 => 24, −23.5 => −23
24.5 => 25, -24.5 => -24
Мені стало цікаво а як у інших, чи може пайтон тут один так вирішив виділитись. Ось що мені вдалось знайти - перелік мов по обраному дефолтному округленню:
Rounding half to even: Python3, .NET, C#, Haskell, R, Delphi
Rounding half away from zero: C, С++, Objective C, MATLAB, Swift, Perl, Go, Ruby, Rust, Python2
Rounding half up: PHP, JavaScript, Java
Тож що ми дізнались з цього тексту? По-перше це те що не всі слідують стандартам (і ще і дивуються). По-друге, що коли вас просять округлити число - завжди питайте яким методом. Ще мабуть розумним було б зберігати числа у тому виді у якому вони є, а округлювати лише тоді коли віддаєте ці дані у UI.
Ну і якщо вам раптом треба округлити число у пайтоні якимось іншим методом - ви завжди можете це зробити за допомогою стдліба
Лінки:
- Is "banker's rounding" really more numerically stable?
- How .NET's Math.Round has Nothing to do with Maths. And That's OK!
- Python 3.x rounding behavior
- IEEE Standard for Binary Floating-Point Arithmetic
🔥6👍3
import __hello__
Ледь не забув - сьогодні ж перший день Advent of Code Не впевнений що мого терпіння вистачить на весь місяць, але перший тиждень-два виконувати ці завдання доволі цікаво 🙂
Ну що, івент скінчився, а мене цьогоріч вистачило на вдвічі більший період ніж минулого року 😁
Якщо у минулому році я знудився вже на 12 день, то у цей раз я майже всі завдання зробив, лише з 21 дня не виконував 2 частину бо почались NP-Hard задачі, а я таке не дуже люблю вирішувати.
Але свою долю новорічного настрою я отримав, як і трохи нових знань (котрі ніколи на практиці не застосую звісно). Окреме задоволення продивлятись комуніті івенту на редіті.
Коротше мені сподобалось, треба буде у наступному році повторити. 🎄
Якщо у минулому році я знудився вже на 12 день, то у цей раз я майже всі завдання зробив, лише з 21 дня не виконував 2 частину бо почались NP-Hard задачі, а я таке не дуже люблю вирішувати.
Але свою долю новорічного настрою я отримав, як і трохи нових знань (котрі ніколи на практиці не застосую звісно). Окреме задоволення продивлятись комуніті івенту на редіті.
Коротше мені сподобалось, треба буде у наступному році повторити. 🎄
👍4🔥1
Хочу поділитись невеличким переліком проблем які я найчастіше бачу у тестах
1. Робити мок того що тобі не належить, наприклад http-клієнта
Коли ви мокаєте зовнішній компонент, ви замінюєте його спрощеною версією, яка може не відображати його реальну поведінку. Це призведе до того, що коли у реальному середовищі виникнуть проблеми - ваші тести всеодно будуть зеленими.
2. Мокати через звичайний Mock() а не через create_autospec та альтернативи
Звичайний Mock() не відтворює інтерфейс того що ви мокаєте. Це означає, що ви можете викликати методи, які не існують у реальному об'єкті, або передати неправильну кількість аргументів у функцію чи метод. Тобто, якщо у замоканій функції зміняться аргументи, ваші тести продовжать бути зеленими, хоча реальний код буде падати з помилкою. Якщо мокаєте щось то робіть це зі спеціфікацією
3. Не використовувати фабрику для створення обʼєктів, сервісів, тощо
У кожному тесті створювати обʼєкт напряму через його конструктор, ще й кожного разу окремо мокати його залежності. Натомість краще зробити окрему функцію що буде інстанціювати обʼєкт, або скористатись паттерном Object Mother
4. Вказувати у сетапі неважливі для цього тесту аргументи
Це є наслідком попереднього пункту, коли ви інстанціюєте обʼєкт без фабрики у котрої вже задані дефолтні аргументи то не завжди зрозуміло які саме дані важливі для цього тесту. Ось наприклад:
У Track усі аргументи є обовʼязковими, тож ми не можемо інстанціювати його лише із важлими для цього тесту аргументами. І це лише синтетичний приклад, у реальних проєктах аргументів може бути значно більше, до того ж частина з них може бути іншими сервісами котрі також треба інстанціювати. Було б краще якби ми таки мали фабріку для цього:
У такому разі сетап тесту став значно простіше і ми інстанціюємо обʼєкт лише з важливими даними для нашого тесту
5. Не вказувати у сетапи важливі для цього тесту аргументи
Це якби ми у минулому прикладі замість sut = make_track(artist=None) написали sut = make_track(), адже всеодно у фабриці artist й так по-замовчуванню None. Так робити не треба бо знову ж таки не ясно які дані для цього тесту важливі, а які ні. Вказуючи artist=None явно ми показуємо від яких даних залежить результат
6. Відзеркалювати структуру проєкта у структурі тестів
Дуже часто бачу що струкруту проєкта відзеркалюють у тестах, тобто якщо те що ви тестуєте має шлях app.music_library.services.tracks.suggestions.SuggestionService то класти тест для цього тесту по такому самому шляху у модулі tests не треба. Це а) не зручно, б) якщо ви будете змінювати структуру діректорій проєкта, вам доведеться те саме зробити і з модулем тестів (але звісно ви про це забудете і все стане ще гірше)
Гарний приклад того як треба робити це Django - у них пласка структура модуля tests у якій зручно орієнтуватись
7. Назви тестів які не дають представлення про те що ми тестуємо
Ну тут все зрозуміло. Це якби у прикладі із автором треку тест би називався test_unknown_artist, або взагалі по назві методу test_for_display
Лінки:
- https://docs.python.org/3/library/unittest.mock.html#unittest.mock.create_autospec
- https://martinfowler.com/bliki/ObjectMother.html
- https://github.com/django/django/tree/main/tests
1. Робити мок того що тобі не належить, наприклад http-клієнта
Коли ви мокаєте зовнішній компонент, ви замінюєте його спрощеною версією, яка може не відображати його реальну поведінку. Це призведе до того, що коли у реальному середовищі виникнуть проблеми - ваші тести всеодно будуть зеленими.
2. Мокати через звичайний Mock() а не через create_autospec та альтернативи
Звичайний Mock() не відтворює інтерфейс того що ви мокаєте. Це означає, що ви можете викликати методи, які не існують у реальному об'єкті, або передати неправильну кількість аргументів у функцію чи метод. Тобто, якщо у замоканій функції зміняться аргументи, ваші тести продовжать бути зеленими, хоча реальний код буде падати з помилкою. Якщо мокаєте щось то робіть це зі спеціфікацією
3. Не використовувати фабрику для створення обʼєктів, сервісів, тощо
У кожному тесті створювати обʼєкт напряму через його конструктор, ще й кожного разу окремо мокати його залежності. Натомість краще зробити окрему функцію що буде інстанціювати обʼєкт, або скористатись паттерном Object Mother
4. Вказувати у сетапі неважливі для цього тесту аргументи
Це є наслідком попереднього пункту, коли ви інстанціюєте обʼєкт без фабрики у котрої вже задані дефолтні аргументи то не завжди зрозуміло які саме дані важливі для цього тесту. Ось наприклад:
def test_display_track_author_unknown_if_none():
sut = Track(
name="Icky Thump",
year=2007,
album="Icky Thump",
artist=None
)
result = sut.for_display() # Icky Thump - Icky Thump (2007) - Unknown Artist
assert result.endswith(" - Unknown Artist")
У Track усі аргументи є обовʼязковими, тож ми не можемо інстанціювати його лише із важлими для цього тесту аргументами. І це лише синтетичний приклад, у реальних проєктах аргументів може бути значно більше, до того ж частина з них може бути іншими сервісами котрі також треба інстанціювати. Було б краще якби ми таки мали фабріку для цього:
def make_track(
name: str = "Icky Thump",
year: int = 2007,
album: str = "Icky Thump",
artist: str | None = None,
) -> Track:
return Track(name=name, year=year, album=album, artist=artist)
def test_display_track_author_unknown_if_none():
sut = make_track(artist=None)
result = sut.for_display()
assert result.endswith(" - Unknown Artist")
У такому разі сетап тесту став значно простіше і ми інстанціюємо обʼєкт лише з важливими даними для нашого тесту
5. Не вказувати у сетапи важливі для цього тесту аргументи
Це якби ми у минулому прикладі замість sut = make_track(artist=None) написали sut = make_track(), адже всеодно у фабриці artist й так по-замовчуванню None. Так робити не треба бо знову ж таки не ясно які дані для цього тесту важливі, а які ні. Вказуючи artist=None явно ми показуємо від яких даних залежить результат
6. Відзеркалювати структуру проєкта у структурі тестів
Дуже часто бачу що струкруту проєкта відзеркалюють у тестах, тобто якщо те що ви тестуєте має шлях app.music_library.services.tracks.suggestions.SuggestionService то класти тест для цього тесту по такому самому шляху у модулі tests не треба. Це а) не зручно, б) якщо ви будете змінювати структуру діректорій проєкта, вам доведеться те саме зробити і з модулем тестів (але звісно ви про це забудете і все стане ще гірше)
Гарний приклад того як треба робити це Django - у них пласка структура модуля tests у якій зручно орієнтуватись
7. Назви тестів які не дають представлення про те що ми тестуємо
Ну тут все зрозуміло. Це якби у прикладі із автором треку тест би називався test_unknown_artist, або взагалі по назві методу test_for_display
Лінки:
- https://docs.python.org/3/library/unittest.mock.html#unittest.mock.create_autospec
- https://martinfowler.com/bliki/ObjectMother.html
- https://github.com/django/django/tree/main/tests
👍6
import __hello__
Хочу поділитись невеличким переліком проблем які я найчастіше бачу у тестах 1. Робити мок того що тобі не належить, наприклад http-клієнта Коли ви мокаєте зовнішній компонент, ви замінюєте його спрощеною версією, яка може не відображати його реальну поведінку.…
Ось ще вам дуже старий повчальний комікс про те що треба робити коли ви знаходите багу.
Перед тим як виправляти баг спитайте себе:
1. Чи допускав я цю помилку деінде?
2. Що станеться, коли я виправлю цей баг?
3. Що я можу зробити, щоб цей баг не виник знову?
Лінки:
- The day I started believing in Unit Tests
- Three Questions About Each Bug You Find
Перед тим як виправляти баг спитайте себе:
1. Чи допускав я цю помилку деінде?
2. Що станеться, коли я виправлю цей баг?
3. Що я можу зробити, щоб цей баг не виник знову?
Лінки:
- The day I started believing in Unit Tests
- Three Questions About Each Bug You Find
👍5