在 Python 3 中将对象转换为迭代器?
Turning an object into an iterator in Python 3?
我正在尝试将库移植到 Python 3。它有一个用于 PDF 流的标记器。 reader class 在这些标记上调用 next()
。这在 Python 2 中有效,但是当我在 Python 3 中 运行 它时,我得到 TypeError: 'PdfTokens' object is not an iterator
.
tokens.py
中有关迭代器的选择:
class PdfTokens(object):
def __init__(self, fdata, startloc=0, strip_comments=True):
self.fdata = fdata
self.iterator = iterator = self._gettoks(startloc)
self.next = next(iterator)
def __iter__(self):
return self.iterator
def _gettoks(self, startloc, cacheobj=_cacheobj,
delimiters=delimiters, findtok=findtok, findparen=findparen,
PdfString=PdfString, PdfObject=PdfObject):
fdata = self.fdata
current = self.current = [(startloc, startloc)]
namehandler = (cacheobj, self.fixname)
cache = {}
while 1:
for match in findtok(fdata, current[0][1]):
current[0] = tokspan = match.span()
token = match.group(1)
firstch = token[0]
if firstch not in delimiters:
token = cacheobj(cache, token, PdfObject)
elif firstch in '/<(%':
if firstch == '/':
# PDF Name
token = namehandler['#' in token](cache, token, PdfObject)
elif firstch == '<':
# << dict delim, or < hex string >
if token[1:2] != '<':
token = cacheobj(cache, token, PdfString)
elif firstch == '(':
ends = None # For broken strings
if fdata[match.end(1)-1] != ')':
nest = 2
m_start, loc = tokspan
for match in findparen(fdata, loc):
loc = match.end(1)
ending = fdata[loc-1] == ')'
nest += 1 - ending * 2
if not nest:
break
if ending and ends is None:
ends = loc, match.end(), nest
token = fdata[m_start:loc]
current[0] = m_start, match.end()
if nest:
(self.error, self.exception)[not ends]('Unterminated literal string')
loc, ends, nest = ends
token = fdata[m_start:loc] + ')' * nest
current[0] = m_start, ends
token = cacheobj(cache, token, PdfString)
elif firstch == '%':
# Comment
if self.strip_comments:
continue
else:
self.exception('Tokenizer logic incorrect -- should never get here')
yield token
if current[0] is not tokspan:
break
else:
if self.strip_comments:
break
raise StopIteration
引发错误的 pdfreader 文件中违规方法的开头:
def findxref(fdata):
''' Find the cross reference section at the end of a file
'''
startloc = fdata.rfind('startxref')
if startloc < 0:
raise PdfParseError('Did not find "startxref" at end of file')
source = PdfTokens(fdata, startloc, False)
tok = next(source)
我的印象是定义自定义迭代器对象只需要一个 .__iter__
方法、一个 .next()
方法并引发 StopIteration 错误。这个 class 拥有所有这些东西,但它仍然会引发 TypeError。
此外,这个库和它的方法在 Python 2.7 中工作并且在 Python 3 环境中停止工作。 Python 3 有何不同?我该怎么做才能使 PdfTokens 对象可迭代?
你不能直接在PdfTokens
的实例上调用next
,你需要先通过调用iter()
来获取它的迭代器。这正是 for 循环所做的*,它首先在对象上调用 iter()
并获得一个迭代器,然后在循环内对该迭代器调用 __next__
直到它没有用完:
instance = PdfTokens(fdata, startloc, False)
source = iter(instance)
tok = next(source)
好吧,并非总是如此,如果在 class 上没有定义 __iter__
,那么迭代器协议将回退到 __getitem__
(如果已定义)。
我正在尝试将库移植到 Python 3。它有一个用于 PDF 流的标记器。 reader class 在这些标记上调用 next()
。这在 Python 2 中有效,但是当我在 Python 3 中 运行 它时,我得到 TypeError: 'PdfTokens' object is not an iterator
.
tokens.py
中有关迭代器的选择:
class PdfTokens(object):
def __init__(self, fdata, startloc=0, strip_comments=True):
self.fdata = fdata
self.iterator = iterator = self._gettoks(startloc)
self.next = next(iterator)
def __iter__(self):
return self.iterator
def _gettoks(self, startloc, cacheobj=_cacheobj,
delimiters=delimiters, findtok=findtok, findparen=findparen,
PdfString=PdfString, PdfObject=PdfObject):
fdata = self.fdata
current = self.current = [(startloc, startloc)]
namehandler = (cacheobj, self.fixname)
cache = {}
while 1:
for match in findtok(fdata, current[0][1]):
current[0] = tokspan = match.span()
token = match.group(1)
firstch = token[0]
if firstch not in delimiters:
token = cacheobj(cache, token, PdfObject)
elif firstch in '/<(%':
if firstch == '/':
# PDF Name
token = namehandler['#' in token](cache, token, PdfObject)
elif firstch == '<':
# << dict delim, or < hex string >
if token[1:2] != '<':
token = cacheobj(cache, token, PdfString)
elif firstch == '(':
ends = None # For broken strings
if fdata[match.end(1)-1] != ')':
nest = 2
m_start, loc = tokspan
for match in findparen(fdata, loc):
loc = match.end(1)
ending = fdata[loc-1] == ')'
nest += 1 - ending * 2
if not nest:
break
if ending and ends is None:
ends = loc, match.end(), nest
token = fdata[m_start:loc]
current[0] = m_start, match.end()
if nest:
(self.error, self.exception)[not ends]('Unterminated literal string')
loc, ends, nest = ends
token = fdata[m_start:loc] + ')' * nest
current[0] = m_start, ends
token = cacheobj(cache, token, PdfString)
elif firstch == '%':
# Comment
if self.strip_comments:
continue
else:
self.exception('Tokenizer logic incorrect -- should never get here')
yield token
if current[0] is not tokspan:
break
else:
if self.strip_comments:
break
raise StopIteration
引发错误的 pdfreader 文件中违规方法的开头:
def findxref(fdata):
''' Find the cross reference section at the end of a file
'''
startloc = fdata.rfind('startxref')
if startloc < 0:
raise PdfParseError('Did not find "startxref" at end of file')
source = PdfTokens(fdata, startloc, False)
tok = next(source)
我的印象是定义自定义迭代器对象只需要一个 .__iter__
方法、一个 .next()
方法并引发 StopIteration 错误。这个 class 拥有所有这些东西,但它仍然会引发 TypeError。
此外,这个库和它的方法在 Python 2.7 中工作并且在 Python 3 环境中停止工作。 Python 3 有何不同?我该怎么做才能使 PdfTokens 对象可迭代?
你不能直接在PdfTokens
的实例上调用next
,你需要先通过调用iter()
来获取它的迭代器。这正是 for 循环所做的*,它首先在对象上调用 iter()
并获得一个迭代器,然后在循环内对该迭代器调用 __next__
直到它没有用完:
instance = PdfTokens(fdata, startloc, False)
source = iter(instance)
tok = next(source)
好吧,并非总是如此,如果在 class 上没有定义 __iter__
,那么迭代器协议将回退到 __getitem__
(如果已定义)。