Java проти Python: базовий Python для Java розробників

Java проти Python: базовий Python для Java розробників

  • 30 травня, 2022
  • читати 20 хв

Це переклад статті Яна Хайна Бюрмана на сайті Real Python.

У цьому посібнику ви вивчите Python з точки зору Java, а після прочитання ви зможете вирішити, чи підходить Python для вирішення ваших завдань, і оцінити, коли можна використовувати Python у поєднанні з Java.

Цей посібник призначений для розробників, знайомих з роботою Java, концепціями, термінологією, класами, типами, структурою колекцій та іншим, але вам не потрібно мати досвід роботи з Python.

Звідки взявся Python?

Python — це мова програмування, розроблена програмістом Гвідо ван Россумом. Він шукав, чим зайнятися під час різдвяних свят 1989 року, і саме тоді він розпочав розробку інтерпретатора Python.

Python бере свій початок у ряді мов: ABC, C та Modula-3. По суті це об'єктно-орієнтована імперативна мова програмування.

Залежно від ваших уподобань та бажаної функціональності її можна застосовувати у повністю об'єктно-орієнтованому стилі або у стилі процедурного програмування з функціями.

На початку 2021 року TIOBE вчетверте оголосила Python мовою програмування року. Відповідно до звіту Octoverse за 2021 рік, Python посідає друге місце серед найпопулярніших мов на GitHub серед учасників репозиторію.

Яка філософія Python?

Деякі ідеї, що лежать в основі Java та Python, схожі, але кожна мова програмування має свої унікальні характеристики.

Філософія Python представлена у вигляді набору дев'ятнадцяти керівних принципів, Zen of Python.

А ще у Python є кілька пасхальних яєць. Розглянемо, що відбувається, коли ви вводите наступну команду в циклі читання-оцінки-друку Python (REPL):

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Розглянувши керівні принципи Zen of Python, ви отримаєте гарне уявлення, як ви можете підходити до роботи з мовою.

Код Python читається

Якщо ви маєте досвід роботи з Java і дивитеся на типовий фрагмент коду Python, ви можете подумати, що дивитеся на псевдокод. Є кілька факторів, які сприяють цьому:

  • Відступ використовується для групування операторів. Це робить блоки коду коротшими і сприяє єдиному стилю кодування.
  • Декілька вбудованих високорівневих структур даних у поєднанні зі скромним набором символів операторів роблять Python дуже виразним.
  • Вибір використання винятків як основний спосіб обробки помилок забезпечує чистоту коду.
  • Програмісти Python віддають перевагу стилю, натхненному концепцією «Легше попросити вибачення, ніж дозволу» (EAFP), а не концепцією «Подивися, перш ніж стрибнути» (LBYL).

Python включає стандартну бібліотеку

Ціль Python полягає в тому, щоб ви могли вирішити більшість повсякденних проблем, використовуючи тільки стандартний дистрибутив Python.

З цією метою Python містить так звану стандартну бібліотеку. Як і бібліотека класів Java, це велика колекція корисних засобів, що складається з констант, функцій, класів та фреймворків.

Python просуває ідею повторного використання коду

Python надає кілька функцій, які дозволяють розробляти код, який можна повторно використовувати у різних місцях, щоб застосувати принцип «Не повторюйся» (DRY).

Однією з особливостей є те, що ви зазвичай розбиваєте свій код на модулі та пакети всередині Python. Зверніть увагу, однак, що модулі та пакети Python відрізняються від модулів та пакетів Java.

Інший метод, який ви можете використовувати в Python, це об'єктно-орієнтоване програмування.

Ви також можете використовувати декоратори для зміни функцій, класів чи методів Python. Це ще один прийом, що дозволяє запрограмувати функціональність лише один раз, після чого її можна використовувати з будь-якої функції, класу чи методу, які ви декорували.

Python легко розширюється

Підтримка модулів та пакетів є одним із компонентів, що дозволяє легко розширювати Python новими функціями. Ви також можете визначити нову або адаптовану поведінку для стандартних операторів та функцій Python, перевантаживши їх. Можна навіть впливати на те, як створюються класи.

Найпростіший спосіб розширити Python — написати код на чистому Python. Ви також можете визначати модулі, використовуючи прив'язки на спрощеному діалекті Python, що називається Cython, а також C або C++.

Як почати знайомство з Python?

У цьому посібнику ви знайдете приклади, які можуть спонукати вас вивчити мову або розглянути фрагменти коду Python.

Як Java-розробник, ви, можливо, пам'ятаєте свої перші кроки з вивчення Java та встановлення вашого першого Java Development Kit. Так само, якщо ви хочете почати роботу з Python, вам спочатку потрібно встановити її, а потім створити пісочницю, де ви зможете безпечно експериментувати.

Вже є кілька посібників, які пояснюють, як це зробити, тому такі підрозділи вкажуть вам ці ресурси.

Установка Python

Перший крок — встановити останню версію Python. Для цього дотримуйтесь цього посібника з встановлення та налаштування Python 3.

Ще одне місце, де ви можете знайти інструкції з встановлення, — це офіційна сторінка завантаження Python.

Примітка. Переконайтеся, що у вас встановлена остання версія Python. На момент написання цього посібника найостаннішою версією є остання версія виправлення серії 3.10.x. Фрагменти коду, показані в цьому посібнику, повинні працювати з цією версією Python.

Багато розробників Python роблять свій внесок у бібліотеки, що підтримують різні версії Python, і часто воліють випробувати попередню версію Python, не порушуючи своєї звичайної роботи з Python.

У таких ситуаціях зручно мати доступ до кількох версій Python на одному комп'ютері. Інструмент, що забезпечує цю функціональність, pyenv, схожий з jEnv в Java.

Створення пісочниці та її використання

Як другий крок вам слід налаштувати віртуальне середовище, щоб ви могли безпечно використовувати переваги екосистеми Python з відкритим вихідним кодом. У цьому розділі пояснюється, як і чому ви повинні це робити.

