Библиотека Python разработчика | Книги по питону
19.1K subscribers
1.06K photos
404 videos
82 files
1.03K links
Полезные материалы для питониста по Фреймворкам Django, Flask, FastAPI, Pyramid, Tornado и др.

По всем вопросам @evgenycarter

РКН clck.ru/3Ko7Hq
Download Telegram
Модуль multiprocessing в Python: потоки против процессов

Модуль multiprocessing позволяет создавать не только процессы, но и потоки. Однако стоит помнить о главной особенности CPython — GIL (Global Interpreter Lock). Этот механизм блокирует выполнение байт-кода Python несколькими потоками одновременно.

Это означает, что потоки полезны в основном в случаях, когда программа выполняет операции, не связанные с Python-интерпретатором, например, ожидание ввода-вывода (IO). К примеру, загрузка трёх различных статей из Википедии будет одинаково эффективной как с потоками, так и с процессами. Причём результат в три раза быстрее по сравнению с выполнением задачи в одном процессе:


from multiprocessing import Pool
from multiprocessing.pool import ThreadPool
import requests

def download_wiki_article(article):
url = 'https://de.wikipedia.org/wiki/'
return requests.get(url + article)

process_pool = Pool(3)
thread_pool = ThreadPool(3)

thread_pool.map(download_wiki_article, ['a', 'b', 'c'])
# ~376 ms

process_pool.map(download_wiki_article, ['a', 'b', 'c'])
# ~373 ms

[download_wiki_article(a) for a in ['a', 'b', 'c']]
# ~1.09 s


Однако использование потоков для задач, нагружающих CPU, практически бессмысленно:


import math
from multiprocessing import Pool
from multiprocessing.pool import ThreadPool

def f(x):
return len(str(math.factorial(x)))

process_pool = Pool(4)
thread_pool = ThreadPool(4)
inputs = [i ** 2 for i in range(100, 130)]

[f(x) for x in inputs]
# ~1.48 s

thread_pool.map(f, inputs)
# ~1.48 s

process_pool.map(f, inputs)
# ~478 ms


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

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Если вам нужно выполнить поиск в отсортированной коллекции, то бинарный поиск — это именно то, что вам нужно. Этот простой алгоритм сравнивает искомое значение с элементом в середине массива; результат определяет, какую половину нужно искать дальше.

Стандартная библиотека Python предоставляет возможность использовать бинарный поиск без его непосредственной реализации. Функция bisect_left возвращает самую левую позицию элемента в отсортированном списке, а bisect_right — самую правую.


from random import randrange
from bisect import bisect_left

n = 1000000
look_for = 555555
lst = sorted(randrange(0, n) for _ in range(n))

%timeit look_for in lst
# 69.7 ms ± 449 µs на цикл

%timeit look_for == lst[bisect_left(lst, look_for)]
# 927 ns ± 2.28 ns на цикл


Результаты демонстрируют, что использование бинарного поиска через bisect_left быстрее, чем стандартный поиск в списке с помощью оператора in.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
📌 О представлении данных в байтах в Python

Когда мы храним данные в памяти или на устройстве хранения, их необходимо представить в виде байтов. Python позволяет работать с абстракцией данных, не задумываясь об их байтовом представлении. Однако при записи строки в файл мы фактически работаем с физической структурой данных.

Чтобы записать символы в файл, их нужно преобразовать в байты — это называется кодированием (encoding). Когда вы читаете байты из файла и хотите преобразовать их в понятные символы, этот процесс называется декодированием (decoding).

🔤 Кодировки и их применение

Существует множество методов кодирования. Один из самых популярных — Unicode, но сам по себе Unicode не является кодировкой в традиционном смысле. Unicode определяет соответствие между символами и их числовыми кодами. Например, 🐍 имеет код 128 013.

Однако, чтобы записать числа в файл, нужна настоящая кодировка. Unicode обычно используется с utf-8, которая (в большинстве случаев) является кодировкой по умолчанию в Python. При чтении из файла Python автоматически декодирует данные, используя utf-8.

