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

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

РКН clck.ru/3Ko7Hq
Download Telegram
Сортировка списка с элементами 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]


👉@BookPython
👍42
Когда вы используете 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


👉@BookPython
4👍21
Скажем, вы хотите получить первые 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)]


👉@BookPython
👍5
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.

👉@BookPython
👍41
Некоторые модули Python компилируются непосредственно в сам интерпретатор. Они называются встроенными модулями (built-in), и их не следует путать со стандартной библиотекой. Чтобы получить полный список таких модулей, можно использовать sys.builtin_module_names. Примеры таких модулей — sys, gc, time и т. д.

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

👉@BookPython
👍41
Reduce — это функция высшего порядка, которая рекурсивно обрабатывает итерируемый объект, применяя некоторое действие к следующему элементу и уже вычисленному значению. Возможно, вы также знаете её под названиями fold, inject, accumulate или другими.

Reduce с выражением result = result + element даёт сумму всех элементов, result = min(result, element) — минимум, а result = element позволяет получить последний элемент последовательности.

В Python есть функция reduce (в Python 3 она перенесена в functools.reduce):


from functools import reduce

reduce(lambda s, i: s + i, range(10))
# 45

reduce(lambda s, i: min(s, i), range(10))
# 0

reduce(lambda s, i: i, range(10))
# 9


Кроме того, если вам нужны простые лямбда-функции вроде a, b: a + b, Python предлагает модуль operator:


from operator import add
reduce(add, range(10))
# 45


👉@BookPython
👍4🤓1
Самый большой недостаток объектов с __slots__ заключается в том, что они не могут динамически получать произвольные атрибуты. Однако можно совместить подход с __slots__ и обычным __dict__.

Чтобы включить возможность динамического присваивания атрибутов объекту, просто добавьте '__dict__' в __slots__:


class A:
__slots__ = ('a', 'b', '__dict__')

A().x = 3


Также учтите, что наследуемые классы автоматически получают __dict__, если явно не указаны пустые __slots__:


class A:
__slots__ = ('a', 'b')

class B(A):
pass

B().x = 3


👉@BookPython
👍3
🚀 Подборка Telegram каналов для программистов

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

https://t.me/bash_srv Bash Советы
https://t.me/win_sysadmin Системный Администратор Windows
https://t.me/sysadmin_girl Девочка Сисадмин
https://t.me/srv_admin_linux Админские угодья
https://t.me/linux_srv Типичный Сисадмин
https://t.me/devopslib Библиотека девопса | DevOps, SRE, Sysadmin
https://t.me/linux_odmin Linux: Системный администратор
https://t.me/devops_star DevOps Star (Звезда Девопса)
https://t.me/i_linux Системный администратор
https://t.me/linuxchmod Linux
https://t.me/sys_adminos Системный Администратор
https://t.me/tipsysdmin Типичный Сисадмин (фото железа, было/стало)
https://t.me/sysadminof Книги для админов, полезные материалы
https://t.me/i_odmin Все для системного администратора
https://t.me/i_odmin_book Библиотека Системного Администратора
https://t.me/i_odmin_chat Чат системных администраторов
https://t.me/i_DevOps DevOps: Пишем о Docker, Kubernetes и др.
https://t.me/sysadminoff Новости Линукс Linux

1C разработка 📌
https://t.me/odin1C_rus Cтатьи, курсы, советы, шаблоны кода 1С
https://t.me/DevLab1C 1С:Предприятие 8
https://t.me/razrab_1C 1C Разработчик
https://t.me/buh1C_prog 1C Программист | Бухгалтерия и Учёт
https://t.me/rabota1C_rus Вакансии для программистов 1С

Программирование C++📌
https://t.me/cpp_lib Библиотека C/C++ разработчика
https://t.me/cpp_knigi Книги для программистов C/C++
https://t.me/cpp_geek Учим C/C++ на примерах

Программирование Python 📌
https://t.me/pythonofff Python академия.
https://t.me/BookPython Библиотека Python разработчика
https://t.me/python_real Python подборки на русском и английском
https://t.me/python_360 Книги по Python

Java разработка 📌
https://t.me/BookJava Библиотека Java разработчика
https://t.me/java_360 Книги по Java Rus
https://t.me/java_geek Учим Java на примерах

