deepak
deepak

Reputation: 2075

In python, why import 'object' from builtins module?

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

Answers (1)

Martijn Pieters
Martijn Pieters

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

Related Questions