5  Бібліотеки

Data Miorsh Ihor Miroshnychenko Youtube Monobank

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

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

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

Примітка

Документація до стандартних бібліотек доступна за посиланням https://docs.python.org/3/library/.

5.1 Імпорт бібліотек

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

Для прикладу напишемо програму generate.py, яка буде симулювати підкидання монетки:

Terminal
code generate.py

5.1.1 Випадковість

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

import random

Тепер ми можемо використовувати функції з цього модуля. Наприклад, функція random.choice() повертає випадковий елемент зі списку. Давайте використаємо цю функцію, щоб визначити, яка сторона монетки випала:

import random

coin = ['орел', 'решка']
print(random.choice(coin))
орел

Запис random.choice(coin) означає, що ми використовуємо функцію choice() з модуля random і передаємо їй список coin як аргумент. Ця функція повертає випадковий елемент зі списку coin, який ми виводимо на екран.

Примітка

Документація до модуля random доступна за посиланням docs.python.org/3/library/random.html.

В попередньому прикладі ми імпортували всю бібліотеку random. Але що, якщо нам потрібно використати лише одну функцію з цієї бібліотеки? Тоді ми можемо імпортувати лише цю функцію за допомогою комбінації ключових слів from та import:

from random import choice

coin = ['орел', 'решка']
print(choice(coin))
решка

Тепер ми можемо використовувати функцію choice() без префіксу random.

Продовжимо досліджувати модуль random. Цього разу це буде функція random.randint(a, b), яка повертає випадкове ціле число від a до b включно. Давайте використаємо цю функцію, щоб симулювати підкидання гральної кістки з 20 гранню1:

import random

d20 = random.randint(1, 20)
print(d20)
14

Підемо далі, та розглянемо наступну функцію random.shuffle(x), яка перемішує елементи списку x у випадковому порядку. Давайте використаємо цю функцію, щоб перемішати колоду карт:

import random

cards = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']

random.shuffle(cards)

for card in cards:
    print(card, end=' ')
8 K 2 6 7 A J 3 Q 9 4 5 10 
Увага

Зверніть увагу, що функція random.shuffle(x) не повертає перемішаний список x, а просто перемішує його на місці.

Уважно читайте документацію!

5.1.2 Статистика

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

Створимо програму average.py, яка буде розраховувати середнє арифметичне значення списку чисел:

Terminal
code average.py

Імпортуємо модуль statistics та розрахуємо середнє значення поточної успішності студента за допомогою функції mean():

import statistics

grades = [
    12, 10, 7, 12, 9, 10, 12,
    8, 11, 12, 10, 9, 8, 11, 12,
    10, 9, 8, 11, 12, 10, 9, 8, 11
]

print(statistics.mean(grades))
10.041666666666666
Примітка

Документація до модуля statistics доступна за посиланням docs.python.org/3/library/statistics.html.

5.2 Аргументи командного рядка

Існує ще більше функціональних можливостей, які постачаються з Python. Серед них є функції для роботи з аргументами командного рядка (англ. command-line arguments).

Раніше, для запуску коду ми виконували команду python hello.py і жодного разу не писали нічого після імені файлу. Але Python дозволяє передавати аргументи командного рядка, які можуть використовуватися у програмі. Насправді, коли ви запускаєте програми в командному рядку, ви можете вказати будь-яку кількість слів, чисел або фраз після команди, яку ви вводите, і все це буде якимось чином передано як вхідні дані для самої програми. Що це означає? Давайте розглянемо приклад.

Створимо файл name.py, в якому будемо використовувати новий модуль sys:

Terminal
code name.py

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

Примітка

Документація до модуля sys доступна за посиланням docs.python.org/3/library/sys.html.

Ми зосередимо увагу на функції sys.argv, яка повертає список аргументів командного рядка, переданих програмі. Давайте використаємо цю функцію, щоб вивести на екран другий аргумент командного рядка:

import sys

print('Привіт, мене звати ' + sys.argv[1] + '!')

Але цього разу замість команди python name.py ми виконаємо команду python name.py Ігор, яка передасть ім’я Ігор у якості аргумента командного рядка.

Terminal
python name.py Ігор

В результаті ми отримаємо наступне повідомлення:

Привіт, мене звати Ігор!

Якщо ж виконати цей код без аргументів командного рядка, то ми отримаємо помилку:

Traceback (most recent call last):
  File "name.py", line 4, in <module>
    print('Привіт, мене звати ' + sys.argv[1] + '!')

IndexError: list index out of range

Це означає, що ми намагаємося отримати доступ до елемента списку, якого не існує. Це тому, що список sys.argv не містить жодного елемента, якщо не передати аргументів командного рядка.

