Я изучил функцию генераторов, и я думаю, что получил ее, но я хотел бы понять, где я мог применить ее в своем коде.
Я имею в виду следующий пример, я прочитал в "Python существенную ссылочную" книгу:
# tail -f
def tail(f):
f.seek(0,2)
while True:
line = f.readline()
if not line:
time.sleep(0.1)
continue
yield line
У Вас есть какой-либо другой эффективный пример, где генераторы являются лучшим инструментом для задания как хвост-f?
Как часто Вы используете функцию генераторов и в котором виде functionality\part программы Вы обычно применяете ее?
Я часто использую их, когда внедряю сканеры (токенизаторы) или перебираю контейнеры данных.
Изменить: вот демонстрационный токенизатор, который я использовал для программы выделения синтаксиса C ++:
whitespace = ' \t\r\n'
operators = '~!%^&*()-+=[]{};:\'"/?.,<>\\|'
def scan(s):
"returns a token and a state/token id"
words = {0:'', 1:'', 2:''} # normal, operator, whitespace
state = 2 # I pick ws as first state
for c in s:
if c in operators:
if state != 1:
yield (words[state], state)
words[state] = ''
state = 1
words[state] += c
elif c in whitespace:
if state != 2:
yield (words[state], state)
words[state] = ''
state = 2
words[state] += c
else:
if state != 0:
yield (words[state], state)
words[state] = ''
state = 0
words[state] += c
yield (words[state], state)
Пример использования:
>>> it = scan('foo(); i++')
>>> it.next()
('', 2)
>>> it.next()
('foo', 0)
>>> it.next()
('();', 1)
>>> it.next()
(' ', 2)
>>> it.next()
('i', 0)
>>> it.next()
('++', 1)
>>>
В целом, чтобы отделить сбор данных (который может быть сложным) от потребления. В частности:
выдают
-ing записи из каждого из них, потребитель видит только поступающие отдельные элементы данных. Генераторы также могут работать как сопрограммы. Вы можете передать данные в их, используя nextval = g.next (data)
на стороне потребителя и data = yield (nextval)
на стороне генератора. . В этом случае генератор и его потребитель меняют местами значения. Вы даже можете заставить yield
генерировать исключение в контексте генератора: g.throw (exc)
делает это.
Во всех случаях, когда у меня есть алгоритмы, считывающие что-либо, я использую исключительно генераторы.
Почему?
Слои в правилах фильтрации, отображения и сокращения намного проще в контексте нескольких генераторов.
Пример:
def discard_blank( source ):
for line in source:
if len(line) == 0:
continue
yield line
def clean_end( source ):
for line in source:
yield line.rstrip()
def split_fields( source ):
for line in source;
yield line.split()
def convert_pos( tuple_source, position ):
for line in tuple_source:
yield line[:position]+int(line[position])+line[position+1:]
with open('somefile','r') as source:
data= convert_pos( split_fields( discard_blank( clean_end( source ) ) ), 0 )
total= 0
for l in data:
print l
total += l[0]
print total
Я предпочитаю использовать много небольших генераторов, чтобы небольшое изменение не нарушило всю цепочку процесса.
Каждый раз, когда ваш код генерирует неограниченное количество значений или, в более общем смысле, если слишком много памяти будет израсходовано путем создания всего списка вначале.
Или, если есть вероятность, что вы не перебираете весь сгенерированный список (а список очень большой). Я имею в виду, что нет смысла сначала генерировать каждое значение (и ждать генерации), если оно не используется.
Моя последняя встреча с генераторами произошла, когда я реализовал линейную рекуррентную последовательность (LRS), например, последовательность Фибоначчи.