Хоча Python поставляється з великою стандартною бібліотекою з різними функціями, ще більше можливостей доступне у вигляді зовнішніх пакетів, переважна більшість яких мають відкритий вихідний код. Індекс пакетів Python, або скорочено PyPI, є основним центральним репозиторієм, який збирає та надає ці пакети. Ви можете встановити пакети за допомогою команди pip. Однак перш ніж це зробити, прочитайте наступні два абзаци.

Щоб уникнути конфліктів версій залежностей, зазвичай не слід спільно використовувати глобальне чи особисте встановлення Python між проектами. На практиці ви супроводжуєте кожен проект чи експериментальну пісочницю віртуальним середовищем.

Таким чином ваші проекти залишаються незалежними. Цей підхід також запобігає конфліктам версій між пакетами. Якщо ви хочете дізнатися більше про цей процес, то ви можете детально прочитати про те, як створювати та активувати свої віртуальні середовища.

Вибір редактора чи інтегрованого середовища розробки

Вирішіть, який редактор або IDE ви хочете використати. Якщо ви звикли до IntelliJ, PyCharm здається логічним вибором, оскільки він належить до тієї ж лінійки продуктів. Ще одним популярним редактором є Visual Studio Code, але ви також можете вибирати багато інших варіантів.

Після встановлення Python та зовнішніх пакетів у віртуальне середовище, а також вибору редактора чи IDE, ви можете почати експериментувати з мовою.

Чим Python відрізняється від Java?

У наступних підрозділах ви познайомитеся з найважливішими відмінностями Python від Java.

Відступ для групування блоків коду

Можливо, найбільш яскравою особливістю Python є синтаксис.

Вказівка його функцій, класів, конструкцій керування потоком та блоків коду дуже відрізняється від того, до чого ви звикли. У Java ви вказуєте блоки коду за допомогою відомих фігурних дужок ({ and }). Однак у Python блоки коду вказуються рівнем відступу.

Тут ви бачите приклад, який демонструє, як відступ визначає групування блоків коду:

def parity(number):
    result = "odd"                              # Function body
    if number % 2 == 0:
        result = "even"                         # Body of if-block
    return result                               # Not part of if-block

for num in range(4):                            # Not part of function
    print("Number", num, "is", parity(num))     # Body of for-loop
print("This is not part of the loop")           # Not part of for-loop

Код відображає кілька нових концепцій:

  • Рядок 1: оператор def починає визначення нової функції під назвою parity(), яка приймає аргумент з ім'ям number. Зверніть увагу, що якби оператор def з'явився б у блоці визначення класу, натомість він запустив би визначення методу.
  • Рядок 2: Всередині parity() тіло функції починається лише на рівні з відступом. Перший оператор — це присвоєння рядка "odd" змінної result.
  • Рядок 3: Тут ви бачите початок оператора if.
  • Рядок 4: Додатковий відступ починає новий блок. Цей блок виконується, коли умовний вираз оператора number % 2 == 0 оцінюється як дійсний. У прикладі він складається лише з одного рядка, в якому ви надаєте значення "even" змінної result.
  • Рядок 5: Відступ, що передує оператору return, вказує на кінець оператора if і пов'язаного з ним блоку.
  • Рядок 7: Так само ви бачите відступ, який передує початку циклу for. Отже, цикл for починається з того ж рівня відступу, що початок блоку визначення функції в першому рядку. Він зазначає кінець блоку визначення функції.
  • Рядок 8: Ви бачите, що те саме знову відбувається всередині циклу for. Перший виклик функції print() є частиною блоку циклу for.
  • Рядок 9: Цей другий виклик функції print() з відступом не є частиною блоку циклу for.

Ви могли помітити, що двокрапка (:) в кінці рядка позначає новий підблок коду, який повинен мати відступ на один рівень. Цей блок коду закінчується, коли наступний оператор знову отримує відступ.

Блок коду повинен складатися щонайменше з одного оператора. Порожній блок коду просто неможливий. У тих окремих випадках, коли оператор не потрібен, ви можете використовувати оператор pass, який взагалі нічого не робить.

Нарешті, ви, ймовірно, також помітили, що для коментарів можна використовувати решітки #.

Цей приклад коду приведе до такого висновку:

Number 0 is even
Number 1 is odd
Number 2 is even
Number 3 is odd
This is not part of the loop

Хоча такий спосіб визначення блоків, на перший погляд, може здатися дивним, до нього можна звикнути.

Є корисний посібник із стилю коду Python під назвою PEP 8.

Рекомендується використовувати рівень відступу у чотири позиції за допомогою пробілів. Посібник із стилю радить не використовувати табуляції у файлах вихідного коду.

Причина в тому, що різні редактори та системні термінали можуть використовувати несумісні позиції табуляції та відображати код по-різному для різних користувачів або між різними операційними системами.

Примітка. Посібник із стилю є прикладом пропозиції щодо покращення Python, або скорочено PEP. PEP не тільки містять пропозиції, але й відображають специфікації для реалізації, так що ви можете порівняти PEP з об'єднанням JEP та JSR у Java. PEP 0 перераховує індекс PEP.

У PEP 8 можна знайти багато цікавої інформації, включаючи угоди про імена Python, вони трохи відрізняються від Java.

Цикл читання-оцінки-друку від початку

З самого початку Python завжди мав вбудований цикл читання-оцінки-друку (REPL).

REPL зчитує максимально короткий повний оператор, вираз або блок, компілює його в байт-код та виконує його оцінку. Якщо код, що оцінюється, повертає об'єкт, відмінний від об'єкта None, він виводить однозначне уявлення цього об'єкта.

Примітка. Ви можете порівняти Python REPL із Java JShell (JEP 222), який доступний з JDK 9.

Наступний фрагмент показує, як працює Python REPL:

>>> zero = int(0)
>>> zero
0
>>> float(zero)
0.0
>>> complex(zero)
0j
>>> bool(zero)
False
>>> str(zero)
'0'