Давайте виправимо цю помилку, додавши перевірку наявності аргументів командного рядка:

import sys

try:
    print('Привіт, мене звати ' + sys.argv[1] + '!')
except IndexError:
    print('Введіть своє ім\'я як аргумент командного рядка!')

Виконаємо код з аргументами командного рядка:

Terminal
python name.py Ігор
Привіт, мене звати Ігор!

Виконаємо код без аргументів командного рядка:

Terminal
python name.py
Введіть своє ім'я як аргумент командного рядка!

З іншої сторони, ми можемо перевірити кількість аргументів командного рядка за допомогою функції len():

import sys

if len(sys.argv) < 2:
    print('Мало аргументів командного рядка!')
elif len(sys.argv) > 2:
    print('Багато аргументів командного рядка!')
else:
    print('Привіт, мене звати ' + sys.argv[1] + '!')
Багато аргументів командного рядка!

Виконаємо код з аргументами командного рядка:

Terminal
python name.py Ігор
Привіт, мене звати Ігор!

Виконаємо код без аргументів командного рядка:

Terminal
python name.py
Мало аргументів командного рядка!

Виконаємо код з багатьма аргументами командного рядка:

Terminal
python name.py Ігор Мірошниченко
Багато аргументів командного рядка!

Одна з речей, яка мені не подобається у цій версії коду полягає у тому, що суть моєї програми винесено у цей блок else. Є щось приємне в тому, щоб тримати всю обробку помилок окремо від коду, який вас дійсно цікавить. Існує кращий спосіб зробити це, використовуючи функцію exit():

import sys

# Перевіряємо кількість аргументів командного рядка
if len(sys.argv) < 2:
    sys.exit('Мало аргументів командного рядка!')
elif len(sys.argv) > 2:
    sys.exit('Багато аргументів командного рядка!')

# Виводимо повідомлення
print('Привіт, мене звати ' + sys.argv[1] + '!')

Функція exit() приймає один аргумент - повідомлення, яке буде виведено на екран, і зупиняє виконання програми. Тепер ми можемо використовувати функцію print() без блоку else.

Виконаємо код без аргументів командного рядка:

Terminal
python name.py
Мало аргументів командного рядка!

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

Terminal
python name.py Ігор Мірошниченко
Багато аргументів командного рядка!

Це тому, що Python розділяє аргументи командного рядка за допомогою пробілів. Щоб вирішити цю проблему, ми можемо помістити аргументи командного рядка у лапки:

Terminal
python name.py "Ігор Мірошниченко"
Привіт, мене звати Ігор Мірошниченко!

Припустимо, що я хочу, щоб друкувалося не тільки моє ім’я, але й імена інших користувачів. Для цього ми можемо використати зрізи (англ. slices) списків. Для цього використовуються квадратні дужки [], які вказують, які елементи списку потрібно вивести. Перепишемо програму name.py:

import sys

if len(sys.argv) < 2:
    sys.exit('Мало аргументів командного рядка!')

for name in sys.argv[1:]:
    print('Привіт, мене звати ' + name + '!')

Виконаємо код з аргументами командного рядка:

Terminal
python name.py Ігор Анна Яромир Артур Святослав
Привіт, мене звати Ігор!
Привіт, мене звати Анна!
Привіт, мене звати Яромир!
Привіт, мене звати Артур!
Привіт, мене звати Святослав!

5.3 Пакети

Однією з причин того, що Python є настільки популярним і потужним в наші дні, є те, що існує багато бібліотек сторонніх розробників, також відомих як пакети (англ. packages). Строго кажучи, у самій мові Python є термін “пакети”, який по суті є модулем, реалізованим у папці, не просто у файлі. Але в більш загальному сенсі, пакети - це бібліотека сторонніх розробників, яку ми з вами можемо встановити на наш власний комп’ютер Mac або PC, або на наш хмарний сервер і отримати доступ до ще більшої функціональності, яку інші люди реалізували для нас.

Одне з місць, де ви можете отримати всі ці пакунки, називається PyPI (The Python Package Index). Це репозиторій, де розробники можуть розміщувати свої пакети, щоб інші люди могли їх встановити. Існує багато інших репозиторіїв, але PyPI є найбільш популярним.

Для встановлення пакетів, Python використовує менеджер пакетів pip - це програма, яка поставляється з Python і дозволяє встановлювати пакети.

5.3.1 Cowsay

Цікавий факт: існує пакет cowsay, який дозволяє виводити повідомлення на екран у вигляді корови2. Давайте встановимо пакет cowsay за допомогою команди pip install cowsay:

