Reputation: 2075
In order to transition to python 3, I am trying to understand writing python 2 and python 3 compatible codes. The following code is from python-future.org
and illustrates a way to construct an iterator that would be compatible with both versions of python.
from builtins import object
class Upper(object):
def __init__(self, iterable):
self._iter = iter(iterable)
def __next__(self): # Py3-style iterator interface
return next(self._iter).upper() # builtin next() function calls
def __iter__(self):
return self
itr = Upper('hello')
assert next(itr) == 'H' # compatible style
assert list(itr) == list('ELLO')
The code runs fine in python 2, but to my surprise, if i remove the import statement then i get an error TypeError: Upper object is not an iterator
. I often derive my custom classes from object
but i have never imported it from builtins. Why does simply importing object
change the behaviour of the code?
Upvotes: 22
Views: 13522
Reputation: 1122172
You are (indirectly) importing from the future.builtins
module; it provides a custom object
baseclass that adds forward-pointing special names.
In Python 2, iterators must have a next()
method (as well as __iter__
); this method was renamed to __next__
in Python 3. By not using the future.builtins.object
version you are simply missing the next
-> __next__
alias provided in Python 2.
See the source code for future.types.newobject.py
:
def next(self): if hasattr(self, '__next__'): return type(self).__next__(self) raise TypeError('newobject is not an iterator')
Note that builtins
will return standard built-in objects if you are running Python 3, the module only returns shims like these for Python 2.
You could simply add that same alias yourself:
class Upper(object):
def __init__(self, iterable):
self._iter = iter(iterable)
def __iter__(self):
return self
def __next__(self): # Py3-style iterator interface
return next(self._iter).upper() # builtin next() function calls
next = __next__ # Py2 alias
Upvotes: 30