Як бачите, інтерпретатор завжди намагається однозначно показати значення виразу. У наведеному вище прикладі видно, як по-різному відображаються цілі, плаваючі, комплексні, логічні і рядкові значення.

Різниця між Java та Python полягає в операторі присвоєння (=). Звичайне привласнення Python, в якому використовується один знак рівності, є оператором, а не виразом, який видає якесь значення або об'єкт.

Це пояснює чому REPL не відображає присвоєння змінної zero: оператори завжди оцінюються як None. Рядок, що йде за привласненням, містить вираз змінної, zero, щоб вказати REPL відображати змінну у будь-якому випадку.

Примітка. У Python 3.8 з'явився оператор виразу привласнення (:=), також відомий як оператор «морж». Він надає значення змінним, але, на відміну звичайного привласнення, обчислює їх як і вираженні. Це схоже на оператор присвоєння Java.

Однак він не повністю взаємозамінний із звичайним оператором привласнення, його можливості дуже обмежені.

У REPL спеціальне змінне підкреслення (_) містить значення останнього виразу, якщо воно не None.

Наступний фрагмент показує, як ви можете використовувати цю спеціальну змінну:

>>> 2 + 2
4
>>> _
4
>>> _ + 2
6
>>> some_var = _ + 1
>>> _
6
>>> some_var
7

Після того, як ви надасте значення some_var, спеціальна змінна _ як і раніше буде зберігати значення 6. Це тому, що оператор присвоювання оцінюється як None.

Динамічно типізований та суворо типізований

Важливою характеристикою мови програмування є те, коли і як інтерпретатор мови або компілятор виконує перевірку типів.

Python — це мова з динамічною типізацією. Це означає, що типи змінних, параметри функцій та значення функцій, що повертаються, перевіряються під час виконання, а не під час компіляції, на відміну від Java.

Python в той же час є строго типовою мовою:

  • Кожен об'єкт має певний тип, пов'язаний із ним
  • Потрібне явне перетворення між несумісними типами

Ось так Python перевіряє сумісність типів під час виконання та робить типи сумісними:

>>> 40 + "2"
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>> 40 + int("2")  # Add two numbers
42
>>> str(40) + "2"  # Concatenate strings
'402'
>>> 40 * "2"       # Repeat string "2" forty times
'2222222222222222222222222222222222222222'

Ви помітите, що не можна просто додати ціле значення до рядкового значення. Коли інтерпретатор виявляє помилку часу виконання, він створює виняток. REPL перехоплює екземпляри Exception і показує зворотне трасування, що веде до помилкового вираження.

Щоб вирішити цю проблему, ви конвертуєте один тип в інший. Якщо ви хочете скласти два об'єкти разом як числа, ви перетворюєте рядок, що представляє число, у просте число за допомогою конструктора int(). Якщо замість цього ви хочете об'єднати два об'єкти у вигляді рядків, ви перетворюєте число на рядок за допомогою конструктора str().

В останньому рядку показано ще одну функцію. Помножуючи послідовність на число, ви отримуєте конкатенований результат вихідної послідовності, що повторюється заданим числом.

Хоча Python — це мова з динамічною типізацією, в код можна додати інструкції типів.

Під час виконання Python тільки робить інструкції доступними для самоаналізу. Проте існують інструменти статичної перевірки типів, які можуть виявляти невідповідності між оголошеннями типів та фактичним використанням функцій, класів та змінних з анотаціями типів.

Примітка. Як зазначено вище, середовище виконання Python робить анотації типів доступними для самоаналізу коду. Деякі бібліотеки використовують цю інформацію, наприклад FastAPI.

Інструкції типів допомагають виявляти помилки на ранній стадії циклу розробки. Особливо у великомасштабних проектах вони допомагають зробити код зручнішим для супроводу та підтримувати кодову базу у хорошому стані. Зазвичай ви спричиняєте засіб перевірки статичного типу як частину кроку перевірки у конвеєрі складання. Більшість IDE також використовують інструкції типів.

CPython проти компілятора JIT

На відміну від Java, еталонна реалізація CPython немає JIT-компілятора. CPython — це компілятор та інтерпретатор, написаний на C та доступний практично на всіх платформах.

CPython завантажує вихідний файл, який називається модулем, у два етапи:

  1. Компіляція: спочатку CPython зчитує код і компілює його в байт-код, що є послідовністю інструкцій, яку може виконати інтерпретатор байт-коду CPython. Певною мірою ви можете порівняти фазу компіляції з тим, як javac компілює файл .java у файл .class.
  2. Виконання: Інтерпретатор байт-коду CPython, тобто віртуальна машина (ВМ) CPython, послідовно виконує байт-код з першого кроку.

Примітка. На відміну від Java, ви не можете припускати, що той самий байт-код Python буде працювати з іншими реалізаціями Python або навіть між їх різними версіями. Однак це допомагає скоротити час, необхідний для завантаження модуля.

Скомпільовані модулі, якщо це можливо, зберігаються в каталозі кеша.

На відміну від основних реалізацій Java VM, CPython не компілює байт-код у власний об'єктний код.

Однак є й інші реалізації Python, які працюють по-іншому:

  • Існує реалізація Python Java під назвою Jython. Вона працює в JVM і існує пряма сумісність між Java і Python.
  • Крім того, існує версія під назвою IronPython, яка працює на платформі .NET.
  • Існує реалізація, що використовує компілятор Just-in-Time (JIT) під назвою PyPy. У середньому PyPy у 4,2 рази швидше, ніж CPython.
  • Нарешті, GraalVM — це високопродуктивне середовище виконання, яке підтримує безліч мов програмування. Воно забезпечує експериментальну підтримку відносно недавньої версії Python.

Наведений вище список не є вичерпним. Сайт Python містить список альтернативних реалізацій та дистрибутивів.

Вбудована функція та навантаження оператора

Як Java розробник, ви можете знати термін навантаження через перевантаження методів.

Хоча в Python є динамічний еквівалент, що забезпечує в чомусь схожу функціональність, існує ще один вид навантаження, який може здатися набагато кориснішим.

