858

Python для початківців: типи даних та як з ними працювати

Про Python

Python – це високорівнева мова програмування з об'єктно-орієнтованою парадигмою, основу якої складають об'єкти та класи. Об'єкт є виділену область пам'яті, описану класом (він же тип) і значенням. Тип визначає область значень, які може приймати об'єкт, а також застосовні до нього операції та методи.

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

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

Сувора динамічна типізація в Python – що це?

Python є мовою зі строгою динамічною типізацією. Приставка "сувора" означає, що Python не виконує неявні перетворення типів, завдяки чому не виникає неприємних "сюрпризів" при їх змішуванні.

Розглянемо, що це означає практично. 

 

 

Для цього запустимо однаковий код у Python та JavaScript:

// classic JavaScript sample
let someNumber = 1 + "1"
console. log('someNumber: ' + someNumber )
CONSOLE X
someNumber:11

JS просто об'єднає різні типи даних в один рядок і видасть результат '11', а Python повідомить про помилку та видасть таку помилку:

>>> # No-no-nо!
...some_number = 1 + "1"
Traceback (most recent call last):
File "/usr/lib/python3.8/code.py", Line 90, in runcode
exec(code, self.locals)
File "<input>", line 2, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

На перший погляд може здатися, що JavaScript вчинив логічніше, та й взагалі дає розробникам більше свободи дій. Але насправді така поведінка може призвести до непередбачуваних наслідків, особливо якщо говорити про великі скрипти на кілька тисяч рядків коду. Наприклад, було б прикро якби банківський сервіс списав із картки не 50+50 = 100 доларів, а 5050 доларів.

У Python така ситуація неможлива. Суворий інтерпретатор миттєво видасть помилку і дозволить програмісту змішати типи даних.

Слово «динамічна» у назві свідчить, що типи об'єктів визначаються під час виконання програми, тобто у режимі runtime. Отже, програмісту не потрібно обов'язково прописувати тип кожної змінної. До речі, у мові Python змінні є лише вказівниками на об'єкти, і самі собою не містять інформації про тип.

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

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

>>> # Let's try integer
... year_of_birth = 1999
>>> year_of_birth
1999
>>> # And now let's redefine year-of-birth - "ningggen ninety five"
>>> year_of_birth
'nineteen ninety five'

Для порівняння, у мовах зі статичною типізацією, наприклад, таких як C++ або Java, типи об'єктів визначаються на етапі компіляції. Отже, якщо ви захочете запустити цей код на Java, то зіткнетеся з помилкою:

int year of birth = 5;
System.out.println(year_of_birth);
//And now let's redefine
year_of_birth = "nineteen ninety five";
System.out.println(year_of_birth);

/home/user/IdeaProjects/BackEnd_Freel_Chat/src/main/java/Main.java:10:25
java: incompatible types: java. lang. String cannot be converted to int

І зрештою, щоб виконати код вам доведеться створювати нову змінну:

int year_of_birth = 5;

System.out.println(year_of_birth);

// Let's create new variable str_year_of_birth

String str_year_of_birth = "nineteen ninety five";

System.out.println(str_year_of _birth);


/usr/lib/jvm/java-1.11.0-openjdk-amd64/bin/java ...
5
nineteen ninety five

Procces finished with exit code 0

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

Що таке типи даних, що змінюються і незмінні

У мові Python всі існуючі типи даних можна розділити на змінні та незмінні. Розглянемо як це працює.

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

Перевіримо практично:

>>> # Immutable data types
... number = 1
>>> # Check number id in memory
... id(number)
9445472
>>> # Redefine number
... number = 6
>>> # Check number id again. It changed
... id(number)
9445632

З іншим id:

>>> # Immutable data types
... int_object = 10
>>> # Check number id in memory 
... id(int_object)
9445760
>>> # Let's change 1t 
... int_object += 5
>>> id(int_object)
9445920

На початку ми маємо змінну int_object зі значенням 10. Коли ми намагаємося додати до неї 5, створюється новий об'єкт із тим самим ім'ям. Зверніть увагу, що перший об'єкт id дорівнює #140271560307328, а другий — #140271560307328.

А тепер розглянемо, як у тій самій ситуації поведеться список:

>>> # List is mutable
... list_object = [10, 20, 30]
>>> # Check list_object id in memory
id(list_object)
140271560307328
>>> # Let's change it
... list_object += [40]
>>> # And check id again
... id(List_object)
140271560367328

Як бачимо, ідентифікатор списку list_object залишився таким самим. Це пов'язано з тим, що в Python списки змінюються об'єкти.

Підводячи рису слід зазначити, що до незмінних об'єктів у мові Python відносяться:

  • числа;
  • рядки;
  • кортежі.

А до змінених:

  • списки;
  • словники;
  • множини.

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

Вбудовані типи даних у Python

Інтерпретатор Python містить багато типів даних. Їх можна поділити на три групи:

  • прості - числа та рядки;
  • колекції - списки, кортежі та словники;
  • інші - файли, ітератори, сокети, NaN.

При написанні коду рекомендується віддавати перевагу вбудованим типам. Вони є оптимізованими структурами даних на мові С, завдяки чому більш ефективні в порівнянні з класами користувача.

Числа в Python

Програмне забезпечення мовою Python може працювати кількома типами числових значень:

  • int - цілі числа;
  • float - числа з плаваючою точкою, вони ж десяткові дроби;
  • complex – комплексні числа.

