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

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

РКН clck.ru/3Ko7Hq
Download Telegram
Проблема при вызове repr для других объектов внутри собственного метода __repr__ заключается в том, что нельзя гарантировать, что ни один из этих объектов не равен self, и вызов не окажется рекурсивным:


In : p = Pair(1, 2)
In : p
Out: Pair(1, 2)
In : p.right = p
In : p
Out: [...]
RecursionError: maximum recursion depth exceeded while calling a Python object


Чтобы легко решить эту проблему, можно использовать декоратор reprlib.recursive_repr:


@reprlib.recursive_repr()
def __repr__(self):
class_name = type(self).__name__
return f'{class_name}({self.left!r}, {self.right!r})'


Теперь всё работает корректно:


In : p = Pair(1, 2)
In : p.right = p
In : p
Out: Pair(1, ...)


👉@BookPython
👍1
Словари, которые используются для хранения атрибутов объектов, не такие же, как те, что вы создаёте с помощью dict, хотя выглядят они абсолютно одинаково:


>>> from sys import getsizeof
>>> class A:
... pass
...
>>> a = dict()
>>> b = A().__dict__
>>> type(a)
<class 'dict'>
>>> type(b)
<class 'dict'>
>>> a
{}
>>> b
{}
>>> getsizeof(a)
240
>>> getsizeof(b)
112


Чтобы уменьшить потребление памяти, словари для __dict__ реализованы иначе. Они разделяют ключи между всеми экземплярами класса A. Однако важно понимать, что b на самом деле не меньше, чем a, - это просто особенность работы getsizeof.

👉@BookPython
4👍1
Функции, объявленные в теле класса, не видят область видимости класса. Это логично, так как область видимости класса существует только во время создания класса.


>>> class A:
... x = 2
... def f():
... print(x)
... f()
...
[...]
NameError: name 'x' is not defined


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


>>> class A:
... x = 2
... def f(self):
... print(self.x)
...
>>> A().f()
2


Что немного неожиданно — то же самое верно и для генераторов и списковых включений (comprehensions).
Они имеют свою собственную область видимости и не могут обращаться к области видимости класса.

Это особенно логично для генераторов, так как они выполняются уже после того, как создание класса завершено.


>>> class A:
... x = 2
... y = [x for _ in range(5)]
...
[...]
NameError: name 'x' is not defined


Comprehensions при этом не имеют доступа к self.
Единственный способ заставить это работать - добавить ещё один уровень области видимости, например, через lambda (да, это выглядит не слишком красиво):


>>> class A:
... x = 2
... y = (lambda x=x: [x for _ in range(5)])()
...
>>> A.y
[2, 2, 2, 2, 2]


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

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

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

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


def x():
...


👉@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
1
В Python разные структуры данных объединяются разными способами.

Списки используют оператор +:


>>> [1, 2] + [2, 3]
[1, 2, 2, 3]


Кортежи и строки также используют +:


>>> (1, 2) + (2, 3)
(1, 2, 2, 3)

>>> "12" + "23"
'1223'


Deque (двусторонняя очередь) тоже поддерживает +:


>>> deque([1, 2]) + deque([2, 3])
deque([1, 2, 2, 3])


Множества объединяются с помощью оператора |:


>>> {1, 2} | {2, 3}
{1, 2, 3}


Словари объединяются по-другому, и порядок важен, если ключи пересекаются:


>>> {**dict(a=1, b=2), **dict(b=3, c=4)}
{'a': 1, 'b': 3, 'c': 4}

>>> {**dict(b=3, c=4), **dict(a=1, b=2)}
{'b': 2, 'c': 4, 'a': 1}


Counter (счётчик) можно сложить с помощью +, при этом значения суммируются:


>>> Counter(dict(a=1, b=2)) + Counter(dict(b=3, c=4))
Counter({'b': 5, 'c': 4, 'a': 1})


👉@BookPython
👍4
В Python None равен None, поэтому может показаться, что проверку на None можно делать через ==:


ES_TAILS = ('s', 'x', 'z', 'ch', 'sh')

def make_plural(word, exceptions=None):
if exceptions == None: # ← ← ←
exceptions = {}

