Python Materials For Beginners
446 subscribers
39 photos
1 video
9 files
150 links
Download Telegram
🟣 Потоки вводу/виводу (Input/Output Streams, stdin/stdout/stderr)

Повернемось до теми Корисні речі, які варто знати про процес

У попередньому прикладі коду я використав такий вивід:
print("Usage: python script.py <num1> <num2>", file=sys.stderr)

Що це значить?
Подивимось у документацію:
print(*objects, sep=' ', end='\n', file=None, flush=False)

The file argument must be an object with a write(string) method; if it is not present or None,
sys.stdout will be used. Since printed arguments are converted to text strings,
print() cannot be used with binary mode file objects. For these, use file.write(...) instead.

🔗 Документація

Простими слова file це те куди виводити рядок нашого повідомлення. По замовченню вивід відбувається у sys.stdout. Також вивід може відбуватись у реальний файл або у поток sys.stdout - спеціальний потік для помилок.

⚠️ Варто зауважити: що потік в/в в анг. варіанті це stream i/o і не варто його плутати з thread - потоком виконання.

sys.stdout як sys.stderr це два потоки для виводу (stream output) обидва виводять текст у консоль користувач не бачить різницю між ними візуально.
При програмній автоматизації процесів можливо перехоплювати текст з обох потоків, можемо окремо мати звичайний тест інформаційного характеру з sys.stdout і окремо з sys.stderr

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

👉 Практична цінність знання про потоки в/в полягає саме у можливості автоматизувати взаємодію між процесами (IPC) шляхом їх перехоплення.
👍1
🟢 Потоки введення/виведення. Практика.

У Python для створення підпроцесів використовується бібліотека subprocess.
Вона дозволяє запускати програми та працювати з їхніми потоками введення/виведення.
subprocess добре підходить для простих випадків з синхронном кодом.

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

Ідея проста: запускаємо Python у інтерактивному режимі та виконуємо дві операції — 2+2 і 1/0.

import asyncio
import sys


async def read_stream(name: str, stream: asyncio.StreamReader):
while data := await stream.readline():
line = data.decode("utf8").rstrip()
print(f"[{name}]", line)


async def main():
process = await asyncio.create_subprocess_exec(
sys.executable, "-i", # python -i
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)

stdout_task = asyncio.create_task(
read_stream("stdout", process.stdout),
)
stderr_task = asyncio.create_task(
read_stream("stderr", process.stderr),
)

process.stdin.write(b"2+2\n")
await process.stdin.drain()

process.stdin.write(b"1/0\n")
await process.stdin.drain()

process.stdin.write(b"exit()\n")
await process.stdin.drain()

await process.wait() # Чекаємо завершення підпроцесу

Вивід у консоль:
[stderr] Python 3.11.6 (tags/v3.11.6:8b6ee5b, Oct 2 2023, 14:57:12) [MSC v.1935 64 bit (AMD64)] on win32
[stderr] Type "help", "copyright", "credits" or "license" for more information.
[stdout] 4
[stderr] >>> >>> Traceback (most recent call last):
[stderr] File "<stdin>", line 1, in <module>
[stderr] ZeroDivisionError: division by zero
[stderr] >>>


Як бачимо ми отримали текст окремо stdout і окремо stderr.

Звісно аналогічним чином ми можемо запускати системі утіліти або інші програми написані на інших мовах програмування — це простір для автоматизації.

Але трохи дивно чому python пише текст привітання саме у stderr
Трохи про Rotating Proxy.
(Гадаю ця інформація буде корисна при покупці проксі для скрапінгу даних.)

По режиму керування:

1. Per-request rotation
2. Session-based rotation
3. Automated request management

За типом інфраструктури/провайдера:

1. Residential High reliability Real home IPs
2. Datacenter Fast & cost-efficient Server IPs
3. Mobile Carrier IPs Mobile-network routing
4. ISP Datacenter speed Consumer-ISP routing

#proxy

