A Django website I maintain currently uses Python 2.7 but I know that I'll have to upgrade it to Python 3 in a couple of months. If I'm writing code right now that has to work in Python 2, is there a Pythonic way to write it such that it would also work in Python 3 without any changes if I know what the syntax is going to be in Python 3? Ideally I'd like the code to continue to work even after the upgrade without changing it but it would be easy for me to spot where I've done this in the codebase so that I can change the code when I have time. Here's an example of what I'm talking about:
# Python 2 uses 'iteritems'
def log_dict(**kwargs):
for key, value in kwargs.iteritems():
log.info("{0}: {1}".format(key, value))
# Python 3 uses 'items'
def log_dict(**kwargs):
for key, value in kwargs.items():
log.info("{0}: {1}".format(key, value))
There is official documentation suggesting ways to do this. That documentation has changed over time as the situation has changed, so it's worth going directly to the source (especially if you're reading this answer a year or two after it was written).
It's also worth reading the Conservative Python 3 Porting Guide and skimming Nick Coghlan's Python 3 Q&A, especially this section.
Going back in time from the early 2018:
The current official suggestions are:
pip install coverage
)pip install future
)pip install pylint
)pip install caniusepython3
)pip install tox
)Notice the last suggestion. Guido and another of the core devs have both been heavily involved in leading large teams to port large 2.7 codebases to 3.x, and found mypy to be very helpful (especially in dealing with bytes-vs.-unicode issues). In fact, that's a large part of the reason gradual static typing is now an official part of the language.
You also almost certainly want to use all of the future
statements available in 2.7. This is so obvious that they seem to have forgotten to leave it out of the docs, but, besides making your life easier (e.g., you can write print
function calls), futurize
and modernize
(and six
and sixer
) require it.
The documentation is aimed at people making an irreversible transition to Python 3 in the near future. If you're planning to stick with dual-version code for a long time, you might be better off following the previous recommendations, which largely revolved around using six instead of futurize. Six covers more of the differences between the two languages, and also makes you write code that's explicit about being dual-version instead of being as close to Python 3 as possible while still running in 2.7. But the downside is that you're effectively doing two ports—one from 2.7 to six-based dual-version code, and then, later, from 3.x-only six code to 3.x-only "native" code.
The original recommended answer was to use 2to3, a tool that can automatically convert Python 2 code to Python 3 code, or guide you in doing so. If you want your code to work in both, you need to deliver Python 2 code, then run 2to3
at installation time to port it to Python 3. Which means you need to test your code both ways, and usually modify it so that it still works in 2.7 but also works in 3.x after 2to3, which isn't always easy to work out. This turns out to not be feasible for most non-trivial projects, so it's no longer recommended by the core devs—but it is still built in with Python 2.7 and 3.x, and getting updates.
There are also two variations on 2to3: sixer auto-ports your Python 2.7 code to dual-version code that uses six, and 3to2 lets you write your code for Python 3 and auto-port back to 2.7 at install time. Both of these were popular for a time, but don't seem to be used much anymore; modernize and futurize, respectively, are their main successors.
For your specific question,
kwargs.items()
will work on both, if you don't mind a minor performance cost in 2.7.iteritems
to items
at install time on 3.x.six.iteritems(kwargs)
, which will do iteritems
in 2.7 and items
in 3.x.six.viewitems(kwargs)
, which will do viewitems
in 2.7 (which is identical to what items
does in 3.x, rather than just similar).kwargs.iteritems()
to six.iteritems(kwargs)
.kwargs.items()
and autmatically convert it to viewitems
at install time on 2.x.viewitems
or items
leaves your code still correctly typed.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With