Ви можете визначити нову поведінку ваших спеціально створених класів для будь-яких відповідних вбудованих функцій та операторів Python.

Примітка. У цьому контексті відповідними функціями та операторами можна вважати ті, які дозволяють перевантажувати їхню поведінку.

Python надає доступний спосіб досягнення навантаження функцій та операторів.

Ви можете спробувати його, визначивши у своєму класі методи зі спеціальними іменами. Ім'я такого методу починається і закінчується двома символами підкреслення, наприклад .__len__() або .__add__(). Ідентифікатор з такою назвою називається дандер, скорочення від подвійного підкреслення (__).

Коли ви викликаєте допустиму вбудовану функцію з об'єктом, для якого є відповідний метод dunder, Python делегує поведінку цьому методу. Аналогічно, коли ви використовуєте оператор, для якого один або кілька операндів містять відповідний метод dunder, Python делегує поведінку цьому методу.

Наприклад, можна визначити .__len__() поведінку вбудованої функції len(). Так само ви можете задати поведінку .__add__() для оператора додавання (+).

Ця функція дозволяє застосовувати красивий, виразний і лаконічний синтаксис коду Python не тільки до стандартних об'єктів, але і до об'єктів користувача.

Хороший синтаксис для функціональної обробки колекцій

У Java ви могли створювати списки, комбінуючи виклики map(), filter() та лямбда-виразів. Ви можете зробити те саме в Python, використовуючи ті ж функції та методи, хоча використання цих конструкцій часто ускладнює код.

Python надає елегантний альтернативний синтаксис для цієї базової функціональної маніпуляції зі списками та іншими колекціями.

Все є об'єктом

В Java не все є об'єктом, незважаючи на те, що код можна помістити тільки всередині класу Java. Наприклад, примітив Java 42 не є об'єктом.

Як і Java, Python також повністю підтримує об'єктно-орієнтований стиль програмування. Відмінність від Java в тому, що в Python все є об'єктом.

Деякі приклади об'єктів Python:

Оскільки вони є об'єктами, ви можете зберігати все це в змінних, передавати їх та аналізувати під час виконання.

Примітка. Класи є об'єктами. Оскільки об'єкти за визначенням є екземплярами класів, класи також мають бути екземплярами чогось, у цьому випадку це екземпляри метакласу.

Стандартним метакласом є тип, але ви можете створювати альтернативні метакласи, зазвичай похідні від типу, щоб змінити спосіб створення класів.

Метакласи у поєднанні з можливістю навантаження вбудованих функцій та операторів є однією з причин того, що Python вважається універсальним набором інструментів для програмування.

Вони дозволяють вам створювати власний програмований всесвіт додаткової або альтернативної поведінки класів та екземплярів.

Які аспекти Java та Python схожі?

Незважаючи на відмінності, ви, ймовірно, вже помітили деяку подібність між Java та Python. Це тому, що і Python, і Java ґрунтуються на мові C.

Орієнтація об'єктів на основі класів

Python — це об'єктно-орієнтована мова програмування, яка базується на класах, які також є однією з основних особливостей Java. Однак набір об'єктно-орієнтованих функцій відрізняється в обох мовах.

Оператори

Одним із аспектів, у якому ви можете помітити загальну спадщину мов, є те, як вони використовують оператори. Багато хто з них мають однакове значення в обох мовах.

Для початку порівняйте відомі арифметичні оператори Java та Python.

Оператор складання (+), оператор віднімання (-), оператор множення (*), оператор поділу (/) та оператор за модулем (%) мають майже однакове призначення в обох мовах, за винятком оператора поділу для цілісних операндів.

Те саме відноситься і до побітових операторів: побітового оператора OR (|), побітового оператора AND (&), побітового оператора XOR (^) і унарного побітового оператора NOT (~), а також до операторів побітового зсуву для зсуву вліво (<<< ) і зсув праворуч (>>).

Ви можете використовувати синтаксис квадратних дужок ([]) у Python для доступу до елемента послідовності так само, як ви можете працювати з доступом до масиву Java.

Форматування рядків

Спочатку Python використовував функції форматування рядків, що базуються на тому, як сімейство функцій printf обробляє їх у мові програмування C.

Це схоже на Java String.format(). У Python цю функцію виконує оператор %. Ліва частина оператора містить рядок формату, а права частина містить кортеж позиційних параметрів, або словник ключових параметрів.

Ось приклад:

>>> "Hello, %s!" % "world"             # %-style, single argument
'Hello, world!'
>>> "The %s is %d." % ("answer", 42)   # %-style, positional
'The answer is 42.'
>>> "The %(word)s is %(value)d." \
... % dict(word="answer", value=42)    # %-style, key-based
'The answer is 42.'

Нещодавно Python прийняв інші способи форматування рядків.

Один з них полягає у використанні рядкового методу .format(), в якому поля, що заміщаються, позначаються фігурними дужками ({}). Прикладом цього є "The {word} is {value}.".format(word="answer", value=42).

Починаючи з Python 3.6, ви також можете використовувати форматовані рядкові літерали, також відомі як f-рядки.

Припустимо, у вас є дві змінні області видимості з іменами word і value. У цьому випадку вираз f"The {word} is {value}." відображає вам той самий рядок, що й у .format() у наведеному вище прикладі.

Конструкції потоку керування

Конструкції потоку управління аналогічні при порівнянні Java та Python. Це означає, що можна інтуїтивно розпізнати багато конструкцій потоку управління. Однак більш детальному рівні також є відмінності.

Цикл Python while схожий на цикл Java:

while (word := input("Enter word: ")) != "END":
    print(word)

print("READY")

Фрагмент коду рядок за рядком копіює стандартне введення до стандартного виводу, поки рядок не стане рівним "END". Цей рядок не копіюється, "READY" натомість записується текст, за яким слідує новий рядок.

Ви, напевно, помітили додаткову цінність оператора Walrus у такій конструкції. Пріоритет цього оператора є найнижчим серед усіх операторів. Це означає, що вам часто потрібно додавати круглі дужки навколо виразу привласнення, коли воно є частиною більшого виразу, як Java.