Terminal
pip install cowsay
Примітка

Документація до пакету cowsay доступна за посиланням pypi.org/project/cowsay/.

Тепер давайте створимо файл cow.py, який буде виводити повідомлення на екран у вигляді корови:

Terminal
code cow.py

Імпортуємо модуль cowsay та використаємо функцію cowsay.cow() для виведення повідомлення на екран у вигляді корови:

import cowsay

cowsay.cow('Привіт, мене звати Бакбик!')
  __________________________
| Привіт, мене звати Бакбик! |
  ==========================
                          \
                           \
                             ^__^
                             (oo)\_______
                             (__)\       )\/\
                                 ||----w |
                                 ||     ||

5.3.2 API

API (англ. Application Programming Interface) - це спосіб, за допомогою якого програми можуть взаємодіяти одна з одною. Існує багато різних API, які ви можете використовувати, наприклад, API для роботи з мережею, API для роботи з базами даних, API для роботи з файлами, API для роботи з графікою і багато іншого.

Пакет, з яким ми будемо знайомитися, називається requests. Цей пакет дозволяє нам виконувати HTTP-запити до веб-сайтів. Для його встановлення виконаємо команду:

Terminal
pip install requests
Примітка

Документація до пакету requests доступна за посиланням pypi.org/project/requests/.

Створимо новий файл itunes.py, який буде використовувати API iTunes. Apple має власний API для свого сервісу iTunes, яке надає вам можливість завантажувати і шукати музику і пісні, а також іншу інформацію.

Terminal
code itunes.py

Якщо переглянути документацію до API iTunes, то ми побачимо, що для пошуку пісень потрібно використовувати адресу entity=song. Для перегляну інформації про одну пісню необхідно до базової адреси додати limit=1. Для пошуку пісень гурту Korn необхідно до базової адреси додати term=korn. Таким чином, ми отримаємо наступну адресу:

https://itunes.apple.com/search?entity=song&limit=1&term=korn

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

{
 "resultCount":1,
 "results": [
{"wrapperType":"track", "kind":"song", "artistId":466532, "collectionId":423045626, "trackId":423045744, "artistName":"Korn", "collectionName":"Korn", "trackName":"Blind", "collectionCensoredName":"Korn", "trackCensoredName":"Blind", "artistViewUrl":"https://music.apple.com/us/artist/korn/466532?uo=4", "collectionViewUrl":"https://music.apple.com/us/album/blind/423045626?i=423045744&uo=4", "trackViewUrl":"https://music.apple.com/us/album/blind/423045626?i=423045744&uo=4", 
"previewUrl":"https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview115/v4/ef/a4/dd/efa4dd64-e8e0-6c12-c88b-a3a996b24b12/mzaf_13798363446300996858.plus.aac.p.m4a", "artworkUrl30":"https://is2-ssl.mzstatic.com/image/thumb/Music114/v4/09/2b/e7/092be7d0-7697-220d-c000-97f366e723e4/mzi.anacpwuj.jpg/30x30bb.jpg", "artworkUrl60":"https://is2-ssl.mzstatic.com/image/thumb/Music114/v4/09/2b/e7/092be7d0-7697-220d-c000-97f366e723e4/mzi.anacpwuj.jpg/60x60bb.jpg", "artworkUrl100":"https://is2-ssl.mzstatic.com/image/thumb/Music114/v4/09/2b/e7/092be7d0-7697-220d-c000-97f366e723e4/mzi.anacpwuj.jpg/100x100bb.jpg", "collectionPrice":9.99, "trackPrice":1.29, "releaseDate":"1994-08-01T07:00:00Z", "collectionExplicitness":"explicit", "trackExplicitness":"notExplicit", "discCount":1, "discNumber":1, "trackCount":12, "trackNumber":1, "trackTimeMillis":258267, "country":"USA", "currency":"USD", "primaryGenreName":"Metal", "isStreamable":true}]
}

Наповнення виглядає дещо незрозуміло, воно має свою структуру: зверніть увагу на фігурні дужки, на квадратні дужки, на коми, на двокрапки, на лапки. Це називається форматом JSON (англ. JavaScript Object Notation). Це формат, який використовується для передачі даних між програмами. Існує багато інших форматів, таких як XML, CSV, YAML, але JSON є одним з найпопулярніших форматів.

Цей текст містить інформацію з бази даних Apple про одну пісню Korn. Давайте використаємо пакет requests, щоб отримати цю інформацію і вивести її на екран:

import requests
import sys

if len(sys.argv) != 2:
    sys.exit('Введіть назву гурту як аргумент командного рядка!')

