Если вам нужно выполнить поиск в отсортированной коллекции, то бинарный поиск — это именно то, что вам нужно. Этот простой алгоритм сравнивает искомое значение с элементом в середине массива; результат определяет, какую половину нужно искать дальше.
Стандартная библиотека Python предоставляет возможность использовать бинарный поиск без его непосредственной реализации. Функция
Результаты демонстрируют, что использование бинарного поиска через
📲 Мы в MAX
👉@BookPython
Стандартная библиотека Python предоставляет возможность использовать бинарный поиск без его непосредственной реализации. Функция
bisect_left возвращает самую левую позицию элемента в отсортированном списке, а bisect_right — самую правую.
from random import randrange
from bisect import bisect_left
n = 1000000
look_for = 555555
lst = sorted(randrange(0, n) for _ in range(n))
%timeit look_for in lst
# 69.7 ms ± 449 µs на цикл
%timeit look_for == lst[bisect_left(lst, look_for)]
# 927 ns ± 2.28 ns на цикл
Результаты демонстрируют, что использование бинарного поиска через
bisect_left быстрее, чем стандартный поиск в списке с помощью оператора in.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
📌 О представлении данных в байтах в Python
Когда мы храним данные в памяти или на устройстве хранения, их необходимо представить в виде байтов. Python позволяет работать с абстракцией данных, не задумываясь об их байтовом представлении. Однако при записи строки в файл мы фактически работаем с физической структурой данных.
Чтобы записать символы в файл, их нужно преобразовать в байты — это называется кодированием (encoding). Когда вы читаете байты из файла и хотите преобразовать их в понятные символы, этот процесс называется декодированием (decoding).
🔤 Кодировки и их применение
Существует множество методов кодирования. Один из самых популярных — Unicode, но сам по себе Unicode не является кодировкой в традиционном смысле. Unicode определяет соответствие между символами и их числовыми кодами. Например, 🐍 имеет код 128 013.
Однако, чтобы записать числа в файл, нужна настоящая кодировка. Unicode обычно используется с utf-8, которая (в большинстве случаев) является кодировкой по умолчанию в Python. При чтении из файла Python автоматически декодирует данные, используя utf-8.
Если вы хотите использовать другую кодировку, просто укажите её с помощью параметра
Пример:
📲 Мы в MAX
👉@BookPython
Когда мы храним данные в памяти или на устройстве хранения, их необходимо представить в виде байтов. Python позволяет работать с абстракцией данных, не задумываясь об их байтовом представлении. Однако при записи строки в файл мы фактически работаем с физической структурой данных.
Чтобы записать символы в файл, их нужно преобразовать в байты — это называется кодированием (encoding). Когда вы читаете байты из файла и хотите преобразовать их в понятные символы, этот процесс называется декодированием (decoding).
🔤 Кодировки и их применение
Существует множество методов кодирования. Один из самых популярных — Unicode, но сам по себе Unicode не является кодировкой в традиционном смысле. Unicode определяет соответствие между символами и их числовыми кодами. Например, 🐍 имеет код 128 013.
Однако, чтобы записать числа в файл, нужна настоящая кодировка. Unicode обычно используется с utf-8, которая (в большинстве случаев) является кодировкой по умолчанию в Python. При чтении из файла Python автоматически декодирует данные, используя utf-8.
Если вы хотите использовать другую кодировку, просто укажите её с помощью параметра
encoding= в функции open. А чтобы работать с "чистыми" байтами, добавьте символ b к режиму открытия файла.Пример:
# Кодирование строки в файл
with open('example.txt', 'w', encoding='utf-8') as f:
f.write('Привет, мир!')
# Чтение в байтовом режиме
with open('example.txt', 'rb') as f:
data = f.read()
print(data) # Вывод: b'\xd0\x9f\xd1\x80\xd0\xb8...'
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
🔑 Использование объектов в качестве ключей словаря в Python
В Python вы можете использовать любой объект в качестве ключа словаря, если он реализует метод
👉 Не используйте изменяемые объекты в качестве ключей! Если объект изменяется после добавления в словарь, он становится "невидимым" для поиска, так как его хэш может измениться.
🌀 Странность с отрицательными хэшами
Есть интересная особенность, которая может вас удивить при отладке или написании юнит-тестов. Рассмотрим следующий пример:
Результаты хэширования экземпляров класса:
💡 В CPython значение
📲 Мы в MAX
👉@BookPython
В 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 зарезервировано для внутренних ошибок. Если хэш-значение равно -1, интерпретатор автоматически преобразует его в -2. Это может вызывать неожиданные проблемы при сравнении или использовании объектов в качестве ключей.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1🔥1
Иногда нужно создать функцию на основе более универсальной. Например, у функции
Для более точной и семантически понятной реализации можно использовать
Это удобно, когда нужно передать функцию в качестве аргумента в другую функцию высшего порядка, но с заблокированными значениями некоторых аргументов:
Без использования
📲 Мы в MAX
👉@BookPython
int() есть параметр base, который можно зафиксировать, чтобы получить новую функцию base2:
>>> int("10")
10
>>> int("10", 2)
2
>>> def base2(x):
... return int(x, 2)
...
>>> base2("10")
2
Для более точной и семантически понятной реализации можно использовать
functools.partial:
from functools import partial
base2 = partial(int, base=2)
Это удобно, когда нужно передать функцию в качестве аргумента в другую функцию высшего порядка, но с заблокированными значениями некоторых аргументов:
>>> list(map(partial(int, base=2), ["1", "10", "100"]))
[1, 2, 4]
Без использования
partial пришлось бы писать код так:
>>> list(map(lambda x: int(x, base=2), ["1", "10", "100"]))
[1, 2, 4]
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Метод
Имя поля используется для указания, какой именно аргумент должен быть подставлен:
Преобразование позволяет указать, что вместо
Спецификация формата задаёт, как значения будут представлены:
Эта спецификация может быть применена и к отдельному объекту с помощью функции
Функция
📲 Мы в MAX
👉@BookPython
format в Python для строк — мощный инструмент, поддерживающий множество возможностей, о которых вы, возможно, даже не знали. Каждый заменяемый плейсхолдер ({...}) может содержать три части: имя поля, преобразование и спецификацию формата.Имя поля используется для указания, какой именно аргумент должен быть подставлен:
>>> '{}'.format(42)
'42'
>>> '{1}'.format(1, 2)
'2'
>>> '{y}'.format(x=1, y=2)
'2'
Преобразование позволяет указать, что вместо
str() следует использовать repr() (или ascii()) при преобразовании объектов в строки:
>>> '{!r}'.format(datetime.now())
'datetime.datetime(2018, 5, 3, 23, 48, 49, 157037)'
>>> '{}'.format(datetime.now())
'2018-05-03 23:49:01.060852'
Спецификация формата задаёт, как значения будут представлены:
>>> '{:+,}'.format(1234567)
'+1,234,567'
>>> '{:>19}'.format(1234567)
' 1234567'
Эта спецификация может быть применена и к отдельному объекту с помощью функции
format (не метода str):
>>> format(5000000, '+,')
'+5,000,000'
Функция
format вызывает метод __format__ объекта, поэтому вы можете изменить его поведение для своих типов.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1💩1
Функция
Иногда нужно проверить, пуст ли генератор (точнее, исчерпан ли он). Для этого можно попытаться получить следующий элемент с помощью
Пример использования:
📲 Мы в MAX
👉@BookPython
itertools.chain позволяет объединить несколько итерируемых объектов, чтобы работать с ними, как с единым целым:
from itertools import chain
print(list(chain(['a', 'b'], range(3), set('xyz'))))
# Вывод: ['a', 'b', 0, 1, 2, 'x', 'z', 'y']
Иногда нужно проверить, пуст ли генератор (точнее, исчерпан ли он). Для этого можно попытаться получить следующий элемент с помощью
next(). Если элемент есть, его нужно вернуть обратно в генератор, но сделать это напрямую невозможно. Однако можно «приклеить» его обратно с помощью chain:
from itertools import chain
def sum_of_odd(gen):
try:
first = next(gen) # Пытаемся получить первый элемент
except StopIteration:
raise ValueError('Empty generator') # Если генератор пуст, выбрасываем исключение
# Используем chain для возврата первого элемента и объединения с остальными
return sum(
x for x in chain([first], gen)
if x % 2 == 1 # Суммируем только нечетные числа
)
Пример использования:
print(sum_of_odd(x for x in range(1, 6))) # Вывод: 9 (1 + 3 + 5)
print(sum_of_odd(x for x in range(2, 3))) # Вывод: 0 (нет нечетных чисел)
print(sum_of_odd(x for x in range(2, 2))) # ValueError: Empty generator
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
В Python блок
Наиболее распространённый случай использования этого — поиск элемента в цикле с прерыванием через
Если в списке нет подходящего элемента, цикл завершается естественным образом, и выполняется блок
📲 Мы в MAX
👉@BookPython
else можно использовать не только после if, но и после циклов for и while. Код внутри else выполняется только в том случае, если цикл завершился естественным образом, то есть не был прерван с помощью break.Наиболее распространённый случай использования этого — поиск элемента в цикле с прерыванием через
break, если элемент найден:
# Пример 1: Список содержит нечётное число
first_odd = None
for x in [2, 3, 4, 5]:
if x % 2 == 1: # Проверяем, является ли число нечётным
first_odd = x
break # Прерываем цикл, так как элемент найден
else:
raise ValueError('No odd elements in list') # Выполнится, если цикл завершился без break
print(first_odd) # Результат: 3
Если в списке нет подходящего элемента, цикл завершается естественным образом, и выполняется блок
else:
# Пример 2: Список не содержит нечётных чисел
for x in [2, 4, 6]:
if x % 2 == 1:
first_odd = x
break
else:
raise ValueError('No odd elements in list') # Исключение будет поднято
# ValueError: No odd elements in list
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
27 ноября собираемся на Pytup: митап Яндекса для Python-разработчиков и ML-инженеров 🚀
Присоединяйтесь в Екатеринбурге или онлайн, чтобы в неформальной обстановке поговорить о Python, машинном обучении и технологиях, которые двигают индустрию вперед.
В программе выступлений:
> Арсений Саблин, разработчик системы контроля качества умных устройств на производстве (Яндекс Алиса), поделится, как используется Python при тестировании станции с Алисой;
> Никита Улько, техлид VK Tech, разберет чистую архитектуру с практической точки зрения: за что ее ценят и как гибко применять ее принципы, фокусируясь на решении конкретных проблем;
> Егор Гордовский, технический менеджер проектов Yandex Cloud, расскажет о сложном техническом организме, помогающем превратить код в работающий сервис — дата-центре.
Помимо докладов участников в Екатеринбурге ждет дискуссия Snake Pit, а также соревнования по классической «Змейке» и гонки на игрушечных роботах-доставщиках.
📅 27 ноября в 17.00 (по Екб)
📍 Екатеринбург (креативный кластер «Домна») + онлайн
Регистрация на митап
Присоединяйтесь в Екатеринбурге или онлайн, чтобы в неформальной обстановке поговорить о Python, машинном обучении и технологиях, которые двигают индустрию вперед.
В программе выступлений:
> Арсений Саблин, разработчик системы контроля качества умных устройств на производстве (Яндекс Алиса), поделится, как используется Python при тестировании станции с Алисой;
> Никита Улько, техлид VK Tech, разберет чистую архитектуру с практической точки зрения: за что ее ценят и как гибко применять ее принципы, фокусируясь на решении конкретных проблем;
> Егор Гордовский, технический менеджер проектов Yandex Cloud, расскажет о сложном техническом организме, помогающем превратить код в работающий сервис — дата-центре.
Помимо докладов участников в Екатеринбурге ждет дискуссия Snake Pit, а также соревнования по классической «Змейке» и гонки на игрушечных роботах-доставщиках.
📅 27 ноября в 17.00 (по Екб)
📍 Екатеринбург (креативный кластер «Домна») + онлайн
Регистрация на митап
❤1👍1🔥1
Когда вы создаете кастомный метод
Пример простого кода:
Проблема возникает, если вы вызываете
Для решения этой проблемы можно использовать декоратор
Теперь код работает корректно:
📲 Мы в MAX
👉@BookPython
__repr__ для объекта, обычно нужно включить представление его атрибутов. Однако важно помнить, что нужно явно вызывать repr(), так как форматирование вызывает str() вместо repr().Пример простого кода:
class Pair:
def __init__(self, left, right):
self.left = left
self.right = right
def __repr__(self):
class_name = type(self).__name__
repr_left = repr(self.left)
repr_right = repr(self.right)
return f'{class_name}({repr_left}, {repr_right})'
Проблема возникает, если вы вызываете
repr для объекта, который содержит ссылку на самого себя. Это может привести к рекурсии:
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__
repr_left = repr(self.left)
repr_right = repr(self.right)
return f'{class_name}({repr_left}, {repr_right})'
Теперь код работает корректно:
In : p = Pair(1, 2)
In : p.right = p
In : p
Out: Pair(1, ...)
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
Тесты могут требовать временные файлы или директории. Для этого отлично подойдет модуль
Так как временные файлы обычно нужно удалять после использования,
📲 Мы в MAX
👉@BookPython
tempfile.Так как временные файлы обычно нужно удалять после использования,
tempfile предоставляет как контекстный менеджер, так и простые функции:
import os
import tempfile
with tempfile.TemporaryDirectory() as dir_path:
open(os.path.join(dir_path, 'a'), 'w').close()
open(os.path.join(dir_path, 'b'), 'w').close()
open(os.path.join(dir_path, 'c'), 'w').close()
assert files_of(dir_path) == ['a', 'b', 'c']
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Нативные значения
Это означает, что в большинстве случаев вы работаете с приближениями, а не с точными значениями:
Модуль
Однако и этого может быть недостаточно:
Для точных вычислений можно использовать
Очевидным ограничением остается то, что иррациональные числа (например, π) все равно будут представлены только в приближенной форме.
📲 Мы в MAX
👉@BookPython
float в Python используют аппаратные возможности вашего компьютера, поэтому любое значение внутренне представлено в виде двоичной дроби.Это означает, что в большинстве случаев вы работаете с приближениями, а не с точными значениями:
In : format(0.1, '.17f')
Out: '0.10000000000000001'
Модуль
decimal позволяет использовать десятичную арифметику с произвольной точностью:
In : Decimal(1) / Decimal(3)
Out: Decimal('0.3333333333333333333333333333')
Однако и этого может быть недостаточно:
In [61]: Decimal(1) / Decimal(3) * Decimal(3) == Decimal(1)
Out[61]: False
Для точных вычислений можно использовать
fractions, где любое число хранится в виде рационального:
In : Fraction(1) / Fraction(3) * Fraction(3) == Fraction(1)
Out: True
Очевидным ограничением остается то, что иррациональные числа (например, π) все равно будут представлены только в приближенной форме.
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2💩2
UTF-8 - это кодировка с переменной длиной. Один символ может быть закодирован с использованием одного, двух, трёх или четырёх байтов. Это означает, что нельзя начать чтение строки в кодировке UTF-8 с произвольного байта, так как это может случайно разрушить символ:
Также это означает, что для пропуска первых
Однако можно пропустить фиксированное количество байтов, принимая во внимание некоторые особенности. Вот как может быть закодирован символ в UTF-8:
Как видно, байт является начальным байтом символа, если его вид не совпадает с
Пример использования:
📲 Мы в MAX
👉@BookPython
In : lion = 'Löwe'
In : lion.encode('utf-8')[2:]
Out: b'\xb6we'
In : lion.encode('utf-8')[2:].decode('utf-8')
...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 0: invalid start byte
Также это означает, что для пропуска первых
N символов строки их необходимо прочитать и декодировать. Рассчитать смещение заранее невозможно.Однако можно пропустить фиксированное количество байтов, принимая во внимание некоторые особенности. Вот как может быть закодирован символ в UTF-8:
0xxxxxxx
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
Как видно, байт является начальным байтом символа, если его вид не совпадает с
10xxxxxx. Такие байты называются продолжением символа (continuation bytes). Давайте пропустим их:
def cut_bytes(s, n):
result = s.encode('utf-8')[n:]
mask = int('11000000', 2)
conbyte = int('10000000', 2)
while result[0] and result[0] & mask == conbyte:
result = result[1:]
return result.decode('utf-8')
Пример использования:
In : cut_bytes(lion, 2)
Out: 'we'
In : cut_bytes(lion, 1)
Out: 'öwe'
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Иногда вам нужно запустить блок кода с несколькими контекстными менеджерами. Например:
Начиная с Python 2.7 и 3.1, это можно сделать с помощью одного выражения
До этого можно было использовать функцию
Однако в современных версиях Python эта функция устарела и вызывает предупреждение. Вместо неё рекомендуется использовать более продвинутый инструмент —
Это особенно полезно, когда количество контекстных менеджеров неизвестно заранее.
📲 Мы в MAX
👉@BookPython
with open('f') as f:
with open('g') as g:
with open('h') as h:
pass
Начиная с Python 2.7 и 3.1, это можно сделать с помощью одного выражения
with:
o = open
with o('f') as f, o('g') as g, o('h') as h:
pass
До этого можно было использовать функцию
contextlib.nested:
with nested(o('f'), o('g'), o('h')) as (f, g, h):
pass
Однако в современных версиях Python эта функция устарела и вызывает предупреждение. Вместо неё рекомендуется использовать более продвинутый инструмент —
contextlib.ExitStack. Он позволяет войти в любое количество контекстов в произвольное время, но гарантирует их корректное завершение:
from contextlib import ExitStack
with ExitStack() as stack:
f = stack.enter_context(o('f'))
g = stack.enter_context(o('g'))
other = [
stack.enter_context(o(filename))
for filename in filenames
]
Это особенно полезно, когда количество контекстных менеджеров неизвестно заранее.
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
Когда корутина asyncio хочет остановиться и взаимодействовать с циклом событий (event loop), она использует
Когда корутина ожидает (
Какое значение возвращает
Почему возникает эта ошибка? Как asyncio понимает, что это вы используете
📲 Мы в MAX
👉@BookPython
await obj (или yield from obj до Python 3.6). Объект obj должен быть другой корутиной, объектом asyncio.Future или любым пользовательским объектом, похожим на Future (любой объект, у которого определен метод __await__).
async def coroutine():
await another_coroutine()
async def another_coroutine():
future = asyncio.Future()
await future
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine())
Когда корутина ожидает (
await) другую корутину, вторая начинает выполняться вместо первой. Если она ожидает третью, то выполняется третья. Это продолжается до тех пор, пока какая-нибудь корутина не ожидает объект Future. Объект Future фактически возвращает значение, и тогда цикл событий (event loop) получает управление.Какое значение возвращает
Future? Оно возвращает сам себя. Можете ли вы напрямую использовать yield для Future? Нет, это внутренняя деталь, о которой вам обычно не нужно беспокоиться.
class Awaitable:
def __await__(self):
future = asyncio.Future()
yield future
# RuntimeError: yield was used
# instead of yield from in task
async def coroutine():
await Awaitable()
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine())
Почему возникает эта ошибка? Как asyncio понимает, что это вы используете
yield для Future, а не сам Future? Есть простая защита: Future устанавливает внутренний флаг перед тем, как вернуть управление.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Please open Telegram to view this post
VIEW IN TELEGRAM