GitHub Сообщество 📌
https://t.me/Githublib Интересное из GitHub

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

Мобильная разработка: iOS, Android 📌
https://t.me/developer_mobila Мобильная разработка
https://t.me/kotlin_lib Подборки полезного материала по Kotlin

Фронтенд разработка 📌
https://t.me/frontend_1 Подборки для frontend разработчиков
https://t.me/frontend_sovet Frontend советы, примеры и практика!
https://t.me/React_lib Подборки по React js и все что с ним связано

Разработка игр 📌
https://t.me/game_devv Все о разработке игр

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

БигДата, машинное обучение 📌
https://t.me/bigdata_1 Big Data, Machine Learning

Программирование 📌
https://t.me/bookflow Лекции, видеоуроки, доклады с IT конференций
https://t.me/rust_lib Полезный контент по программированию на Rust
https://t.me/golang_lib Библиотека Go (Golang) разработчика
https://t.me/itmozg Программисты, дизайнеры, новости из мира IT
https://t.me/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻
https://t.me/nodejs_lib Подборки по Node js и все что с ним связано
https://t.me/ruby_lib Библиотека Ruby программиста
https://t.me/lifeproger Жизнь программиста. Авторский канал.

QA, тестирование 📌
https://t.me/testlab_qa Библиотека тестировщика

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

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

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

Математика 📌
https://t.me/Pomatematike Канал по математике
https://t.me/phis_mat Обучающие видео, книги по Физике и Математике
https://t.me/matgeoru Математика | Геометрия | Логика

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

https://t.me/mir_teh Мир технологий (Technology World)

Вакансии 📌
https://t.me/sysadmin_rabota Системный Администратор
https://t.me/progjob Вакансии в IT
В 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 это правило не действует.

👉@BookPython
👍411
Если вы хотите создать словарь из известного набора ключей и одного фиксированного значения для всех них, можно использовать словарное включение (dictionary comprehension):


keys = ['a', 'b', 'c']
{k: True for k in keys}


Результат:


{'a': True, 'b': True, 'c': True}


Однако у класса dict есть специальный метод класса fromkeys, предназначенный именно для этого случая:


dict.fromkeys(keys, True)


Результат:


{'a': True, 'b': True, 'c': True}


👉@BookPython
👍6❤‍🔥1
Ты можешь использовать любой объект в качестве ключа словаря в 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.

👉@BookPython
👍41
Если декоратор, который вы пишете, становится слишком сложным, имеет смысл преобразовать его из функции в класс с методом __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.

👉@BookPython
👍31
В 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()


👉@BookPython
2👍1
Оператор 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


👉@BookPython
👍5
Если вы хотите, чтобы объекты класса имели автоинкрементируемый 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.

👉@BookPython
👍3🤔1
Можно добавлять символы Unicode в строковый литерал не только по их номеру, но и по имени.


>>> '\N{EM DASH}'
'—'
>>> '\u2014'
'—'


Это также совместимо с f-строками:


>>> width = 800
>>> f'Width \N{EM DASH} {width}'
'Width — 800'


👉@BookPython
👍41
Python предоставляет мощную библиотеку для работы с датой и временем — datetime.
Интересная особенность в том, что объекты datetime имеют специальный интерфейс для поддержки часовых поясов (а именно атрибут tzinfo), но сам модуль реализует этот интерфейс лишь частично, оставляя остальную работу другим модулям.

Наиболее популярный модуль для этой задачи — pytz.
Хитрость в том, что pytz не полностью соответствует интерфейсу tzinfo. В документации pytz это указано уже в первых строках:

«Эта библиотека отличается от задокументированного Python API для реализаций tzinfo».


Вы не можете использовать объекты часовых поясов pytz напрямую в качестве tzinfo. Если попробовать, можно получить совершенно неожиданные результаты:


In : paris = pytz.timezone('Europe/Paris')
In : str(datetime(2017, 1, 1, tzinfo=paris))
Out: '2017-01-01 00:00:00+00:09'


Обратите внимание на смещение +00:09.
Правильное использование pytz выглядит так:


In : str(paris.localize(datetime(2017, 1, 1)))
Out: '2017-01-01 00:00:00+01:00'