Примітка. У Python немає конструкції циклу do {...} while (...)

Цикл Python for схожий на цикл for-each у Java.

Це означає, що якщо ви хочете, наприклад, перебрати список із перших п'яти римських цифр, ви можете закодувати його, використовуючи аналогічну логіку:

>>> roman_numerals = "I II III IV V".split()
>>> roman_numerals
['I', 'II', 'III', 'IV', 'V']
>>> for numeral in roman_numerals:
...     print(numeral)
...
I
II
III
IV
V

Використання str.split() — це зручний спосіб створення списку слів.

Примітка. Таким чином можна виконувати ітерацію не тільки по екземпляру list, але і по будь-якому об'єкту, що ітерується.

Іноді натомість вам може знадобитися лічильник пробігу. У цьому випадку ви повинні використовувати range():

>>> for i in range(5):
...     print(i)
...
0
1
2
3
4

У цьому прикладі i посилається на наступне значення запитаного діапазону при кожній ітерації. Це значення згодом друкується.

У рідкісному випадку, коли ви хочете перебрати колекцію і в той же час хочете мати поряд з нею поточний лічильник, ви можете використати enumerate():

>>> for i, numeral in enumerate("I II III IV V".split(), start=1):
...     print(i, numeral)
...
1 I
2 II
3 III
4 IV
5 V

У наведеному прикладі показана функціональність двох попередніх прикладів, об'єднаних в один цикл. За замовчуванням супутній лічильник починається з нуля, але за допомогою необов'язкового ключового аргументу start можна вказати інше значення.

Python також розуміє оператори break та continue.

Іншою конструкцією потоку управління, схожою на Java, є оператор if:

>>> for n in range(3):
...     if n <= 0:
...         adjective = "not enough"
...     elif n == 1:
...         adjective = "just enough"
...     else:
...         adjective = "more than enough"
...     print(f"You have {adjective} items ({n:d})")
...
You have not enough items (0)
You have just enough items (1)
You have more than enough items (2)

Як було продемонстровано вище, конструкція Python if...else також підтримує ключове слово elif, що корисно, оскільки тут немає простого оператора switch...case.

Примітка. Нещодавно випущена версія Python 3.10 містить нову функцію під назвою структурне зіставлення зі зразком, яка вводить ключові слова match і case, але поводиться зовсім інакше, ніж оператор switch в Java.

Ця нова мовна функція надихнута оператором зіставлення зі зразком у Scala, ще одній мові програмування, що працює на JVM.

Хоча на перший погляд багато конструкцій кодування виглядають однаково, між ними все ж таки є багато відмінностей.

Наприклад, цикли Python, а також конструкції перехоплення винятків підтримують частину else:. Крім того, Python надає оператор with для менеджерів контексту.

Java проти Python: що таке нативні типи даних високого рівня?

У наступних підрозділах ви знайдете короткий огляд стандартних типів Python. Основна увага приділяється тому, чим ці типи або пов'язані з ними оператори відрізняються від Java, або як вони порівнюються з відповідним класом колекції Java.

Числові типи та їх оператори

Python пропонує безліч числових типів на вибір відповідно до вашої конкретної області застосування. Він має три вбудовані числові типи:

Якщо ви порівняєте обидві мови, то виявите, що цілі числа Python можуть містити довільні довгі значення, обмежені лише обсягом (віртуальної) пам'яті, доступною на вашій машині. Ви можете думати про них як про розумну суміш власних цілих чисел фіксованої точності — або примітивних цілих типів, як їх називає Java — і BigInteger чисел Java з наступними наслідками

  1. Ви маєте всі зручності цілих чисел довільної точності, і ви можете використовувати з ними всі відомі символічні оператори.
  2. Python застосовує швидку цілісну арифметику з фіксованою точністю до наданих значень, коли значення досить малі для цього.

Використовуючи префікси 0x, 0o, та 0b, ви можете вказувати цілі числа Python як шістнадцяткові, вісімкові та двійкові константи відповідно.

Примітка. Це означає, що вісімкові числа не мають префіксу з одним або декількома початковими нулями (0), що відрізняється від Java.

Відомі арифметичні оператори +, -, *, / та % у Java та Python мають однакове значення в обох мовах, за винятком оператора поділу для цілих типів.

