Comments 28
Вопрос по примеру из использования генераторов.
А если у нас большой файл, но нам надо сделать не
file.read().split("\n")
а, например,
file.read().split("<...>")
то есть другой, отличный от \n разделитель?
В данный момент использую решение 10-летней давности с offset и seek(), которое работает но не столь элегентно… Может есть решение?
Написал такое:
def custom_splitter(file, delimiter, remove_delimiters):
counter = 0
segment = ''
del_length = len(delimiter)
while char := file.read(1):
segment += char
counter += 1
if counter % del_length == 0 and segment.endswith(delimiter):
yield segment[:-del_length] if remove_delimiters else segment
segment = ''
counter = 0
with open('text.txt', encoding='utf-8') as file:
for segment in custom_splitter(file, '<...>', True):
print('-'*20)
print(segment)
Хотя возможно, что с точки зрения производительности лучше было бы хранить и обновлять строку из последних символов, количество которых равно длине разделителя, и определять конец сегмента по ней, вместо str.endswith
.
Правда я читал блоками по N байт, каждый блок сплитил по разделителю, а остаток (который возможно неполный) переносил в следующую итерацию…
Тоже вариант, который, вероятно, лучше моего.
Написал тот, о котором упоминал:
def custom_splitter(file, delimiter, remove_delimiters):
end = ''
segment = []
while char := file.read(1):
segment += [char]
end += char
if len(end) >= len(delimiter):
if end == delimiter:
yield ''.join(segment[:-len(delimiter)]) \
if remove_delimiters else ''.join(segment)
segment.clear()
end = end[1:]
yield ''.join(segment)
Но всё-таки это ни разу не методы рефакторинга, тут просто синтаксический сахар и несколько полезностей из стандартной библиотеки.
1. Включение (прим. пер.: абстракции) списков, словарей и множествОбычно на русский язык эти сomprehensions переводят как генераторы. То есть генератор списка, генератор словаря и генератор множества.
>>> print(f'1 USD = {usd_to_eur:.2f} EUR')Вот то что идет после двоеточия — это задание особенностей форматирования. Их несколько существует. Вот тут собрана таблица с примерами: mkaz.blog/code/python-string-format-cookbook
Тоже видел подобный перевод. Но, по-моему, он вносит некоторую путаницу – в Python этот термин уже используется для другой штуки, о которой, кстати, говорится в 10 пункте этой статьи. Т. е., было бы так:
- Генераторы
... - Использование генераторов.
1. ГенераторыГенераторы коллекций.
говорится в 10 пункте этой статьи.ИМХО, то что в десятом пункте вообще не корректно называть генератором, я бы назвал это итератором (хотя это вопрос к автору а не переводчику).
Генератор генерирует новую коллекцию, а итератор идет по существующей. В десятом пункте идет построчный обход существующих в файле строк, то есть итерация.
Похоже, вы правы. В десятом пункте действительно выполняется итерация по итератору строк файла через вызов метода __next__
, если я корректно выразился.
Наткнулся на такую, вроде бы неплохую, статью https://opensource.com/article/18/3/loop-better-deeper-look-iteration-python на эту тему.
Да, генераторы коллекций – путаницы действительно меньше.
С другой стороны, теоретически может быть путаница с вещами наподобие этой:
def what_am_i():
lst = []
for i in range(100):
lst.append(i)
yield lst[:]
Да, вроде неплохая статья. Как-то читал в документации про форматирование – тяжело все запомнить.
squared_evens = [n ** 2 for n in numbers if n % 2 == 0]
Илиsquared_evens = []
for n in numbers:
if n % 2 == 0:
squared_evens.append(n ** 2)
Я бы не сказал, что "здорово снижают". Со временем, если чаще ими пользоваться, то они воспринимаются лучше, чем поначалу. В них же еще можно использовать перенос строки, например, отделять с помощью него ту часть, которая до for.
squared_evens = [n**2
for n in range(10)
if n % 2 == 0]
Ваш пример ничего, но получается практически тот же for.
С другой стороны, я люблю вставлять для отладки печать или запись в лог, опять-таки это делать удобнее в for.
Вообще, я стараюсь писать так, чтобы в одной строке был один «логический кирпичик».
Конечно, если использовать тернарные выражения и более одного цикла / вложенные включения – тогда действительно прощай читабельность.
list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_b = [x**3 if x < 0 else x**2 for x in list_a if x % 2 == 0]
# вначале фильтр пропускает в выражение только четные значения
# после этого ветвление в выражении для отрицательных возводит в куб, а для остальных в квадрат
print(list_b) # [-8, 0, 4, 16]
- Множественное присваивание и распаковка кортежей
>> # Вместо этого: >> code = 404 >> message = "Not Found" >> >> # Характерный для Python способ: >> code, message = 404, "Not Found"
Отличный пример, но читаемость ни разу не выросла. На code review я за такое бью по рукам.
- Тернарное выражение
>> # Вместо этого: >> if got_even_number(): ... state = "Even" ... else: ... state = "Odd" ... >> # Характерный для Python способ: >> state = "Even" if got_even_number() else "Odd"
Тернарный оператор — очень скользкая дорожка. Чаще я сталкиваюсь с таким:
state = "Long description in case of `got_even_number` returns 1" \
if got_even_number() else \
"Another long description used when `got_even_number` returns 0"
В данном примере обычный if
предпочтительнее. А ещё лучше обойтись вообще без if
:
states = (
"Another long description used when `got_even_number` returns 0",
"Long description in case of `got_even_number` returns 1",
)
number = got_even_number()
state = states[number]
Форматирование через f-строки сложно назвать улучшением. Они бывают удобными, но имеют ряд моментов:
- Если забыть f перед строкой — получаем нерабочую строку, которую порою трудно отследить.
- Возможность вставлять различные конструкции внутрь фигурных скобок ухудшает читабельность всей строки. И это плохо, поскольку в рамках проекта желательно все строки строить единым образом, а сложить все рядышком в метод format уже не получится.
Конечно, все это отчасти вкусовщина, но я не вижу потенциальной выгоды f-строк перед format'ом.
Отчасти, отследить строку с забытой перед ней f помогает подсветка фигурных скобок, по крайней мере в Visual Studio Code с расширением Python такое наблюдаю. Конечно, это не так заметно, по сравнению с сообщениями линтера.
В одной статье читал, что str.format якобы плох в том случае, когда в нем используются именованные аргументы, а именно в этой: https://realpython.com/python-f-strings/
очевидные вещи — такие очевидные
10 предпочтительных методов рефакторинга кода на Python