В python следует использовать внутри-генераторы внутри генератора? Чтобы быть ясным, я не прошу об использовании декоратора для создания диспетчера контекста из функции генератора. Я спрашиваю, есть ли неотъемлемая проблема, используя оператор-оператор в качестве менеджера контекста внутри генератора, поскольку он будет улавливать StopIteration
и GeneratorExit
исключения, по крайней мере, в некоторых случаях. Ниже приводятся два примера.Как использовать диспетчер контекста python внутри генератора
Хороший пример проблемы поднят примером Безли (стр. 106). Я изменил его, чтобы использовать оператор with, чтобы файлы были явно закрыты после выхода в методе openener. Я также добавил два способа исключения исключения при повторении результатов.
import os
import fnmatch
def find_files(topdir, pattern):
for path, dirname, filelist in os.walk(topdir):
for name in filelist:
if fnmatch.fnmatch(name, pattern):
yield os.path.join(path,name)
def opener(filenames):
f = None
for name in filenames:
print "F before open: '%s'" % f
#f = open(name,'r')
with open(name,'r') as f:
print "Fname: %s, F#: %d" % (name, f.fileno())
yield f
print "F after yield: '%s'" % f
def cat(filelist):
for i,f in enumerate(filelist):
if i ==20:
# Cause and exception
f.write('foobar')
for line in f:
yield line
def grep(pattern,lines):
for line in lines:
if pattern in line:
yield line
pylogs = find_files("/var/log","*.log*")
files = opener(pylogs)
lines = cat(files)
pylines = grep("python", lines)
i = 0
for line in pylines:
i +=1
if i == 10:
raise RuntimeError("You're hosed!")
print 'Counted %d lines\n' % i
В этом примере менеджер контекста успешно закрывает файлы в функции открывания. Когда возникает исключение, я вижу след обратно из исключения, но генератор останавливается молча. Если оператор with-catch обнаруживает исключение, почему генератор не работает?
Когда я определяю свои собственные контекстные менеджеры для использования внутри генератора. Я получаю ошибки во время выполнения, говоря, что я проигнорировал GeneratorExit
. Например:
class CManager(object):
def __enter__(self):
print " __enter__"
return self
def __exit__(self, exctype, value, tb):
print " __exit__; excptype: '%s'; value: '%s'" % (exctype, value)
return True
def foo(n):
for i in xrange(n):
with CManager() as cman:
cman.val = i
yield cman
# Case1
for item in foo(10):
print 'Pass - val: %d' % item.val
# Case2
for item in foo(10):
print 'Fail - val: %d' % item.val
item.not_an_attribute
Эта маленькая демонстрационная отлично работает в case1 без каких-либо исключений, поднятым, но терпит неудачу в котором Вариант 2 возникает ошибка атрибута. Здесь я вижу RuntimeException
, потому что оператор with поймал и проигнорировал исключение GeneratorExit
.
Может кто-то помочь в разъяснении правил для этого сложного варианта использования? Я подозреваю, что это то, что я делаю, или не делаю в моем методе __exit__
. Я попытался добавить код для повторного рейза GeneratorExit
, но это не помогло.
Это удивительный ответ! – lukecampbell
@mgilson Спасибо за отличный ответ. Кажется, что функция генератора не является тем, что улавливает ошибку атрибута. Это то поведение, которое я хотел, но, похоже, это невозможно. Я хочу использовать один оператор with для упорядочения обработки исключений в последовательности генераторов. – David
@David - контекстные менеджеры - это то, над чем я работал с приличным количеством в последнее время, поэтому они свежие на моем уме :). Это был хороший вопрос. Я хочу, чтобы все новые пользователи задавали такие приятные первые вопросы :). – mgilson