Оператор truediv (/), застосований до операндів int, дає значення float в Python, яке відрізняється від Java. Python має оператор floordiv (//) для поділу, який округляє до найближчого цілого числа, порівнянного з оператором поділу (/) в Java:

>>> 11 / 4          # truediv
2.75
>>> 11.0 // 4       # floordiv, despite float operand(s)
2.0

Крім того, Python надає оператор подвійної зірочки (**) для зведення в ступінь як альтернатива функції з двома аргументами pow(). Оператор matmul (@) зарезервований як додатковий оператор типів, що надаються зовнішніми пакетами, призначений для зручності позначення множення матриць.

І Python, і Java перейняли побітові оператори з мови програмування C. Це означає, що побітові оператори (|, &, ^ та унарні ~) мають однакове значення в обох мовах програмування.

Якщо ви хочете використовувати ці оператори для негативних значень, то корисно знати, що в Python цілі числа представлені як два додаткових значення в концептуально нескінченному просторі для зберігання бітів.

Це означає, що концептуально негативні значення мають нескінченну кількість початкових 1 бітів, так само, як концептуально позитивні числа мають нескінченну кількість початкових 0 бітів:

>>> bin(~0)
'-0b1'
>>> bin(~0 & 0b1111)       # ~0 is an "infinite" sequence of ones
'0b1111'

Фрагмент коду вище показує, що незалежно від вибраного вами значення, якщо ви виконуєте побітове AND із цим значенням з константою ~0, то значення дорівнює вибраному значенню. Це означає, що константа ~0 концептуально є нескінченною послідовністю 1 бітів.

Також доступні оператори бітового зсуву (<< and >>). Однак еквівалента побітового оператора зсуву вправо із заповненням нулями (>>>) в Java не існує, оскільки він не має сенсу в системі числення з довільними довгими цілими числами. У межах досяжності немає найзначнішого біта. Інша різниця між обома мовами полягає в тому, що Python не дозволяє зрушувати біти з негативним числом зрушень.

Стандартна бібліотека Python також надає інші цифрові типи. Є десяткова арифметика decimal.Decimal з фіксованою та плаваючою комою, яку можна порівняти з Java BigDecimal.

Існує клас fractions.Fraction для раціональних чисел, який можна порівняти з Apache Commons Math Fractions. Зверніть увагу, що ці типи не класифікуються як вбудовані цифрові типи.

Основні типи послідовностей

Типи послідовностей — це контейнери, в яких ви можете отримати доступ до їх елементів, використовуючи цілі індекси. Рядки та послідовності байтів також є типами послідовностей. Вони представлені декількома розділами пізніше.

Python має три вбудовані основні типи послідовностей:

Як бачите, синтаксична різниця між ініціалізаторами списку та кортежу полягає у квадратних дужках ([]) та круглих дужках (()).

Список в Python схожий на список ArrayList в Java і змінюється. Зазвичай такий контейнер використовується для однорідної колекції, як Java. Однак у Python можна зберігати об'єкти незв'язаних типів.

Кортеж, з іншого боку, більше схожий на незмінну версію Pair-подібного класу в Java, за винятком довільної кількості записів замість двох. Порожні дужки (()) позначають порожній кортеж. Конструкція like (3) позначає кортеж, що містить один елемент. І тут єдиним елементом є 3.

Діапазон дає послідовність чисел, які зазвичай використовують в циклах. Ви бачили приклад цього раніше в посібнику.

Щоб вибрати елемент з послідовності, ви можете вказати індекс, що відраховується від нуля, у квадратних дужках, як у some_sequence[some_index]. Негативні індекси відраховуються у зворотному порядку від кінця, тому -1 позначають останній елемент.

Ви також можете вибрати фрагмент із послідовності. Це вибір нуля, одного або кількох елементів, що дає об'єкт того ж типу, що й вихідна послідовність. Ви можете вказати значення start, stop та step, також відоме як крок. Приклад синтаксису нарізки є some_sequence[<start>:<stop>:<step>].

Всі ці значення є необов'язковими, і на практиці використовуються значення за промовчанням, якщо не зазначено інше.

Для позитивних індексів синтаксис Python схожий на те, як ви вибираєте елементи в масивах Java.

Ви можете об'єднати більшість послідовностей за допомогою оператора зі знаком плюс (+) та повторити їх за допомогою оператора зірочки (*):

>>> ["testing"] + ["one", "two"] * 2
['testing', 'one', 'two', 'one', 'two']

Ви не можете виконати конкатенацію або повторення послідовності діапазонів, але ви можете їх розрізати.

Наприклад, спробуйте range(6, 36, 3)[7:2:-2] і подумайте, що у вас вийде.

Словники

Словник dict у Python схожий на словник Java LinkedHashMap. Синтаксис константи ініціалізатора dict є послідовністю розділених комами записів key: value між фігурними дужками ({}). Приклад цього є {"pi": 3.14, "e": 2.71}.

Щоб вибрати елемент зі словника або іншого порівняння, ви можете вказати ключ у квадратних дужках ([]), як у math_symbols["pi"]. І ключі, і значення можуть бути будь-якими об'єктами, але ключі повинні бути хешовані, що означає, що вони зазвичай незмінні або, принаймні, повинні поводитися як незмінні об'єкти. Ключі не обов'язково мають бути одного типу, хоча зазвичай це так. Те саме стосується і цінностей.

Набори

Python також пропонує набори. Ви можете ініціалізувати набір за допомогою синтаксису, подібного {"pi", "e"}, або використовуючи синтаксис конструктора set(), використовуючи об'єкт, що ітерується, як аргумент. Щоб створити порожній набір, ви використовуєте вираз set(), тому що літерал {} вже було передано словникам.

Набори використовують хешування у базовій реалізації. Коли ви перебираєте набір, майте на увазі, що елементи будуть з'являтися у випадковому порядку, як в Java. Більше того, порядок може навіть змінитись між окремими викликами Python.

Деякі оператори були перевантажені для операцій над безліччю.

Рядки

Як і в Java, рядки в Python є незмінними послідовностями елементів Unicode. Рядкові літерали вказуються між подвійними лапками ("), або ви також можете вказати їх між одинарними лапками ('), що відрізняється від Java.

Два приклади рядків: "foo" та 'bar'. Різні лапки не обов'язково мають різне значення щодо рядка. Однак слід зазначити, що якщо ви використовуєте будь-які лапки як частину рядка, вам доведеться екранувати їх, якщо вони також є роздільниками рядка.

Як і в Java, зворотна коса риса (\) в Python — це символ, який вводить керуючу послідовність. Інтерпретатор Python розпізнає escape-послідовності, також відомі Java, такі як \b, \n, \t, і кілька додаткових з мови програмування C.

За замовчуванням Python використовує кодування UTF-8 для вихідних файлів Python. Це означає, що ви можете помістити літерал Unicode безпосередньо в рядок, як у випадку "é". Ви також можете використовувати 16- або 32-бітове шістнадцяткове представлення його кодової точки. Для "é", ви б зробили це, використовуючи escape-послідовності \u00E9 або \U000000E9. Зверніть увагу на різницю між малими і великими Uescape-послідовностями. Нарешті, також можна вказати його опис у форматі Unicode, наприклад, \N{Latin Small Letter E with acute}.

Ви можете використовувати символи Unicode навіть в ідентифікаторах Python, але тоді виникає питання, чи це доцільно робити.

Якщо ви ставите перед рядком префікс r, наприклад, r"raw\text", зворотна коса риса втрачає своє особливе значення. Це особливо зручно, коли ви хочете вказати на регулярні висловлювання.

Ви також можете укладати свої рядки в потрійні лапки як зручний спосіб створення багаторядкових рядків, як у наступному прикладі:

>>> s = """This is a
...        multiline
...        string.
...        """
...
>>> for line in s.splitlines():
...     print(repr(line))
...
'This is a'
'       multiline'
'       string.'
'       '

Цей тип рядка можна порівняти з текстовими блоками Java (JEP 378), хоча з іншими синтаксичними обмеженнями та іншим збереженням пробілів (табуляції, пробіли та символи нового рядка).

Байти

У Java, якщо вам потрібно зберігати двійкові дані, а не текст, ви, ймовірно, використовували б ByteBuffer, що дає вам об'єкти, що змінюються. У Python об'єкти bytearray надають аналогічну функціональність.

На відміну від Java, Python також пропонує тип bytes для зберігання незмінних двійкових даних. Байтові літерали дуже схожі на рядкові літерали, крім того, що перед літералом ставиться префікс b. Рядки містять .encode() метод для перетворення їх у послідовність байтів, а об'єкт bytes містить метод .decode() для перетворення в рядок:

>>> bytes(4)                     # Zero-filled with a specified length
b'\x00\x00\x00\x00'
>>> bytes(range(4))              # From an iterable of integers
b'\x00\x01\x02\x03'
>>> b = "Attaché case".encode()  # Many different codecs are available
>>> b
b'Attach\xc3\xa9 case'
>>> b.decode()                   # Decode back into a string again
'Attaché case'

Якщо кодек не вказаний, для кодування рядків та декодування байтів використовується кодек UTF-8 за замовчуванням. Коли потрібно, ви можете вибрати з великого списку кодеків, які забезпечують всі види перетворення тексту і байтів.

Об'єкти bytes у Python також мають метод .hex(), який створює рядок, в якому вміст буде відображатися у шістнадцятковому вигляді. Для зворотної операції можна використовувати метод класу .fromhex() для створення об'єкта bytes з шістнадцяткового рядкового представлення.

Логічні оператори

False та True є двома об'єктами-екземплярами bool у Python. У числовому контексті оцінюється True до 1 та False до 0. Це означає, що True + True оцінюється як 2.

Логічні оператори Python відрізняються від операторів &&, || та ! у Java. У Python це зарезервовані ключові слова and, or та not.

Ви бачите це узагальнено у таблиці нижче:

Як і в Java, існує коротке замикання в оцінці логічних операторів and та or, коли інтерпретатор Python оцінює операнди зліва направо, доки зможе визначити істинність всього висловлювання.

Ще одна подібність з Java полягає в тому, що інтерпретатор повертає як результат останній обчислений підвираз. Отже, ви повинні знати, що результат виразу and або or не обов'язково дає об'єкт-примірник bool.

Всі об'єкти Python мають або хибне або справжнє значення.

Іншими словами, коли ви конвертуєте об'єкти Python у bool, результат чітко визначений:

  • Числові значення, рівні 0, перетворюються на False і на True в іншому випадку
  • Порожні контейнери, колекції, рядки та байтові об'єкти перетворюються на False і на True
  • Об'єкт None також перетворюється на False
  • Всі інші об'єкти оцінюються як True

Примітка. Визначені користувачем класи можуть надавати метод dunder .__bool__() для визначення достовірності екземплярів свого класу.

Якщо ви хочете перевірити, чи контейнер або рядок є непустим, ви просто надаєте цей об'єкт у логічному контексті. Це вважається пітонічним підходом.

Ознайомтеся з різними способами перевірки непустого рядка:

>>> s = "some string"
>>> if s != "":                 # Comparing two strings
...     print('s != ""')
...
s != ""

>>> if len(s) != 0:             # Asking for the string length
...     print("len(s) != 0")
...
len(s) != 0

>>> if len(s):                  # Close, but no cigar
...     print("len(s)")
...
len(s)

>>> if s:                       # Pythonic code!
...     print("s")
...
s

В останньому прикладі ви просто надаєте рядок у логічному контексті. Якщо рядок не порожній, він оцінюється як істина.

Примітка. Вищевикладене означає, що Pythonic використовує неявне перетворення bool для всіх видів типів. Розділ нижче None присвячений цьому докладніше.

Ви можете дотримуватися шляху навчання Write More Pythonic Code, якщо хочете дізнатися більше про найбільш типові конструкції Python.

У Python ви кодуєте умовний вираз, написаний на Java з умовним оператором (? :), як вираз із ключовими словами if і else:

Розглянемо приклад такого типу виразу в Python:

>>> for n in range(3):
...     word = "item" if n == 1 else "items"
...     print(f"Amount: {n:d} {word}")
...
Amount: 0 items
Amount: 1 item
Amount: 2 items

REPL виводить "item" тільки тоді, коли n дорівнює 1. У всіх інших випадках REPL виводить "items".

None

Python None — це одноелементний об'єкт, який можна використовувати для ідентифікації нульових значень. У Java ви б використали літерал null для аналогічних цілей.

Найчастіше None у Python використовується як значення параметра за замовчуванням у визначеннях функцій або методів. Крім того, функції або методи, які не повертають жодного значення, насправді неявно повертають об'єкт None.

Загалом це вважається запахом коду, коли ви покладаєтеся на неявне перетворення None у логічному контексті через ризик того, що ви закодуєте ненавмисну ​​поведінку для інших типів об'єктів, які повернуть хибне значення.

Отже, якщо ви хочете перевірити, чи дійсно об'єкт є об'єктом None, ви повинні зробити це очевидно. Оскільки є тільки один об'єкт None, ви можете зробити це за допомогою оператора ідентифікації об'єкта is або протилежного оператора is not:

>>> some_value = "All" or None

>>> if some_value is None:
...     print(f"is None: {some_value}")

>>> if some_value is not None:
...     print(f"is not None: {some_value}")
...
is not None: All

Майте на увазі, що слово not є невід'ємною частиною оператора is not і, зокрема, воно відрізняється від логічного оператора not.

У цьому прикладі рядок "All" має справжнє значення у логічному контексті. Ви також можете згадати, що оператор or веде себе як коротке замикання і просто повертає останній вираз, як тільки результат стає відомим, як у даному випадку "All".

Інші типи даних контейнера

Java пропонує свої стандартні типи контейнерів через структуру колекцій.

Python використовує інший підхід. Він пропонує базові типи контейнерів, які ви досліджували раніше в цьому розділі як вбудовані типи, а потім стандартна бібліотека Python надає ще кілька типів даних контейнерів через модуль collections.

Є багато корисних прикладів типів контейнерів, до яких можна отримати доступ через модуль collections:

  • namedtuple надає кортежі, в яких ви також можете отримати доступ до елементів через імена полів
  • deque забезпечує двосторонні черги зі швидким додаванням та видаленням на обох кінцях колекції
  • ChainMap дозволяє згорнути кілька об'єктів зіставлення в один вид зіставлення
  • Counter забезпечує зіставлення для підрахунку об'єктів, що хешуються.
  • defaultdict надає зіставлення, яке викликає фабричну функцію для надання відсутніх значень

Ці типи контейнерів даних були реалізовані лише в простому Python.

На цьому етапі у вас є хороша основа для розуміння подібностей та відмінностей між функціями, синтаксисом та типами даних Java та Python.

Тепер настав час зробити крок назад і вивчити доступні бібліотеки та фреймворки Python і з'ясувати, наскільки вони підходять для конкретних випадків використання.

Корисні та популярні бібліотеки або фреймворки Python

Ви можете використовувати Python у багатьох областях.

Нижче ви знайдете деякі з цих областей разом з найбільш корисними і популярними бібліотеками або фреймворками Python:

Сценарії командного рядка: argparse забезпечує функціональні можливості для створення аналізатора аргументів командного рядка.

Веб-фреймворки:

  • Django пропонує трохи більш компактний підхід, коли справа доходить до розробки повних та потенційно складних веб-сайтів. Він включає можливість визначати моделі, пропонує власне рішення ORM і надає повний набір функцій адміністратора. Можна додати додаткові плагіни для подальшого розширення адмінки.
  • Flask позиціонує себе як мікрофреймворк, який добре справляється з одним завданням - обслуговування веб-запитів. Ви можете комбінувати цю основну функціональність з іншими вже існуючими компонентами на ваш вибір, такими як ORM і перевірка форми. Багато розширень, відомих як плагіни Flask, доступні для зручної інтеграції цих компонентів.
  • Requests роблять надсилання HTTP-запитів надзвичайно зручним.

Моделювання та аналіз даних: pandas, заснований на NumPy, — це швидкий, потужний, гнучкий та простий інструмент для аналізу та обробки даних з відкритим вихідним кодом. Деякі називають панд «програмованою електронною таблицею на стероїдах».

Машинне навчання: TensorFlow, Keras та PyTorch — кілька популярних фреймворків у галузі машинного навчання.

Набори інструментів SQL та об'єктно-реляційні перетворювачі (ORM): SQLAlchemy — дуже популярний набір інструментів Python SQL та структура ORM.

Розподіл робочого навантаження: Celery — це розподілена система черги завдань.

У Python також є кілька інструментів, що заслуговують на увагу, пов'язаних із забезпеченням якості :

  • pytest — чудова альтернатива стандартній бібліотеці unittest.
  • behave це популярний інструмент розробки, заснований на поведінці (BDD). Ви можете комбінувати його з PyHamcrest для більш виразних перевірок тверджень.
  • Flake8 — це програма для перевірки посібника зі стилю написання коду.
  • Pylint — це інструмент, який перевіряє наявність помилок у коді Python та виявляє стандартні відхилення кодування.
  • Black — безкомпромісний реформатор коду, що важко налаштовуюється. Хоча він може здатися жахливим, це насправді чудовий інструмент для будь-якого великомасштабного програмного проекту.
  • mypy — програма перевірки статичних типів, що найбільш широко використовується.
  • Bandit знаходить поширені проблеми із безпекою.
  • Safety перевіряє встановлені залежності від відомих вразливостей.
  • tox — інструмент командного рядка, який допомагає запускати автоматичні тести та перевірки інструментів QA, визначені для вашого проекту, за допомогою однієї команди, а також для кількох версій Python та конфігурацій залежностей.

Наведені вище списки — це лише невелика частина доступних пакетів і фреймворків.

Коли Python буде кориснішою за Java і чому?

Часто вибирають мову програмування для одного набору завдань та іншу мову програмування для іншого.

При порівнянні Java та Python слід враховувати наступні аспекти:

  • І Java, і Python успішно використовуються у найбільших веб-додатках світу.
  • Ви також можете використовувати Python для написання інструментів оболонки.
  • Елегантний синтаксис Python, зручність читання коду, велика бібліотека і велика колекція зовнішніх пакетів забезпечують швидку розробку. Ймовірно, вам потрібно менше половини рядків коду, щоб досягти тієї ж функціональності, що і Java.
  • Оскільки стандартний Python не вимагає кроків компіляції або компонування, ви відразу побачите результати під час оновлення коду. Це ще більше прискорює час циклу розробки.
  • У більшості випадків для загальних програм стандартна швидкість виконання Java вище, ніж у Python.
  • Ви можете розширити Python за допомогою C або C++ відносно невеликими зусиллями. Певною мірою це нівелює відмінності у швидкості виконання.

Для певних завдань, таких як моделювання даних, аналітика, машинне навчання та штучний інтелект, швидкість виконання справді має значення. Для цих областей Python здається найбільш логічним вибором.

Висновок

У цьому посібнику ви познайомилися з Python і отримали чітке уявлення про властивості цієї мови. Ви вивчили подібність і різницю між Java і Python

Тепер у вас є деякий досвід швидкого початку роботи з Python та розуміння того, в яких ситуаціях та для яких областей корисно застосовувати Python.

А підтягнути свою Java можна на курсах Java.

Вдалого коду!

Рекомендуємо курс по темі