Если вы хотите измерить время между двумя событиями, следует использовать time.monotonic() вместо time.time().time.monotonic() никогда не идёт назад, даже если системные часы были изменены:from contextlib import contextmanagerimport time@contextmanagerdef timeit(): start = time.monotonic() yield print(time.monotonic() - start)def main(): with timeit(): time.sleep(2)main()📲 Мы в MAX👉@BookPython
Библиотека Python разработчика | Книги по питону
@bookpython
Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍По всем вопросам @evgenycarterРКН clck.ru/3Ko7Hq
Похожие каналы
Все →Последние посты
Декоратор создаёт новый объект (обычно функцию), используя в качестве аргумента другую единственную функцию. Однако иногда хочется задать больше, чем одну функцию.Сделать это напрямую невозможно из-за ограничений синтаксиса Python, но можно использовать небольшой трюк. Возвращаемая функция может содержать ещё один декоратор, который можно повторно применить к дополнительным функциям, добавляя новое поведение. Примерно так работает @property:@propertydef x(self): return self._x@x.setterdef x(self, value): self._x = valueНиже приведён пример того, как можно определить функцию, которая использует дополнительные функции для особых случаев:from functools import wrapsdef 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_decoratordef special_cases(func): @wraps(func) def decorated(*args): return func(*args) decorated.case = make_case_decorator(decorated) return decorated@special_casesdef 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
Ты не можешь изменять переменные замыкания простым присваиванием.Python рассматривает присваивание как определение локальной переменной внутри тела функции и вообще не делает замыкания.Работает нормально, печатает 2:def make_closure(x): def closure(): print(x) return closuremake_closure(2)()Вызывает UnboundLocalError: local variable 'x' referenced before assignment:def make_closure(x): def closure(): print(x) x *= 2 print(x) return closuremake_closure(2)()Чтобы это заработало, нужно использовать nonlocal.Оно явно сообщает интерпретатору, что присваивание не создает новую локальную переменную, а работает с переменной из замыкания:def make_closure(x): def closure(): nonlocal x print(x) x *= 2 print(x) return closuremake_closure(2)()📲 Мы в MAX👉@BookPython
🚀 Подборка полезных 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 Программисты, дизайнеры, новости из мира IThttps://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Мир технологий 📌 ht
Любая выполняющаяся корутина asyncio может быть отменена с помощью метода cancel(). В корутину будет выброшено исключение CancelledError, что приведёт к её завершению, а также завершению всех оборачивающих её корутин, если только ошибка не будет перехвачена и подавлена.CancelledError является подклассом Exception, а значит, его можно случайно перехватить конструкцией try ... except Exception, которая предназначена для отлова «любых ошибок». Чтобы безопасно это обработать внутри корутины, приходится писать примерно так:try: await action()except asyncio.CancelledError: raiseexcept Exception: logging.exception('action failed')📲 Мы в MAX👉@BookPython
Условное использование менеджеров контекста обычно доставляет неудобства: нельзя просто разместить 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
В 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
В списковых включениях (list comprehensions) может быть больше одного цикла for и условия if:In : [(x, y) for x in range(3) for y in range(3)]Out: [ (0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]In : [ (x, y) for x in range(3) for y in range(3) if x != 0 if y != 0]Out: [(1, 1), (1, 2), (2, 1), (2, 2)]Кроме того, любое выражение внутри for и if может использовать все переменные, которые были определены ранее:In : [ (x, y) for x in range(3) for y in range(x + 2) if x != y]Out: [ (0, 1), (1, 0), (1, 2), (2, 0), (2, 1), (2, 3)]Можно смешивать if и for в любом порядке:In : [ (x, y) for x in range(5) if x % 2 for y in range(x + 2) if x != y]Out: [ (1, 0), (1, 2), (3, 0), (3, 1), (3, 2), (3, 4)]📲 Мы в MAX👉@BookPython
🚀 Подборка полезных 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 Программисты, дизайнеры, новости из мира IThttps://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Мир технологий 📌 ht
Оператор in можно использовать с генераторами: x in g.Python будет итерироваться по g, пока не найдёт x или пока генератор не закончится.>>> def g():... print(1)... yield 1... print(2)... yield 2... print(3)... yield 3...>>> 2 in g()12TrueОднако range() делает для вас больше.У него переопределён магический метод __contains__, который позволяет оператору in работать с O(1) сложностью:In [1]: %timeit 10**20 in range(10**30)375 ns ± 10.7 ns per loopИмейте в виду, что это не работает для функции xrange() в Python 2.📲 Мы в MAX👉@BookPython
В 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]# 1Inspector()[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 # 1s.stop # 2s.step # 3📲 Мы в MAX👉@BookPython
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, не требует нормализации, хотя работает немного медленнее.📲 Мы в MAX👉@BookPython
Можно добавлять символы Unicode в строковый литерал не только по их номеру, но и по имени.>>> '\N{EM DASH}''—'>>> '\u2014''—'Это также совместимо с f-строками:>>> width = 800>>> f'Width \N{EM DASH} {width}''Width — 800'📲 Мы в MAX👉@BookPython
🚀 Подборка полезных 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 Программисты, дизайнеры, новости из мира IThttps://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Мир технологий 📌 ht
Если вы хотите, чтобы объекты класса имели автоинкрементируемый 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
Оператор break подавляет исключение, если используется в блоке finally, даже когда блок except отсутствует:for i in range(10): try: 1 / i finally: print('finally') break print('after try')print('after while')Вывод:finallyafter whileТо же самое верно и для continue, однако его нельзя использовать в блоке finally до версии Python 3.8:SyntaxError: 'continue' not supported inside 'finally' clause📲 Мы в MAX👉@BookPython
В asyncio распространённая практика для планирования выполнения кода с задержкой — создать задачу, которая делает await asyncio.sleep(x):import asyncioasync 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 asynciodef 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
Если декоратор, который вы пишете, становится слишком сложным, имеет смысл преобразовать его из функции в класс с методом __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 decoratedsaving_orig = SavingOrigПоследняя строка позволяет одновременно дать классу имя в стиле CamelCase и сохранить имя декоратора в стиле snake_case.📲 Мы в MAX👉@BookPython
Ты можешь использовать любой объект в качестве ключа словаря в Python, если он реализует метод __hash__. Этот метод может возвращать любое целое число при одном важном условии: равные объекты должны иметь одинаковые хэши (обратное не обязательно).Также следует избегать использования изменяемых объектов в качестве ключей, потому что если объект изменится и перестанет быть равным самому себе в прошлом состоянии, его больше нельзя будет найти в словаре.Есть ещё один странный момент, который может удивить при отладке или написании юнит-тестов:class A: def __init__(self, x): self.x = x def __hash__(self): return self.xhash(A(2)) # 2hash(A(1)) # 1hash(A(0)) # 0hash(A(-1)) # -2 (!)hash(A(-2)) # -2В CPython значение -1 зарезервировано для внутренних состояний ошибок, поэтому оно автоматически преобразуется в -2.📲 Мы в MAX👉@BookPython
В Python 3, после выхода из блока except переменные, в которых хранятся перехваченные исключения, удаляются из locals(), даже если они существовали раньше:>>> e = 2>>> try:... 1/0... except Exception as e:... pass... >>> eTraceback (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... >>> errorZeroDivisionError('division by zero',)В Python 2 это правило не действует.📲 Мы в MAX👉@BookPython