12
\$\begingroup\$

A common idiom that I use for Python2-Python3 compatibility is:

try:
    from itertools import izip
except ImportError:  #python3.x
    izip = zip

However, a comment on one of my Stack Overflow answers implies that there may be a better way. Is there a more clean way to accomplish this?

\$\endgroup\$
  • \$\begingroup\$ Perhaps the commenter meant to use the import to shadow zip? I.e., try: from itertools import izip as zip; except ImportError: pass. (Please excuse the lack of newlines.) \$\endgroup\$ – jpmc26 May 16 '13 at 23:10
  • \$\begingroup\$ Perhaps -- (I knew about that one). I was just wondering if there was some magic with __import__ that I didn't know about or something. \$\endgroup\$ – mgilson May 16 '13 at 23:14
13
\$\begingroup\$

Not sure this is really an answer, or I should elaborate on my comment, and in hindsight probably not even a very good comment anyway, but:

Firstly, you can just simplify it to:

try:
    from itertools import izip as zip
except ImportError: # will be 3.x series
    pass

What I was thinking about was:

From 2.6 you can use as per the docs:

from future_builtins import map # or zip or filter

You do however then have the same problem of ImportError - so:

try:
    from future_builtins import zip
except ImportError: # not 2.6+ or is 3.x
    try:
        from itertools import izip as zip # < 2.5 or 3.x
    except ImportError:
        pass

The advantage of using future_builtin is that it's in effect a bit more "explicit" as to intended behaviour of the module, supported by the language syntax, and possibly recognised by tools. For instance, I'm not 100% sure, but believe that the 2to3 tool will re-write zip correctly as list(zip(... in this case, while a plain zip = izip may not be... But that's something that needs looking in to.

Updated - also in the docs:

The 2to3 tool that ports Python 2 code to Python 3 will recognize this usage and leave the new builtins alone.

\$\endgroup\$
  • 1
    \$\begingroup\$ Ahh ... I didn't actually know about future_builtins. It seems a little silly that they don't have that in python3 though. I don't know why they didn't do something like from __future__ import zip instead. That would have made sense to me. Anyway, thanks for the explanation. \$\endgroup\$ – mgilson May 17 '13 at 1:16
  • \$\begingroup\$ I also think it's silly that 2to3 recognizes the usage of future_builtins yet leaves the from future_builtins import whatever statement(s) in the output. \$\endgroup\$ – martineau Jun 25 '13 at 19:19
3
\$\begingroup\$

If you are trying to make Python 2.x code compatible with Python 3.x you should look at six:

http://pythonhosted.org/six/#module-six.moves

\$\endgroup\$
  • \$\begingroup\$ Yeah, six is nice. Generally, if it's simple enough though, I tend to prefer to avoid the extra dependency. \$\endgroup\$ – mgilson May 20 '13 at 23:50
3
\$\begingroup\$

This is roughly twice as fast as using a try/except:

import itertools
zip = getattr(itertools, 'izip', zip)
\$\endgroup\$
  • \$\begingroup\$ I find this claim hard to believe ... getattr works by catching the AttributeError and substituting the default value. Admittedly, that's implemented in C so it might be slightly faster, but I find it hard to believe that there's a factor of 2. I also find it hard to believe that the time would matter in the real world if you have this at the top-level of your module... Finally, this is possibly the cleanest way to write it that I've seen. Kudos for that :-). \$\endgroup\$ – mgilson Jun 21 '17 at 3:28
  • \$\begingroup\$ @mgilson: Use timeit and test it yourself. I thought it might be faster, but I didn't think ~2x. \$\endgroup\$ – Mike McKerns Jun 22 '17 at 11:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.