if word in exceptions:
return exceptions[word]
elif any(word.endswith(t) for t in ES_TAILS):
return word + 'es'
elif word.endswith('y'):
return word[0:-1] + 'ies'
else:
return word + 's'

exceptions = dict(
mouse='mice',
)

print(make_plural('python'))
print(make_plural('bash'))
print(make_plural('ruby'))
print(make_plural('mouse', exceptions=exceptions))


Однако так делать неправильно. Действительно, None равен None, но не только он может быть равен None. Пользовательские объекты тоже могут вернуть True при сравнении с None через ==:


class A:
def __eq__(self, other):
return True

print(A() == None) # True
print(A() is None) # False


Правильный способ проверки на None — использовать is None.

👉@BookPython
👍4
В Python числа с плавающей точкой могут иметь значение NaN. Его можно получить с помощью math.nan.
NaN не равен ничему, включая самого себя:


>>> math.nan == math.nan
False


Кроме того, объект NaN не является уникальным — можно получить несколько разных объектов NaN из разных источников:


>>> float('nan')
nan
>>> float('nan') is float('nan')
False


Это означает, что обычно нельзя использовать NaN в качестве ключа словаря:


>>> d = {}
>>> d[float('nan')] = 1
>>> d[float('nan')] = 2
>>> d
{nan: 1, nan: 2}


👉@BookPython
👍31
Некоторые модули могут содержать такие загадочные конструкции:


try:
cache
except NameError:
cache = {}


На первый взгляд, нет смысла писать что-то подобное. Переменная cache однозначно вызовет NameError в начале выполнения модуля, так как она ещё не была определена.

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

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

👉@BookPython
1👍1👎1
Класс объекта доступен через атрибут __class__:


>>> [1, 2].__class__
<class 'list'>


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


>>> type([1, 2])
<class 'list'>


Если вы хотите проверить, является ли объект экземпляром заданного класса, следует использовать isinstance, а не сравнение:


>>> class A:
... pass
...
>>> class B(A):
... pass
...
>>> type(B())
<class '__main__.B'>
>>> isinstance(B(), A)
True


👉@BookPython
👍3
Python позволяет перегружать многие разные операторы, и оператор сдвига — один из них.
Вот пример того, как можно создать композицию функций с использованием этого оператора. Здесь символы, похожие на стрелки, показывают направление потока данных:


from collections import deque
from math import sqrt


class Compose:
def __init__(self):
self._functions = deque()

def __call__(self, *args, **kwargs):
result = None
for f in self._functions:
result = f(*args, **kwargs)
args = [result]
kwargs = dict()
return result

def __rshift__(self, f):
self._functions.append(f)
return self

def __lshift__(self, f):
self._functions.appendleft(f)
return self


compose = Compose


sqrt_abs = (compose() << sqrt << abs)
sqrt_abs2 = (compose() >> abs >> sqrt)

print(sqrt_abs(-4)) # 2.0
print(sqrt_abs2(-4)) # 2.0


Объяснение:

<< — добавляет функцию в начало цепочки (выполняется первой).
>> — добавляет функцию в конец цепочки (выполняется последней).
В примере sqrt_abs(-4) сначала берёт abs(-4) → 4, а затем sqrt(4) → 2.0.
sqrt_abs2(-4) делает то же самое, но функции добавлены в другом порядке.

👉@BookPython
👍3🔥1
Все объекты в Python создаются с помощью вызова метода __new__. Даже если вы определяете свой собственный __new__ для класса, вы должны вызвать super().__new__(...).

Можно подумать, что object.__new__ — это базовая реализация, отвечающая за создание всех объектов. Но это не совсем так. Существует несколько таких реализаций, и они несовместимы.

Например, у dict есть собственная низкоуровневая реализация __new__, и объекты типов, унаследованных от dict, нельзя создать с помощью object.__new__:


In : class D(dict):
...: pass
...:

In : class A:
...: pass
...:

In : object.__new__(A)
Out: <__main__.A at 0x7f200c8902e8>

In : object.__new__(D)
...
TypeError: object.__new__(D) is not safe,
use D.__new__()


👉@BookPython
3👍2
Программируешь на Python? Попробуй оживить робота своим алгоритмом и поборись за призовой фонд в 10 250 000 рублей на True Tech Champ 2025.

