In a lot of places python2 will now be using lists where iterators were
used before. This is due to a major shift in python 3 where iterators
are now a bigger focus. In all likelyhood this won't cause any significant
loss in performance unless the iterables contain many thousands of objects
Using the built-in methods which return lists in python 2 and iterators in
python 3 keeps the codebase smaller, more readable, and avoids external
dependencies
Below are explanations for major changes to coding style along with
explanations of the changes in behavior from python 2 to 3 and preferred
styles to use in the future to keep the code compatible with both versions
Keep in mind that python 2 is end-of-life in 2020 and Schrodinger has
switched to only support python 3 since any release 2018 or later, so
using python 3 now is encouraged
Iterators no longer have .next(), use next()
In python 3 the .next() method was removed from iterators
Instead the next() built-in method should be used, i.e. next(iterator)
next() built-in was backported to python 2.6+ so this should be fine
Use absolute imports and explicit relative imports
In python3 absolute imports are checked first before implicit relative imports
In python2 this was the other way around, so if your module had a submodule names math, then import math would import that submodule rather than the module from the standard library
Add from __future__ import absolute_import to bring this behavior to python2
Absolute imports used for core python modules
Submodules imported from q2mm or other python modules should use absolute import syntax or atleast explicit relative syntax
E.g. import q2mm.gradient or import .gradient are ok but not import gradient when importing q2mm's gradient submodule
Use python3 division to result in float
In python3, division of two ints results in a float
In python2, this resulted in an int instead
Use from __future__ import division to bring python3 division behavior to python2
If you desire the old behavior, use // as the division operator to return an int
I've done some preliminary checks and didn't see any obvious places where the int was needed without explicitly being typed
If anything breaks, please keep this change in mind if your code uses any division
Changing instances of map() to list comprehension
In python3 map() now returns an iterator instead of a list
I've exchanged cases where a list was needed with list comprehensions
I tried to leave map() in places where an iterator would still work
In the future use list comprehensions when you need a list, or map
when you need an iterator
Wrapping map in list (i.e. list(map(func, iterable))) is OK but not
very pythonic
Changing izip() to zip()
In python3 zip() now generates an iterator where in python2 izip() from itertools was needed to do this.
Previously I changed these to use zip() if python3 was being used or izip() if python2 was being used
In favor of readability and removing unnecessary dependencies I've changed all of these to just use zip() regardless of python version
This is highly unlikely to cause any significant problems with performance
Changing xrange() to range()
In python3 range() now behave exactly how xrange() did in python2
Most likely this won't affect performance anywhere, so for readability just using range()
Changing any dict.iter() to dict.()
In python3 dict.iteritems(), dict.iterkeys(), etc. has been removed
Similarly dict.viewitems(), etc. has also been removed
Instead dict.items(), etc. now returns a view object
Views are memory efficient like iterators but are dynamic in that
changing the dictionary changes what is in the view
For the most part, when iterating over the keys or values in a dict
it will work the same as iterating over lists unless the dict object
is changed
Leaving version switch to deal with basestring
Basestring has been removed from python3 as all text-type strings
are now unicode, which was not the case in python2
Most solutions to this require dependence on six or another python
module for dealing with python2/3 differences in a clean way
Since most of these compatabilities have been handled without using
an external dependence then to avoid adding that dependence I'm just
going to leave the if statements which switch behavior based on
python version
If you like this documentation it may be good to add it to the wiki for this project under "Development" to reference for future coding style, at least until python2 is effectively no longer used by anyone for Q2MM.
Iterators no longer have .next(), use next()
Use absolute imports and explicit relative imports
import math
would import that submodule rather than the module from the standard libraryfrom __future__ import absolute_import
to bring this behavior to python2import q2mm.gradient
orimport .gradient
are ok but notimport gradient
when importing q2mm's gradient submoduleUse python3 division to result in float
from __future__ import division
to bring python3 division behavior to python2//
as the division operator to return an intChanging instances of map() to list comprehension
Changing izip() to zip()
Changing xrange() to range()
Changing any dict.iter() to dict.()
Leaving version switch to deal with basestring
six
or another python module for dealing with python2/3 differences in a clean way