url = 'https://itunes.apple.com/search?entity=song&limit=1&term=' + sys.argv[1]
response = requests.get(url)
print(response.json())
{'resultCount': 1, 'results': [{'wrapperType': 'track', 'kind': 'song', 'artistId': 466532, 'collectionId': 423045626, 'trackId': 423045744, 'artistName': 'Korn', 'collectionName': 'Korn', 'trackName': 'Blind', 'collectionCensoredName': 'Korn', 'trackCensoredName': 'Blind', 'artistViewUrl': 'https://music.apple.com/us/artist/korn/466532?uo=4', 'collectionViewUrl': 'https://music.apple.com/us/album/blind/423045626?i=423045744&uo=4', 'trackViewUrl': 'https://music.apple.com/us/album/blind/423045626?i=423045744&uo=4', 'previewUrl': 'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview115/v4/ef/a4/dd/efa4dd64-e8e0-6c12-c88b-a3a996b24b12/mzaf_13798363446300996858.plus.aac.p.m4a', 'artworkUrl30': 'https://is1-ssl.mzstatic.com/image/thumb/Music114/v4/09/2b/e7/092be7d0-7697-220d-c000-97f366e723e4/mzi.anacpwuj.jpg/30x30bb.jpg', 'artworkUrl60': 'https://is1-ssl.mzstatic.com/image/thumb/Music114/v4/09/2b/e7/092be7d0-7697-220d-c000-97f366e723e4/mzi.anacpwuj.jpg/60x60bb.jpg', 'artworkUrl100': 'https://is1-ssl.mzstatic.com/image/thumb/Music114/v4/09/2b/e7/092be7d0-7697-220d-c000-97f366e723e4/mzi.anacpwuj.jpg/100x100bb.jpg', 'collectionPrice': 9.99, 'trackPrice': 1.29, 'releaseDate': '1994-08-01T07:00:00Z', 'collectionExplicitness': 'explicit', 'trackExplicitness': 'notExplicit', 'discCount': 1, 'discNumber': 1, 'trackCount': 12, 'trackNumber': 1, 'trackTimeMillis': 258267, 'country': 'USA', 'currency': 'USD', 'primaryGenreName': 'Metal', 'isStreamable': True}]}

Ми отримали той самий відформатований текст, але він був стандартизований у вигляді словника Python: Apple API віддає нам JSON, а пакет requests перетворює його у словник Python.

Давайте відформатуємо вивід, щоб він був більш читабельним. Для цього використаємо бібліотеку json, яка вбудована в Python:

import json
import requests
import sys

if len(sys.argv) != 2:
    sys.exit('Введіть назву гурту як аргумент командного рядка!')

url = 'https://itunes.apple.com/search?entity=song&limit=1&term=' + sys.argv[1]
response = requests.get(url)
print(json.dumps(response.json(), indent=4))
{
  "resultCount": 1,
  "results": [
    {
      "wrapperType": "track",
      "kind": "song",
      "artistId": 466532,
      "collectionId": 423045626,
      "trackId": 423045744,
      "artistName": "Korn",
      "collectionName": "Korn",
      "trackName": "Blind",
      "collectionCensoredName": "Korn",
      "trackCensoredName": "Blind",
      "artistViewUrl": "https://music.apple.com/us/artist/korn/466532?uo=4",
      "collectionViewUrl": "https://music.apple.com/us/album/blind/423045626?i=423045744&uo=4",
      "trackViewUrl": "https://music.apple.com/us/album/blind/423045626?i=423045744&uo=4",
      "previewUrl": "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview115/v4/ef/a4/dd/efa4dd64-e8e0-6c12-c88b-a3a996b24b12/mzaf_13798363446300996858.plus.aac.p.m4a",
      "artworkUrl30": "https://is1-ssl.mzstatic.com/image/thumb/Music114/v4/09/2b/e7/092be7d0-7697-220d-c000-97f366e723e4/mzi.anacpwuj.jpg/30x30bb.jpg",
      "artworkUrl60": "https://is1-ssl.mzstatic.com/image/thumb/Music114/v4/09/2b/e7/092be7d0-7697-220d-c000-97f366e723e4/mzi.anacpwuj.jpg/60x60bb.jpg",
      "artworkUrl100": "https://is1-ssl.mzstatic.com/image/thumb/Music114/v4/09/2b/e7/092be7d0-7697-220d-c000-97f366e723e4/mzi.anacpwuj.jpg/100x100bb.jpg",
      "collectionPrice": 9.99,
      "trackPrice": 1.29,
      "releaseDate": "1994-08-01T07:00:00Z",
      "collectionExplicitness": "explicit",
      "trackExplicitness": "notExplicit",
      "discCount": 1,
      "discNumber": 1,
      "trackCount": 12,
      "trackNumber": 1,
      "trackTimeMillis": 258267,
      "country": "USA",
      "currency": "USD",
      "primaryGenreName": "Metal",
      "isStreamable": true
    }
  ]
}