True Tech Champ 2025 — это третий всероссийский чемпионат по программированию от МТС с онлайн-этапами отбора и грандиозным шоу-финалом в Москве.

Тебя ждут два трека — выбирай:

I. Алгоритмический [призовой фонд 2 750 000 рублей].

Если классический олимпиадный формат — твоя стихия, этот трек для тебя. Блесни математическими навыками, покажи скилы в работе со структурами данных и написании алгоритмов — и окажись выше соперников в турнирной таблице.

II. Программирование роботов [призовой фонд 7 500 000 рублей].

Запрограммируй робота на скоростное прохождение лабиринта в симуляторе и пройди в финал. На финале участники встретятся офлайн и сразятся на четырех уровнях с полосой препятствий, вспышками света, лазерами и другими препятствиями.

Трек будет интересен начинающим и опытным разработчикам: С++, Go, Python, JS, Java, C# и не только.

Подробности на сайте. Регистрация открыта до 20 октября.
❤‍🔥1
Очередь с приоритетами - это структура данных, которая поддерживает две операции: добавление элемента и извлечение минимального элемента среди всех ранее добавленных.

Одна из самых распространённых реализаций очереди с приоритетами - бинарная куча (binary heap). Это полное бинарное дерево, обладающее следующим свойством: ключ, хранящийся в каждом узле, меньше или равен (≤) ключам в дочерних узлах.
Минимальный элемент из всех находится в корне дерева.


1

3 7

5 4 9 8

15 16 17 18 19


В бинарной куче операции вставки и извлечения минимального элемента выполняются за O(log n).


Хранение бинарной кучи в памяти

Полное бинарное дерево обычно хранится в массиве, где:

* левый потомок элемента x[i] находится по индексу 2*i + 1,
* правый потомок - по индексу 2*i + 2.

Пример массива для дерева выше:


[1, 3, 7, 5, 4, 9, 8, 15, 16, 17, 18, 19]



Работа с кучей в Python

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

Пример использования:


from heapq import *

# Создаём список
heap = [3, 2, 1]

# Преобразуем список в кучу
heapify(heap)
print(heap) # [1, 2, 3]

# Добавляем элемент в кучу
heappush(heap, 0)
print(heap) # [0, 1, 3, 2]

# Извлекаем минимальный элемент
print(heappop(heap)) # 0

# Куча после извлечения
print(heap) # [1, 2, 3]


👉@BookPython
👍1
Вы можете передавать аргументы в пользовательский метакласс прямо из определения класса.
Синтаксис класса поддерживает именованные аргументы:


class Klass(Parent, arg='arg')


Ключевое слово metaclass зарезервировано для указания метакласса, но остальные аргументы можно использовать свободно.

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


class FilterMeta(type):
def __new__(mcs, name, bases, namespace, remove=None, **kwargs):
if remove is not None and remove in namespace:
del namespace[remove]

return super().__new__(mcs, name, bases, namespace)


class A(metaclass=FilterMeta, remove='half'):
def half(x):
return x // 2

half_of_4 = half(4)
half_of_100 = half(100)


a = A()
print(a.half_of_4) # 2
print(a.half_of_100) # 50
a.half # AttributeError


В этом примере метод half удаляется из класса A во время его создания, поэтому попытка обратиться к a.half вызывает AttributeError.

👉@BookPython
👍1
Стандартный модуль json имеет интерфейс командной строки, который может быть полезен для форматирования JSON с помощью одного только Python. Этот модуль называется json.tool и используется следующим образом:


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


👉@BookPython
👍3
Генератор можно остановить. Ты можешь явно вызвать g.close(), но обычно это делает сборщик мусора. Когда вызывается close, в точке, где выполнение генератора было приостановлено, выбрасывается исключение GeneratorExit:


def gen():
try:
yield 1
yield 2
finally:
print('END')


g = gen()
print(next(g)) # выведет '1'
g.close() # выведет 'END'


Обрати внимание на три момента:


1. Нельзя использовать yield при обработке GeneratorExit

Если в блоке finally попытаться сделать yield, возникнет ошибка RuntimeError:


def gen():
try:
yield 1
finally:
yield 3 # ошибка!


g = gen()
next(g)
g.close() # RuntimeError



2. Исключение не выбрасывается, если генератор ещё не запускался