https://www.youtube.com/watch?v=YTQVusxV-Bs
Please open Telegram to view this post
VIEW IN TELEGRAM
👏1
Please open Telegram to view this post
VIEW IN TELEGRAM
👏1
Python Materials For Beginners
Підбірка відео по конкурентності: Асинхронний Пайтон за 5 хвилин / Python - AsyncIO Введення у Асинхронне Програмування - Процеси, Потоки, GIL та Asyncio Основи роботи з Процесами та Потоками у Пайтоні #asyncio
На жаль в багатьох відео пояснення асинхронки у python якесь не дуже (у тому числі у відео що я привів вище). Чомусь багато авторів лізуть під капот діставати генератори, які не дуже й то пояснюють основну суть.
На мою думку варто більше акцентувати свою увагу на розумінні IO-bound та CPU-bound задачах.
Також важливі моменти asyncio.gather та asyncio.wait, asyncio.wait_for які як раз дають змогу
отримувати виграш у часі виконання програми за рахунок IO-bound.

#asyncio
I/O-bound задача (Input/Output bound — задача, обмежена вводом-виводом) — це тип задач у програмуванні, під час виконання яких процесор більшість часу простоює в очікуванні, поки завершиться зовнішня операція.

У таких задачах швидкість виконання програми залежить не від потужності процесора (CPU), а від швидкості зовнішніх систем чи пристроїв.

До I/O-bound можна віднести:
1. Роботу з мережею (у тому числі HTTP-запити та роботу з БД).
2. Роботу з пристроями (наприклад, роботу через serial port).
3. Роботу з файлами.

Операційна система надає спеціальний набір функцій для асинхронної роботи, які зазвичай називають неблокуючими (non-blocking). Python будує над ними свою обгортку яка підтримує синтаксис async/await

#asyncio
Бібліотеки httpx і curl_cffi (на відміну від requests) надають як синхроний так і асинхронний інтерфейси:
import httpx

def main():
with httpx.Client() as client:
response = client.get("https://httpbin.org/get")
print(response.status_code)

main()


import asyncio
import httpx

async def main():
async with httpx.AsyncClient() as client:
response = await client.get("https://httpbin.org/get")
print(response.status_code)

asyncio.run(main())


За виключенням синтаксису async/await і запуску через asyncio.run різниці немає.
І що варто підкреслити не має виграшу у часі виконання.
Обидва приклади відправляють HTTP запит на сервер, а потім чекають від нього відповіді.

#asyncio
Виграшу у часі виконання.

За рахунок неблокуючих операцій ми можемо відправити запит на сервер, але не чекати відповіді - робити іншу задачу.
async with httpx.AsyncClient() as client:    
task = asyncio.create_task(
client.get("https://example.com/")
)

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

# тут пізніше отримуємо відповідь
response = await task
print(response.status_code)

#asyncio #create_task
Виграш у часі виконання за рахунок одночасного очікування відповідей. asyncio.gather

import time
import asyncio
import httpx

# Ключ від безкоштовного апі https://app.reqres.in
API_KEY = "free_user_****************"

async def main():
async with httpx.AsyncClient() as client:
client.headers["x-api-key"] = API_KEY

start = time.monotonic()

responses = await asyncio.gather(
client.get("https://reqres.in/api/users?delay=1"), # запит 1с
client.get("https://reqres.in/api/users?delay=3"), # запит 3с
client.get("https://reqres.in/api/users?delay=2"), # запит 2с
)

print(f"Elapsed: {time.monotonic() - start:.2f} s")
print("Status codes: ", [r.status_code for r in responses])


if __name__ == "__main__":
asyncio.run(main())

Вивід:
Elapsed: 3.30 s
Status codes: [200, 200, 200]

API app.reqres.in дозволяє задати час відповіді для кожного запиту (1, 3 та 2 с). Це дає можливість побачити перевагу асинхронності, коли запити виконуються групою й відповіді очікуються одночасно.
Якби ми робили запити послідовно, час виконання становив би 1 + 3 + 2 = ~6 с. Проте завдяки асинхронності ми досягли результату ~3,3 с, що співмірне з часом найдовшого запиту.

#asyncio #gather
2