Библиотека Python разработчика | Книги по питону
18.3K subscribers
1.05K photos
403 videos
82 files
1.15K links
Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍

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

РКН clck.ru/3Ko7Hq
Download Telegram
Если вы хотите итерироваться одновременно по нескольким итерируемым объектам, функция zip может быть хорошим выбором. Она возвращает генератор, который выдаёт кортежи, содержащие по одному элементу из каждого исходного итерируемого объекта:


In : eng = ['one', 'two', 'three']
In : ger = ['eins', 'zwei', 'drei']
In : for e, g in zip(eng, ger):
...: print('{e} = {g}'.format(e=e, g=g))
...:


Вывод:


one = eins
two = zwei
three = drei


Обратите внимание, что zip принимает итерируемые объекты как отдельные аргументы, а не в виде списка аргументов. Чтобы «развернуть» значения (unzip), можно использовать оператор *:


In : list(zip(*zip(eng, ger)))
Out: [('one', 'two', 'three'), ('eins', 'zwei', 'drei')]


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🐳2
💡10 функций, для продвинутых Python-разработчиков

1. Разворачиваем вложенных списков любой глубины

flatten = lambda lst: [x for sub in lst for x in (flatten(sub) if isinstance(sub, list) else [sub])]


2. Декоратор для мемоизации результатов функции

memoize = lambda f: (lambda *args, _cache={}, **kwargs: _cache.setdefault((args, tuple(kwargs.items())), f(*args, **kwargs)))


3. Разбиение списка на куски длины n

chunked = lambda lst, n: [lst[i:i+n] for i in range(0, len(lst), n)]


4. Уникализация последовательности с сохранением порядка

uniq = lambda seq: list(dict.fromkeys(seq))


5. Глубокий доступ к вложенным ключам словаря

deep_get = lambda d, *keys: __import__('functools').reduce(lambda a, k: a.get(k) if isinstance(a, dict) else None, keys, d)


6. Преобразование Python-объекта в читаемый JSON

pretty_json = lambda obj: __import__('json').dumps(obj, ensure_ascii=False, indent=2)


7. Чтение последних n строк файла (аналог tail)

tail = lambda f, n=10: list(__import__('collections').deque(open(f), maxlen=n))


8. Выполнение shell-команды и возврат вывода

sh = lambda cmd: __import__('subprocess').run(cmd, shell=True, check=True, capture_output=True).stdout.decode().strip()


9. Быстрое объединение путей

path_join = lambda *p: __import__('os').path.join(*p)


10. Группировка списка словарей по значению ключа

group_by = lambda seq, key: {k: [d for d in seq if d.get(key) == k] for k in set(d.get(key) for d in seq)}


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👎32👍1🤯1
У Python очень короткий список встроенных констант. Одна из них — Ellipsis, которую также можно записать как .... Эта константа не имеет особого значения для интерпретатора, но используется в ситуациях, где такой синтаксис уместен.

В библиотеке NumPy Ellipsis поддерживается в качестве аргумента для __getitem__, например, x[...] возвращает все элементы массива x.

PEP 484 придаёт Ellipsis дополнительный смысл: Callable[..., type] используется для обозначения типа вызываемых объектов без указания типов аргументов.

Наконец, ... можно использовать, чтобы показать, что функция ещё не реализована. Это полностью допустимый Python-код:


def x():
...


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🚀 Подборка полезных IT каналов в Max


Системное администрирование, DevOps 📌

https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.
https://max.ru/tipsysdmin Типичный Сисадмин

1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С

Программирование C++📌
https://max.ru/cpp_lib Библиотека C/C++ разработчика

Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика

Программирование React📌
https://max.ru/react_lib React

Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика

Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика

GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub

Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных

Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков

Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов

Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻

Шутки программистов 📌
https://max.ru/itumor Шутки программистов

Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free

Книги, статьи для дизайнеров 📌
https://max.ru/odesigners Статьи, книги для дизайнеров

Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике

Вакансии 📌
https://max.ru/progjob Вакансии в IT

Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных


Бонус 📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
Иногда программное обеспечение начинает вести себя странно в продакшене. Вместо того чтобы просто перезапустить его, вы, вероятно, захотите понять, что именно происходит, чтобы позже это исправить.

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

В таком случае может пригодиться strace. Это утилита Unix, которая отслеживает системные вызовы. Вы можете запустить её заранее — strace python script.py — но чаще бывает удобнее подключиться к уже работающему процессу: strace -p PID.


$ cat test.py
with open('/tmp/test', 'w') as f:
f.write('test')
$ strace python test.py 2>&1 | grep open | tail -n 1
open("/tmp/test", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3


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

В следующем примере вывод приостанавливается до тех пор, пока кто-то не введёт данные в STDIN:


$ strace python -c 'input()'
...
read(0,


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Чтобы немедленно остановить выполнение программы на Python, следует использовать sys.exit(). Альтернативой является функция exit(), однако она предназначена для использования в интерактивном режиме. Благодаря строковому представлению, она может помочь пользователям, которые пытаются завершить сессию, используя exit (что поддерживается многими оболочками):


>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit
>>> str(exit)
'Use exit() or Ctrl-D (i.e. EOF) to exit'


И exit(), и sys.exit() на самом деле не завершают программу, а просто выбрасывают исключение SystemExit. SystemExit — это прямой подкласс BaseException, а значит, он не может быть перехвачен через except Exception, но может быть перехвачен через except BaseException или через голый except:.


>>> try:
... exit()
... except:
... 'Nothing'
...
'Nothing'


Поскольку это может быть проблемой, можно использовать функцию os._exit. Она не выбрасывает никаких исключений — просто завершает текущий процесс. Однако это означает, что блоки finally, а также завершающие действия менеджеров контекста не будут выполнены.


$ python3
Python 3.4.3 (default, Apr 28 2015, 13:37:07)
[GCC 4.8.3 20140911 (Red Hat 4.8.3-9)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> try:
... os._exit(42)
... finally:
... print('Bye!')
...
$ ...


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥2
Стандартный модуль json имеет интерфейс командной строки, который может быть полезен для форматирования JSON только с помощью Python. Модуль для этого называется json.tool и предназначен для вызова следующим образом:


$ echo '{"a": [], "b": "c"}' | python -m json.tool
{
"a": [],
"b": "c"
}


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Функция round округляет число до заданной точности в десятичных знаках.


>>> round(1.2)
1
>>> round(1.8)
2
>>> round(1.228, 1)
1.2


Также можно задать отрицательную точность:


>>> round(413.77, -1)
410.0
>>> round(413.77, -2)
400.0


round возвращает значение того же типа, что и входное число:


>>> type(round(2, 1))
<class 'int'>

>>> type(round(2.0, 1))
<class 'float'>

>>> type(round(Decimal(2), 1))
<class 'decimal.Decimal'>

>>> type(round(Fraction(2), 1))
<class 'fractions.Fraction'>


Для собственных классов можно определить поведение округления с помощью метода __round__:


>>> class Number(int):
... def __round__(self, p=-1000):
... return p
...
>>> round(Number(2))
-1000
>>> round(Number(2), -2)
-2


Значения округляются до ближайшего кратного 10 ** (-precision). Например, при precision=1, значение округляется до кратного 0.1: round(0.63, 1) возвращает 0.6.
Если два значения одинаково близки, округление происходит в сторону чётного числа:


>>> round(0.5)
0
>>> round(1.5)
2


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


>>> round(2.85, 1)
2.9


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


>>> format(2.85, '.64f')
'2.8500000000000000888178419700125232338905334472656250000000000000'


Если нужно округление "в большую сторону при 0.5" (round half up), можно использовать decimal.Decimal:


>>> from decimal import Decimal, ROUND_HALF_UP
>>> Decimal(1.5).quantize(0, ROUND_HALF_UP)
Decimal('2')
>>> Decimal(2.85).quantize(Decimal('1.0'), ROUND_HALF_UP)
Decimal('2.9')
>>> Decimal(2.84).quantize(Decimal('1.0'), ROUND_HALF_UP)
Decimal('2.8')


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Сортировка списка с элементами None может быть затруднительной:


In [1]: data = [
...: dict(a=1),
...: None,
...: dict(a=-3),
...: dict(a=2),
...: None,
...: ]


Попытка сортировки приведёт к ошибке:


In [2]: sorted(data, key=lambda x: x['a'])
---------------------------------------------------------------------------
TypeError: 'NoneType' object is not subscriptable


Можно удалить None перед сортировкой, а затем добавить их обратно (в конец или начало списка — в зависимости от задачи):


In [3]: sorted(
...: (d for d in data if d is not None),
...: key=lambda x: x['a']
...: ) + [
...: d for d in data if d is None
...: ]
Out[3]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]


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


In [4]: sorted(data, key=lambda x: float('inf') if x is None else x['a'])
Out[4]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]


Если тип данных не поддерживает бесконечность (float('inf')), можно сортировать кортежи:


In [5]: sorted(data, key=lambda x: (1, None) if x is None else (0, x['a']))
Out[5]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1
Когда вы используете fork для создания нового процесса, текущее состояние генератора случайных чисел (включая seed) копируется в дочерний процесс. Это может привести к тому, что разные процессы будут генерировать одинаковые «случайные» значения.

Чтобы избежать этого, необходимо вручную вызывать random.seed() в каждом процессе.

Однако, если вы используете модуль multiprocessing, он уже автоматически выполняет это за вас.

Пример:


import multiprocessing
import random
import os
import sys

def test(a):
print(random.choice(a), end=' ')

a = [1, 2, 3, 4, 5]

# Вызов в основном процессе
for _ in range(5):
test(a)
print()

# Вызов с multiprocessing.Process
for _ in range(5):
p = multiprocessing.Process(
target=test, args=(a,)
)
p.start()
p.join()
print()

# Вызов с использованием os.fork
for _ in range(5):
pid = os.fork()
if pid == 0:
test(a)
sys.exit()
else:
os.wait()
print()


Вывод будет примерно такой:


4 4 4 5 5
1 4 1 3 3
2 2 2 2 2


Причём, начиная с Python 3.7, os.fork также использует механизм at_fork hook, который переинициализирует генератор случайных чисел, как и multiprocessing.

Так что в Python 3.7+ вывод кода выше может быть таким:


1 2 2 1 5
4 4 4 5 5
2 4 1 3 1


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Скажем, вы хотите получить первые N элементов итерируемого объекта. Прямолинейный способ — использовать islice:


from itertools import islice

def fib():
a, b = 0, 1
while True:
yield b
a, b = b, (a + b)

list(islice(fib(), 5))
# Результат: [1, 1, 2, 3, 5]


Если вы также хотите получить индексы элементов, можно применить enumerate:


list(enumerate(islice(fib(), 5)))
# Результат: [(0, 1), (1, 1), (2, 2), (3, 3), (4, 5)]


Другой способ сделать это — использовать zip и range, что может показаться более читаемым:


list(zip(range(5), fib()))
# Результат: [(0, 1), (1, 1), (2, 2), (3, 3), (4, 5)]


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
collections.defaultdict позволяет создать словарь, который возвращает значение по умолчанию, если запрашиваемого ключа нет (вместо того чтобы выбрасывать исключение KeyError).
При создании defaultdict необходимо указывать не само значение по умолчанию, а фабрику для его создания.

Это позволяет создавать словари с бесконечным числом вложенных уровней, что дает возможность писать что-то вроде d[a][b][c]...[z].


>>> def infinite_dict():
... return defaultdict(infinite_dict)
...
>>> d = infinite_dict()
>>> d[1][2][3][4] = 10
>>> dict(d[1][2][3][5])
{}


Такое поведение называется “автовивификация” (от англ. autovivification) — термин пришёл из языка Perl.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Некоторые модули Python компилируются непосредственно в сам интерпретатор. Они называются встроенными модулями (built-in), и их не следует путать со стандартной библиотекой. Чтобы получить полный список таких модулей, можно использовать sys.builtin_module_names. Примеры таких модулей — sys, gc, time и т. д.

Обычно вам не важно, является ли модуль встроенным или нет; однако стоит иметь в виду, что import сначала ищет модуль среди встроенных. Поэтому будет загружен встроенный модуль sys, даже если в текущей директории есть файл sys.py. С другой стороны, если, например, в текущей директории есть файл datetime.py, он действительно может быть загружен вместо стандартного модуля datetime.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31
В Python 3, после выхода из блока except переменные, в которых хранятся перехваченные исключения, удаляются из locals(), даже если они существовали раньше:


>>> e = 2
>>> try:
... 1/0
... except Exception as e:
... pass
...
>>> e
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'e' is not defined


Если нужно сохранить ссылку на исключение, используйте другую переменную:


>>> error = None
>>> try:
... 1/0
... except Exception as e:
... error = e
...
>>> error
ZeroDivisionError('division by zero',)


В Python 2 это правило не действует.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ты можешь использовать любой объект в качестве ключа словаря в 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 зарезервировано для внутренних состояний ошибок, поэтому оно автоматически преобразуется в -2.

📲 Мы в MAX

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


class SavingOrig:
def __init__(self, another_decorator):
self._another = another_decorator

def __call__(self, f):
decorated = self._another(f)
if hasattr(f, 'orig'):
decorated.orig = f.orig
else:
decorated.orig = f
return decorated

saving_orig = SavingOrig


Последняя строка позволяет одновременно дать классу имя в стиле CamelCase и сохранить имя декоратора в стиле snake_case.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
В asyncio распространённая практика для планирования выполнения кода с задержкой — создать задачу, которая делает await asyncio.sleep(x):


import asyncio

async def do(n=0):
print(n)
await asyncio.sleep(1)
loop.create_task(do(n + 1))
loop.create_task(do(n + 1))

loop = asyncio.get_event_loop()
loop.create_task(do())
loop.run_forever()


Однако создание новой задачи может быть затратным и не требуется, если вы не собираетесь выполнять асинхронные операции (как в функции do из примера).
Другой способ сделать это — использовать функции loop.call_later и loop.call_at, которые планируют вызов асинхронного колбэка:


import asyncio

def do(n=0):
print(n)
loop = asyncio.get_event_loop()
loop.call_later(1, do, n+1)
loop.call_later(1, do, n+1)

loop = asyncio.get_event_loop()
do()
loop.run_forever()


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Оператор break подавляет исключение, если используется в блоке finally, даже когда блок except отсутствует:


for i in range(10):
try:
1 / i
finally:
print('finally')
break
print('after try')

print('after while')


Вывод:


finally
after while


То же самое верно и для continue, однако его нельзя использовать в блоке finally до версии Python 3.8:


SyntaxError: 'continue' not supported inside 'finally' clause


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Если вы хотите, чтобы объекты класса имели автоинкрементируемый ID, это можно сделать, отслеживая текущий ID в атрибуте класса:


class Task:
_task_id = 0

def __init__(self):
self._id = self._task_id
type(self)._task_id += 1


Учтите, что нельзя писать self._task_id += 1. Это создаст атрибут _task_id в экземпляре, а не в классе. Вместо этого стоит использовать фабричный метод, чтобы сделать код красивее:


class Task:
_task_id = 0

def __init__(self, task_id):
self._id = task_id

@classmethod
def create(cls):
obj = cls(cls._task_id)
cls._task_id += 1
return obj


Эта версия также проще для тестирования, так как можно легко задать любой пользовательский ID.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🚀 Подборка полезных IT каналов в Max


Системное администрирование, DevOps 📌

https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.
https://max.ru/tipsysdmin Типичный Сисадмин

Excel лайфхак 📌
https://t.me/Excel_lifehack Excel лайфхак

1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С

Программирование C++📌
https://max.ru/cpp_lib Библиотека C/C++ разработчика

Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика

Программирование React📌
https://max.ru/react_lib React

Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика

Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика

GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub

Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных

Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков

Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов

Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻

Шутки программистов 📌
https://max.ru/itumor Шутки программистов

Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free

Книги, статьи для дизайнеров 📌
https://max.ru/odesigners Статьи, книги для дизайнеров

Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике

Вакансии 📌
https://max.ru/progjob Вакансии в IT

Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных


Бонус 📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП
👎1