В этом случае генератор просто переходит в состояние остановлен, но finally не выполняется:


def gen():
try:
yield 1
finally:
print('END')


g = gen()
g.close() # ничего не выводит
print(list(g)) # выведет '[]'



3. close() ничего не делает, если генератор уже завершён

Если генератор полностью отработал, close() не вызывает finally повторно и просто игнорируется:


def gen():
try:
yield 1
yield 2
finally:
print('END')


g = gen()
print(list(g)) # ['1', '2']
print('Closing now')
g.close()

# Вывод:
# END
# [1, 2]
# Closing now


👉@BookPython
👍3
Модуль functools для манипуляций с функциями

Модуль functools в Python предоставляет инструменты для работы с функциями, позволяя выполнять различные манипуляции с ними. Вот некоторые из наиболее важных функций и возможностей, которые предоставляет functools:

1. functools.partial: Позволяет зафиксировать некоторые аргументы функции и создать новую функцию с предопределенными значениями.


from functools import partial

def multiply(x, y):
return x * y

double = partial(multiply, 2)
print(double(5)) # Вывод: 10


2. functools.reduce: Применяет функцию к паре элементов в последовательности, сокращая ее до одного значения. Обычно используется для аккумуляции значений.


from functools import reduce

numbers = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, numbers)
print(product) # Вывод: 24


3. functools.lru_cache: Кэширует результаты вызовов функции, чтобы ускорить повторные вызовы с теми же аргументами. Полезно для функций с дорогими вычислениями.


from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(10)) # Вывод: 55


4. functools.wraps: Декоратор, который сохраняет метаданные оригинальной функции (такие как имя и документация) при создании декоратора.


from functools import wraps

def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Что-то делаем перед вызовом функции")
return func(*args, **kwargs)
return wrapper

@my_decorator
def say_hello():
"""Выводит приветствие."""
print("Привет!")

print(say_hello.__name__) # Вывод: say_hello
print(say_hello.__doc__) # Вывод: Выводит приветствие.


5. functools.total_ordering: Упрощает реализацию всех методов сравнения для класса, определяя только несколько из них.


from functools import total_ordering

@total_ordering
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

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

def __lt__(self, other):
return (self.x, self.y) < (other.x, other.y)

p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1 < p2) # Вывод: True
print(p1 <= p2) # Вывод: True


Эти функции и декораторы делают functools мощным инструментом для функционального программирования в Python.

Мы в MAX

👉@BookPython
👍4
Когда вы пишете собственный метод __repr__ для какого-то объекта, обычно нужно включать представление его атрибутов. Для этого при форматировании следует вызывать repr() для объектов, так как по умолчанию вызывается str().

Это делается с помощью нотации !r:


class Pair:
def __init__(self, left, right):
self.left = left
self.right = right

def __repr__(self):
class_name = type(self).__name__
return f'{class_name}({self.left!r}, {self.right!r})'


Мы в MAX

👉@BookPython
👍3
Что такое Pickling и Unpickling?

В Python pickling и unpickling — это процессы сериализации и десериализации объектов, соответственно, с использованием модуля pickle.

- Pickling — это процесс преобразования объекта Python (например, словаря, списка, класса и т.д.) в байтовую последовательность (или поток байтов), которая может быть сохранена в файл или передана по сети. Этот процесс позволяет сохранить состояние объекта для дальнейшего использования.

- Unpickling — это обратный процесс, при котором байтовая последовательность преобразуется обратно в объект Python.

Пример использования:

1. Pickling (сериализация):


import pickle

# Создаем объект
data = {'name': 'Alice', 'age': 25, 'city': 'New York'}

# Сохраняем объект в файл
with open('data.pickle', 'wb') as f:
pickle.dump(data, f)


2. Unpickling (десериализация):


import pickle

# Читаем объект из файла
with open('data.pickle', 'rb') as f:
loaded_data = pickle.load(f)

print(loaded_data)


Когда это может быть полезно?
- Сохранение промежуточных данных в файл для повторного использования.
- Передача сложных объектов между разными программами или системами.
- Сохранение состояния приложения.

Однако, нужно быть осторожным с unpickling, так как загрузка непроверенных данных может привести к выполнению вредоносного кода.

Мы в MAX

👉@BookPython
👍2