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

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

РКН clck.ru/3Ko7Hq
Download Telegram
Чтобы объект можно было использовать в качестве ключа словаря, он должен быть хешируемым. Хешируемые объекты поддерживают метод __hash__, который возвращает целое число. Для получения хеша значения используется встроенная функция hash.

Встроенные типы, которые являются неизменяемыми, по умолчанию хешируемы. Все пользовательские объекты тоже хешируемы, но есть нюанс. Если вы определяете метод __eq__ для своего типа, то вы также должны определить __hash__ таким образом, чтобы hash(a) == hash(b) для всех a и b, которые считаются равными. Нарушение этого правила может привести к некорректной работе словаря:


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

def __hash__(self):
return random.randrange(10000)

def __eq__(self, other):
return self.x == other.x



d = {}
d[A(2)] = 2
d.get(A(2), 0)
# Вывод: 0


Обратите внимание: как только вы определяете __eq__ в классе, реализация __hash__ по умолчанию удаляется, так как она больше не подходит (по умолчанию все значения считаются неравными).

📲 Мы в MAX

👉@BookPython
🔥3
Python поддерживает цепочные присваивания со следующим синтаксисом:


a = b = c = 42


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


a = (b = (c = 42))


В Python всё не так. Операция присваивания не возвращает результат — это оператор, а не выражение. Вместо этого происходит несколько присваиваний слева направо:


2 0 LOAD_CONST 1 (42)
2 DUP_TOP
4 STORE_FAST 0 (a)
6 DUP_TOP
8 STORE_FAST 1 (b)
10 STORE_FAST 2 (c)


📲 Мы в MAX

👉@BookPython
🔥2👍1
Как отлаживать Python-код, не выходя из редактора - с помощью встроенного модуля pdb.

🔍 Быстрая отладка с pdb

Часто, когда код не работает как надо, мы начинаем закидывать print()-ами. Но это неудобно, медленно и мусорит код. Вместо этого вставь в нужное место строчку:


import pdb; pdb.set_trace()


Когда выполнение дойдет до этой строки, ты попадешь в интерактивную консоль отладчика прямо в терминале. Дальше можно:

- n (next) — перейти к следующей строке;
- s (step) — зайти внутрь функции;
- c (continue) — продолжить выполнение;
- l (list) — показать текущий контекст;
- p var — вывести значение переменной var.

💡 Пример


def calc(a, b):
import pdb; pdb.set_trace()
result = a + b
return result

calc(2, 3)


На строке с pdb.set_trace() ты остановишься и сможешь изучить, что происходит внутри.

Зачем это нужно?

- Понять, почему что-то идет не так.
- Посмотреть, какие значения у переменных прямо в момент ошибки.
- Быстро отладить без запуска IDE - удобно в Docker, SSH или при работе с cron.

Попробуй - один раз освоишь, и уже не захочешь возвращаться к print().


📲 Мы в MAX

👉@BookPython
🔥4👍3
Обработка исключений в асинхронных программах может быть непростой задачей.

В asyncio, если корутина выбрасывает исключение, оно передаётся в тот код, который ожидает соответствующий future. Если await вызывается в нескольких местах, то каждое из них получит это исключение (так как оно сохраняется внутри объекта исключения). Следующий код напечатает сообщение об ошибке пять раз:


import asyncio

async def error():
await asyncio.sleep(1)
raise ValueError()

async def waiter(task):
try:
await task
except ValueError:
print('error')
else:
print('OK')

async def main():
task = asyncio.get_event_loop().create_task(error())

for _ in range(5):
asyncio.get_event_loop().create_task(waiter(task))

await asyncio.sleep(2)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())


Если исключение выброшено, но задача (task) ни разу не была ожидаема (awaited), исключение будет потеряно. В таком случае при уничтожении задачи вы получите предупреждение: “Task exception was never retrieved”.

Когда вы используете await asyncio.gather(tasks) и одна из задач выбрасывает исключение, оно передаётся наружу. Однако если несколько задач выбросят исключения, вы получите только первое, остальные будут проигнорированы:


import asyncio

async def error(i):
await asyncio.sleep(1)
raise ValueError(i)

async def main():
try:
await asyncio.gather(
error(1),
error(2),
error(3),
)
except ValueError as e:
print(e)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())


Вы можете использовать gather с параметром return_exceptions=True, чтобы получать исключения как обычные значения. Следующий код напечатает: [42, ValueError(2,), ValueError(3,)]


import asyncio

async def error(i):
await asyncio.sleep(1)
if i > 1:
raise ValueError(i)
return 42

async def main():
results = await asyncio.gather(
error(1),
error(2),
error(3),
return_exceptions=True,
)

print(results)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())


📲 Мы в MAX

👉@BookPython
❤4👍1🔥1
В Python очень короткий список встроенных констант. Одна из них — Ellipsis, которую также можно записать как .... Эта константа не имеет особого значения для интерпретатора, но используется в местах, где такой синтаксис выглядит уместно.

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

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

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


def x():
...


📲 Мы в MAX

👉@BookPython
👍2🔥1
Чтобы сохранить любую информацию в памяти или на устройстве хранения, её необходимо представить в виде байтов. Python, как правило, предоставляет уровень абстракции, при котором вы оперируете непосредственно данными, а не их байтовым представлением.

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

Существует сотни методов кодирования. Самый популярный — вероятно, Unicode, но он сам по себе не может преобразовывать данные в байты. В смысле байтового представления Unicode вообще не является кодировкой. Unicode определяет соответствие между символами и их числовыми кодами. Например, 🐍 имеет код 128013.