Если вы хотите использовать другую кодировку, просто укажите её с помощью параметра encoding= в функции open. А чтобы работать с "чистыми" байтами, добавьте символ b к режиму открытия файла.

Пример:

# Кодирование строки в файл
with open('example.txt', 'w', encoding='utf-8') as f:
f.write('Привет, мир!')

# Чтение в байтовом режиме
with open('example.txt', 'rb') as f:
data = f.read()
print(data) # Вывод: b'\xd0\x9f\xd1\x80\xd0\xb8...'


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
🔑 Использование объектов в качестве ключей словаря в Python

В Python вы можете использовать любой объект в качестве ключа словаря, если он реализует метод __hash__. Этот метод возвращает целое число, но при этом важно соблюдать одно ключевое требование: равные объекты должны иметь одинаковый хэш (обратное утверждение необязательно).

👉 Не используйте изменяемые объекты в качестве ключей! Если объект изменяется после добавления в словарь, он становится "невидимым" для поиска, так как его хэш может измениться.

🌀 Странность с отрицательными хэшами

Есть интересная особенность, которая может вас удивить при отладке или написании юнит-тестов. Рассмотрим следующий пример:


class A:
def __init__(self, x):
self.x = x

def __hash__(self):
return self.x


Результаты хэширования экземпляров класса:


>>> hash(A(2))
2
>>> hash(A(1))
1
>>> hash(A(0))
0
>>> hash(A(-1)) # внимание!
-2
>>> hash(A(-2))
-2


💡 В CPython значение -1 зарезервировано для внутренних ошибок. Если хэш-значение равно -1, интерпретатор автоматически преобразует его в -2. Это может вызывать неожиданные проблемы при сравнении или использовании объектов в качестве ключей.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🔥1
Иногда нужно создать функцию на основе более универсальной. Например, у функции int() есть параметр base, который можно зафиксировать, чтобы получить новую функцию base2:


>>> int("10")
10
>>> int("10", 2)
2
>>> def base2(x):
... return int(x, 2)
...
>>> base2("10")
2


Для более точной и семантически понятной реализации можно использовать functools.partial:


from functools import partial

base2 = partial(int, base=2)


Это удобно, когда нужно передать функцию в качестве аргумента в другую функцию высшего порядка, но с заблокированными значениями некоторых аргументов:


>>> list(map(partial(int, base=2), ["1", "10", "100"]))
[1, 2, 4]


Без использования partial пришлось бы писать код так:


>>> list(map(lambda x: int(x, base=2), ["1", "10", "100"]))
[1, 2, 4]


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Метод format в Python для строк — мощный инструмент, поддерживающий множество возможностей, о которых вы, возможно, даже не знали. Каждый заменяемый плейсхолдер ({...}) может содержать три части: имя поля, преобразование и спецификацию формата.

Имя поля используется для указания, какой именно аргумент должен быть подставлен:


>>> '{}'.format(42)
'42'
>>> '{1}'.format(1, 2)
'2'
>>> '{y}'.format(x=1, y=2)
'2'


Преобразование позволяет указать, что вместо str() следует использовать repr() (или ascii()) при преобразовании объектов в строки:


>>> '{!r}'.format(datetime.now())
'datetime.datetime(2018, 5, 3, 23, 48, 49, 157037)'
>>> '{}'.format(datetime.now())
'2018-05-03 23:49:01.060852'


Спецификация формата задаёт, как значения будут представлены:


>>> '{:+,}'.format(1234567)
'+1,234,567'
>>> '{:>19}'.format(1234567)
' 1234567'


Эта спецификация может быть применена и к отдельному объекту с помощью функции format (не метода str):


>>> format(5000000, '+,')
'+5,000,000'


Функция format вызывает метод __format__ объекта, поэтому вы можете изменить его поведение для своих типов.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1