В іншому числові об'єкти в Python підтримують такі ж операції, як і інші мови програмування - додавання, множення, поділ:

>>> a = 20.5
... b = 10
...
... # Sum
... sum_a_b = a + b
>>> sum_a_b
30.5
>>> # MultipLication 
... mult_a_b = a + b
>>> mult_a_b
205.0
>>> # Division
... div_a_b = a / b
>>> div_a_b
2.05

Крім того, стандартна бібліотека Python містить модуль math, що підключається директивою import math. Він оснащений великим набором функцій для роботи з числовими значеннями:

>>> import math
>>> a = 20.5
>>> b = 10
>>> # factorial of b
... math_factor = math. factorial(b)
>>> math_factor
3628800
>>> # remainder of the division
... math_fmod = math. fmod(a, b)
>>> math_fod
0.5
>>> # cut off the fractional part
... math_trunc = math.trunc(a)
>>> math_trunc
20

Також варто окремо поговорити про спеціальний тип long у Python, який дозволяє працювати з числами необмеженої довжини. Для порівняння в C++ найбільший тип long long обмежується 64 бітами, а значить найбільше позитивне число тут дорівнює 18446744073709551615.

У Python такої межі немає. Як приклад спробуємо отримати якесь дуже велике число.

Для цього зведемо 27 до 560-го ступеня:

>>> print (27 ** 569)
366191221537348906724224962113233696161029018222927370716739871087661400220838316

>>>

Рядки в Python

Строки, тип string — это последовательности символов, к которым может применяться большинство методов других последовательностей: списков и кортежей.

Рядки, тип string - це послідовності символів, до яких може застосовуватися більшість методів інших послідовностей: списків та кортежів.

Ось основні операції з рядками, які доступні в Python:

  • конкатенація рядків - "склеювання" декількох рядків в один;
  • довжина рядка - функція, що підраховує кількість символів у рядку і повертає відповідне числове значення;
  • вилучення елемента за індексом - функція, що повертає значення елемента з потрібним індексом - позицією символу всередині рядка;
  • зріз рядка — витяг з одного символу або цілого фрагмента.

Крім того, рядки Python підтримують роботу з негативними індексами, які зручно використовувати, щоб звертатися до елементів в кінці списку. Наприклад, у виразі box_slice = some_string[-5:] ми викликаємо п'ять останніх символів рядка some_string.

Також тип string має набір унікальних методів, які актуальні тільки для нього, наприклад:

  • пошук підрядка;
  • пошук підрядки із заміною;
  • поділ рядка по роздільнику.

Списки в Python

Списки в мові Python є впорядкованими колекціями об'єктів. Вони можуть мати необмежену кількість рівнів вкладеності та зберігати будь-яку кількість об'єктів. Також один список може одночасно зберігати об'єкти різних типів.

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

  • додавання нового елемента до кінця списку;
  • видалення елемента з потрібним індексом;
  • сортування елементів у потрібному порядку - зростання, спадання та інше.

Кортежі в Python

Кортежі, або tuple - це такі ж списки, що тільки незмінні. Над ними можна проводити ті самі операції, як і над списками, за винятком тих, що змінюють кортеж.

Розглянемо практично:

>>> some_tuple = ('p', 'y', 't', 'h', 'o', 'n')
>>> # Get last element
... last_element = some_tuple[-1]
>>> last_element
'n'
>>> # slice from 0 to 3 index
... tuple_stice = some_tuple[0:3]
>>> tuple_slice 
('p', 'y', 't')
>>> # try to change first element 
... some_tuple[0] = 'c'

Якщо ми захочемо змінити елемент 'p' на 'c', інтерпретатор Python видасть нам таку помилку:

>>> # try to change first element
... some_tuple [0] = 'c'
Traceback (most recent call last):
  File "/usr/lib/python3.8/code.py", line 90, in runcode 
    exec (code, self.locals)
  File "<input>", line 2, in <module>
TypeError: 'tuple' object does not support item assignment

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

Словники в Python

Словник, він же dict – це колекція пар «ключ – значення». Як ключі тут можуть виступати будь-які незмінні об'єкти: числа, рядки і навіть кортежі, а значеннями можуть бути об'єкти будь-яких типів, включаючи інші словники.

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

Файли у Python

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

Як приклад уявимо, що ви хочете написати книгу, присвячену Python. У такому випадку, для початку потрібно створити файл з типом доступу «запис» - w (write), а потім записувати в нього рядки тексту, використовуючи метод write(). 

Виглядати це буде так:

>>> # Start from first chapter
... # Create book file in current folder
... my_book = open("my_book.txt", "w")
... my_book.write ("Chapter 1: Hello, Python\n")
... my_book.write("To be continued...\n")
... #Close the file 
... my_book.close()

Тепер, щоби переконатися, що все пройшло як треба, ми можемо створити новий об'єкт-файл, тільки в режимі читання — r (read). Для цього пишемо:

>>> Open our book and read content
... book = open("my_book.txt", "r")
... text = book.read()

Як бачимо, всі рядки, які ми записали раніше, були записані у файл.

Chapter 1: Hello, Python
To be continued ...

Де дізнатися більше про типи даних у Python

Описаних у цій статті знань буде цілком достатньо для вирішення найпростіших завдань. Щоб заглибитися в теорію та дізнатися про систему типів всю актуальну інформацію, рекомендуємо вивчити офіційну документацію Python, розділ «Built-in Types».