Но чтобы записать числа в файл, нужна настоящая кодировка. Unicode обычно используется вместе с utf-8, которая (в большинстве случаев) является стандартной в Python. При чтении из файла Python автоматически декодирует utf-8. Вы можете выбрать любую другую кодировку с помощью параметра encoding= функции open, либо читать «сырые» байты, добавив b к режиму открытия файла.

📲 Мы в MAX

👉@BookPython
Если вы создаёте новые объекты внутри метода __init__, возможно, будет лучше передавать их как аргументы и использовать фабричный метод. Это позволяет разделить бизнес-логику и технические детали создания объектов.

В этом примере __init__ принимает host и port для создания подключения к базе данных:


class Query:
def __init__(self, host, port):
self._connection = Connection(host, port)


Возможный рефакторинг:


class Query:
def __init__(self, connection):
self._connection = connection

@classmethod
def create(cls, host, port):
return cls(Connection(host, port))


Такой подход имеет как минимум следующие преимущества:

• Упрощает внедрение зависимостей. В тестах можно использовать Query(FakeConnection()).
• Класс может иметь столько фабричных методов, сколько нужно; подключение может создаваться не только по host и port, но и путём клонирования другого подключения, чтения конфигурационного файла или объекта, использования значения по умолчанию и т.д.
• Такие фабричные методы можно сделать асинхронными; это невозможно для __init__.

📲 Мы в MAX

👉@BookPython
👍5
🚀 Подборка полезных 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 Типичный Сисадмин
https://max.ru/channel_win_sysadmin Системный Администратор Windows
https://max.ru/channel_linux_admin Linux: Системный администратор
https://max.ru/channel_linuxmod Linux
https://max.ru/channel_i_linux Системный администратор
https://max.ru/channel_devopslib DevOps, SRE, Sysadmin
https://max.ru/channel_devops_star DevOps Star (Звезда Девопса)

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

Английский с нуля 🇬🇧
https://max.ru/UchuEnglish

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

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

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

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

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

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

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

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 Обучающие видео, книги по Физике и Математике

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

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

Городские📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП
https://max.ru/channel_krasnodar_novosty Краснодар Новости
https://max.ru/channel_novosibirsk_novosti Новосибирск
https://max.ru/channel_samara_novosti Новости Самары
https://max.ru/channel_ekaterinburg_novosti Новости Екатеринбурга
https://max.ru/channel_kazan_novosti Новости Казани
https://max.ru/channel_omsk_novosti Новости Омска
https://max.ru/channel_moskva_24 Москва 24
Обычно вы взаимодействуете с генератором, запрашивая данные с помощью next(gen). В Python 3 вы также можете отправлять значения обратно в генератор с помощью g.send(x). Но существует техника, которой вы, вероятно, не пользуетесь каждый день, а возможно, и вовсе не знаете: выбрасывание исключений внутри генератора.

С помощью gen.throw(e) можно выбросить исключение в той точке, где генератор gen приостановлен — то есть на инструкции yield. Если генератор обрабатывает это исключение, gen.throw(e) возвращает следующее значение, полученное через yield (или выбрасывает StopIteration, если генератор завершён). Если генератор не перехватывает исключение, оно пробрасывается обратно к вызывающему коду.


def gen():
try:
yield 1
except ValueError:
yield 2

g = gen()

next(g)
# Out: 1

g.throw(ValueError)
# Out: 2

g.throw(RuntimeError('TEST'))
# RuntimeError: TEST


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

Тем не менее, декоратор @contextmanager из модуля contextlib использует именно такую технику, позволяя коду внутри контекста перехватывать исключения.


from contextlib import contextmanager

@contextmanager
def atomic():
print('BEGIN')

try:
yield
except Exception:
print('ROLLBACK')
else:
print('COMMIT')

with atomic():
print('ERROR')
raise RuntimeError()



BEGIN
ERROR
ROLLBACK


📲 Мы в MAX

👉@BookPython
👍2
Стандартный механизм расширения путей в оболочке называется globbing. Шаблоны, которые вы используете для сопоставления путей, называются globs.


$ echo /li*
/lib /lib64


Python поддерживает globbing с помощью модуля glob. Однако есть важное замечание: оболочка возвращает сам шаблон, если файлы не найдены, а Python — нет:


$ echo /zz**
/zz**
$ python -c 'from glob import glob; print(glob("/zz**"))'
[]


📲 Мы в MAX

👉@BookPython
👍2❤1🔥1
Функция super() позволяет обращаться к родительскому (базовому) классу. Это может быть очень полезно в случаях, когда производный класс хочет добавить что-то к реализации метода, а не полностью переопределять его:


class BaseTestCase(TestCase):
def setUp(self):
self._db = create_db()

class UserTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self._user = create_user()


Имя функции super не означает "отличный" или "очень хороший". В данном контексте слово super означает "выше" (как, например, в слове superintendent — заведующий). Несмотря на это, super() не всегда ссылается на базовый класс — он может вернуть и "соседний" класс. Более точным названием была бы, возможно, функция next(), так как возвращается следующий класс согласно цепочке разрешения методов (MRO — Method Resolution Order).

Пример:


class Top:
def foo(self):
return 'top'

class Left(Top):
def foo(self):
return super().foo()

class Right(Top):
def foo(self):
return 'right'

class Bottom(Left, Right):
pass

# выводит 'right'
print(Bottom().foo())


Обрати внимание: результат работы super() может отличаться в зависимости от MRO вызвавшего объекта.


>>> Bottom().foo()
'right'
>>> Left().foo()
'top'


📲 Мы в MAX

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