Тепер ми отримали відформатований текст, який більш читабельний. Але що, якщо ми хочемо вивести лише назву пісні? Для цього нам потрібно звернутися до словника Python, який міститься у змінній response.json(). Цей словник містить ключ results, який містить список пісень. Цей список містить словник, який містить ключ trackName, який містить назву пісні. Пропоную модифікувати URL-адресу і вивести на 1, а 10 треків (limit=10) гурту Korn:

import json
import requests
import sys

if len(sys.argv) != 2:
    sys.exit('Введіть назву гурту як аргумент командного рядка!')

url = 'https://itunes.apple.com/search?entity=song&limit=10&term=' + sys.argv[1]
response = requests.get(url)
data = response.json()

for result in data['results']:
    print(result['trackName'])
Blind
Shoots and Ladders
Lies
Faget
Fake
Divine
Need To
Ball Tongue
Helmet In the Bush
Daddy
Predictable
Примітка

Документація до пакету json доступна за посиланням docs.python.org/3/library/json.html.

5.3.3 Власні пакети

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

Створимо новий файл saying.py, який буде моїм власним модулем:

Terminal
code saying.py

В цьому файлі я створю функцію say_hello(), яка буде виводити вітальне повідомлення на екран, функцію say_goodbye(), яка буде виводити прощальне повідомлення на екран. Щоб переконатися, що ці функції працюють належним чином, я поміщу їх у функцію main():

def main():
    say_hello('Гаррі')
    say_goodbye('Гаррі')

def say_hello(name):
    print(f'Привіт, {name}')

def say_goodbye(name):
    print(f'До побачення, {name}')

main()
Привіт, Гаррі
До побачення, Гаррі

Тепер я хочу використовувати ці функції так, ніби я дійсно створив власну бібліотеку, який робить доступною весь його функціонал для будь кого. Для цього створимо новий файл say.py:

Terminal
code say.py

В цьому файлі я імпортую функції з модуля saying і використовую їх:

import sys
from saying import say_hello

if len(sys.argv) == 2:
    say_hello(sys.argv[1])

Виконаємо код з аргументом командного рядка:

Terminal
python say.py Гаррі

І отримаю наступний результат:

Привіт, Гаррі
До побачення, Гаррі
Привіт, Гаррі

Чому так відбувається? Xоч я все зробив згідно з нашою попередньою практикою, це не зовсім правильний спосіб виклику main(). Нюанс в тому, що у файлі saying.py я викликаю функцію main() у самому кінці. І навіть коли я імпортую цей модуль у новий файл say.py, то функція main() все одно буде викликана.

Код from saying import say_hello просить Python знайти модуль saying, прочитати його, а потім імпортувати функцію say_hello(). На жаль, до того часу, як Python прочитає файл зверху вниз зліва направо, останній рядок коду викликає функцію main(), що призводить до обов’язкового виклику всього коду.

Для виправлення ситуації необхідно замість виклику функції main() у кінці коду використовувати запис:

Лістинг 5.1: Умова виконання функції main()
if __name__ == '__main__':
    main()

Запис __name__ - це спеціальна змінна Python, яка містить ім’я поточного модуля. Якщо модуль виконується як програма, то ця змінна містить рядок '__main__'. Якщо ж модуль імпортується, то ця змінна містить ім’я модуля. Таким чином, ми можемо перевірити, чи виконується модуль як програма, і якщо так, то викликати функцію main().

Давайте подивимось. Спочатку виправимо нам модуль saying.py:

def main():
    say_hello('Гаррі')
    say_goodbye('Гаррі')

def say_hello(name):
    print(f'Привіт, {name}')

def say_goodbye(name):
    print(f'До побачення, {name}')

if __name__ == '__main__':
    main()

Імпортуємо модуль saying:

import sys
from saying import say_hello

if len(sys.argv) == 2:
    say_hello(sys.argv[1])

Виконаємо код з аргументом командного рядка:

Terminal
python say.py Гаррі
Привіт, Гаррі

  1. 20-гранна кістка використовується у деяких настільних іграх, наприклад, у Dungeons & Dragons.↩︎

  2. Існує окрема форма образотворчого мистецтва під назвою ASCII-графіка, яка використовує символи ASCII на екрані комп’ютерного терміналу або принтера для представлення зображень.↩︎

Data Miorsh Ihor Miroshnychenko Youtube Monobank