Также после любых арифметических операций с датами рекомендуется нормализовать объект datetime на случай изменения смещения (например, на границе перехода на летнее время):


In : new_time = time + timedelta(days=2)
In : str(new_time)
Out: '2018-03-27 00:00:00+01:00'

In : str(paris.normalize(new_time))
Out: '2018-03-27 01:00:00+02:00'


Начиная с Python 3.6, рекомендуется использовать dateutil.tz вместо pytz.
Он полностью совместим с tzinfo, может напрямую передаваться в атрибут tzinfo, не требует нормализации, хотя работает немного медленнее.

👉@BookPython
👍4
Есть шесть магических методов Python, которые определяют правила сравнения объектов:
__lt__ для <
__gt__ для >
__le__ для <=
__ge__ для >=
__eq__ для ==
__ne__ для !=

Если некоторые из этих методов не определены или возвращают NotImplemented, применяются следующие правила:
a.__lt__(b) эквивалентно b.__gt__(a)
a.__le__(b) эквивалентно b.__ge__(a)
a.__eq__(b) эквивалентно not a.__ne__(b) (обратите внимание, что в этом случае a и b не меняются местами)

Однако, из a >= b и a != b не следует автоматически, что a > b.
Декоратор functools.total_ordering создает все шесть методов на основе __eq__ и одного из следующих: __lt__, __gt__, __le__ или __ge__.


from functools import total_ordering

@total_ordering
class User:
def __init__(self, pk, name):
self.pk = pk
self.name = name

def __le__(self, other):
return self.pk <= other.pk

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

assert User(2, 'Vadim') < User(13, 'Catherine')


👉@BookPython
👍4
В Python можно переопределить оператор квадратных скобок ([]), реализовав магический метод __getitem__.
Вот так можно создать объект, который виртуально содержит бесконечное количество повторяющихся элементов:


class Cycle:
def __init__(self, lst):
self._lst = lst

def __getitem__(self, index):
return self._lst[index % len(self._lst)]

print(Cycle(['a', 'b', 'c'])[100]) # 'b'


Необычность здесь в том, что оператор [] поддерживает особый синтаксис. Его можно использовать не только так — [2], но и так — [2:10], [2:10:2], [2::2] или даже [:].
Обычно это интерпретируется как [start:stop:step], но вы можете задать любую логику для своих объектов.

Что же передаётся в параметр index метода __getitem__, если использовать такой синтаксис? Для этого в Python существуют объекты slice.


class Inspector:
def __getitem__(self, index):
print(index)

Inspector()[1]
# 1

Inspector()[1:2]
# slice(1, 2, None)

Inspector()[1:2:3]
# slice(1, 2, 3)

Inspector()[:]
# slice(None, None, None)


Можно даже комбинировать кортежи и срезы:


Inspector()[:, 0, :]
# (slice(None, None, None), 0, slice(None, None, None))


Объект slice сам по себе ничего не делает — он просто хранит атрибуты start, stop и step:


s = slice(1, 2, 3)
s.start # 1
s.stop # 2
s.step # 3


👉@BookPython
👍5
Оператор in может использоваться с генераторами: x in g.
Python будет перебирать элементы генератора g, пока не найдёт x или пока генератор не исчерпает элементы.


>>> def g():
... print(1)
... yield 1
... print(2)
... yield 2
... print(3)
... yield 3
...
>>> 2 in g()
1
2
True


Однако range() делает для вас больше.
У него переопределён магический метод __contains__, что позволяет оператору in работать за O(1):


In [1]: %timeit 10**20 in range(10**30)
375 ns ± 10.7 ns per loop


Имейте в виду, что это не работает для функции xrange() в Python 2.

👉@BookPython
👍1
Оператор in можно использовать с генераторами: x in g.
Python будет итерироваться по g, пока не найдёт x или пока генератор не закончится.


>>> def g():
... print(1)
... yield 1
... print(2)
... yield 2
... print(3)
... yield 3
...
>>> 2 in g()
1
2
True


Однако range() делает для вас больше.
У него переопределён магический метод __contains__, который позволяет оператору in работать с O(1) сложностью:


In [1]: %timeit 10**20 in range(10**30)
375 ns ± 10.7 ns per loop


Имейте в виду, что это не работает для функции xrange() в Python 2.

👉@BookPython
👍41