Python2 And Python3 Differences And Compatibility Tips

Python 3, which has been endorsed as the future of Python, was released in late 2008 and is currently under development. The goal is to address and fix Python 2’s legacy design flaws, clean up code base redundancy, and pursue a single best practice for performing tasks.

At first, the fact that Python 3 is not backward-compatible led to slow adoption by users, unfriendly to beginners. However, thanks to the efforts and determination of the Python community, there were 21903 Packages available to support Python 3.5 now, including most of the most popular Packages libraries. At the same time, a growing number of Packages libraries (e.g. Django, Numpy) indicated that their new versions would no longer support Python 2.

Python 2.7 will be released after 3.0 on July 3, 2010, and is scheduled to be the last version of 2.x. The historical task of Python 2.7 is to make it easier for Python 2.x users to port code to Python 3.x by providing compatibility measures between 2 and 3. So if you want your code to be compatible with two different versions, you need to at least make it work on Python 2.7。

1. Difference And Compatibility.

The __future__ module is the first thing we need to know, and the main purpose of this module is to support importing modules and functions in P2 that only work in P3. Is a very good compatibility tool library, and many examples of compatibility tips given below rely on it.

1.1 _future__ feature list.

Features Optional in the version Built-in in the version The effect
nested_scopes 2.1.0b1 2.2 PEP 227: static nested scopes
generators 2.2.0a1 2.3 PEP 255: a simple generator
division 2.2.0a2 3.0 PEP 238: the division operator was changed
absolute_import 2.5.0a1 3.0 PEP 328: Imports multiple lines with an absolute relative path
with_statement 2.5.0a1 2.6 PEP 343: with statement
print_function 2.6.0a2 3.0 PEP 3105: print statement is upgraded to a function
unicode_literals 2.6.0a2 3.0 PEP 3112: Bytes type

1.2 Unified not equal grammar.

  1. Python 2 supports the use of <> and != to indicate not equal.
  2. Python 3 only supports use != to indicate not equal.
  3. Compatibility skills : All use ! = grammar only.

1.3 Unified integer type.

  1. The integer types in Python 2 can be subdivided into short int and long int.
  2. Python 3 eliminates short integers and use int for long integers only.
  3. Compatibility skills :
    # Python 2 only
    k = 9223372036854775808L
    # Python 2 and 3:
    k = 9223372036854775808
    # Python 2 only
    bigint = 1L
    # Python 2 and 3
    from future.builtins import int
    bigint = int(1)

1.4 Unified integer division.

Python 2’s division/symbol actually has two functions.

  1. When both operands are integer objects, floor division (truncation of fractional parts) is performed to return the integer object.
  2. When two operands have at least one floating-point object, true division (preserving the fractional part) is performed to return the floating-point object.

The division/symbol of Python 3 only has the function of true division, while the function of floor division is left to //

Compatibility skills :

# Python 2 only:
assert 2 / 3 == 0
# Python 2 and 3:
assert 2 // 3 == 0
“True division” (float division):
# Python 3 only:
assert 3 / 2 == 1.5
# Python 2 and 3:
from __future__ import division # (at top of module)

1.5 Unified indentation syntax.

  1. Python 2 can indent using a mixture of TAB and space (1 TAB == 8 Spaces), but not all ides support this feature, so the same code won’t run across the ide.
  2. Python 3 use tabs as indentation uniformly. If tab and space exist at the same time, an exception will be triggered:
    TabError: inconsistent use of tabs and spaces in indentation.
  3. Compatibility skills : Use tabs as indentation.

1.6 Unified class definition.

  1. Python 2 supports both new style (object) and old style classes.
  2. Python 3 use the new style class uniformly, and multiple inheritance can only be applied to new style class.
  3. Compatibility skills : Use new style classes uniformly.

1.7 Unified character encoding types.

  1. Python 2 uses ASCII character encoding by default, but since ASCII supports only a few hundred characters and is not flexible enough for non-english characters, Python 2 also supports Unicode, a more powerful character encoding. However, since Python 2 supports two sets of character encoding at the same time, some identification and conversion problems are unavoidable.
  2. Python 3 use unicode character encoding uniformly, which saves developers time and makes it easy to input and display more kinds of characters in the program.
  3. Compatibility skills : Use the prefix u in all string assignments, or introduce the unicode_literals character module.
    # Python 2 only
    s1 = 'Hello World'
    s2 = u'Hello World'
    
    # Python 2 and 3
    s1 = u'Hello World'
    s2 = u'Hello World'
    
    # Python 2 and 3
    from __future__ import unicode_literals # at top of module
    s1 = 'Hello World'
    s2 = 'Hello World'

1.8 Unified import module path search.

  1. When Python 2 imports a module, it first searches the current directory (cwd) and, if not, the environment variable path (sys. path). This feature often brings troubles to developers, and i believe that everyone has encountered it, especially when the custom module and system module are renamed.
  2. To solve this problem, Python 3 only searches for environment variable paths by default. When you need to search for custom modules, you can add project paths to environment variables in package management mode, and then import them using absolute paths and relative paths (at the beginning).
  3. Compatibility skills : Unified use of absolute path for custom module import.

1.9 Fixed variable scope leaks in list derivation.

  1. Variables in the list of Python 2 can be leaked to global scope, for example:
    import platform
    print('Python', platform.python_version())
    i = 1
    print('before: I = %s' % i)
    print('comprehension: %s' % [i for i in range(5)])
    print('after: I = %s' % i)
    # OUT
    Python 2.7.6
    before: i = 1
    comprehension: [0, 1, 2, 3, 4]
    after: i = 4
  2. Python 3 solves this problem, and the variables in the list are no longer exposed to the global scope.
    import platform
    print('Python', platform.python_version())
    i = 1
    print('before: i =', i)
    print('comprehension:', [i for i in range(5)])
    print('after: i =', i)
    # OUT
    Python 3.4.1
    before: i = 1
    comprehension: [0, 1, 2, 3, 4]
    after: i = 1

1.10 Fixed an illegal comparison operation exception.

  1. Python 2 can compare two objects of different data types.
    import platform
    print('Python', platform.python_version())
    print("[1, 2] > 'foo' = ", [1, 2] > 'foo')
    print("(1, 2) > 'foo' = ", (1, 2) > 'foo')
    print("[1, 2] > (1, 2) = ", [1, 2] > (1, 2))
  2. Below is the execution result.
    Python 2.7.6
    [1, 2] > 'foo' = False
    (1, 2) > 'foo' = True
    [1, 2] > (1, 2) = False
    

However, this seemingly convenient feature is actually a time bomb, because you can’t determine what causes the return value to be False (either data comparison or data type inconsistency).  Python 3 corrects this, and a TypeError exception is triggered if the comparison operand types are inconsistent.

Compatibility skills : Never compare objects with inconsistent data types.

1.11 Unified exception throwing syntax.

  1. Python 2 supports both the old and new exception triggering syntax.
    raise IOError, "file error" # Old
    raise IOError("file error") # New
  2. Python 3 use the new exception trigger syntax uniformly, otherwise SyntaxError exception will be triggered.
    raise IOError("file error")
  3. Compatibility skills :
    ### throw exception
    # Python 2 only:
    raise ValueError, "dodgy value"
    # Python 2 and 3:
    raise ValueError("dodgy value")
    ### use traceback to throw exception
    # Python 2 only:
    traceback = sys.exc_info()[2]
    raise ValueError, "dodgy value", traceback
    # Python 3 only:
    raise ValueError("dodgy value").with_traceback()
    # Python 2 and 3: option 1
    from six import reraise as raise_
    # or # from future.utils import raise_
    traceback = sys.exc_info()[2]
    raise_(ValueError, "dodgy value", traceback)
    # Python 2 and 3: option 2
    from future.utils import raise_with_traceback
    raise_with_traceback(ValueError("dodgy value"))
    ### process exception chain
    # Setup:
    class DatabaseError(Exception):
          pass
    # Python 3 only
    class FileDatabase:
          def __init__(self, filename):
              try:
                  self.file = open(filename)
              except IOError as exc:
                  raise DatabaseError('failed to open') from exc
    # Python 2 and 3:
    from future.utils import raise_from
    class FileDatabase:
          def __init__(self, filename):
              try:
                  self.file = open(filename)
              except IOError as exc:
                  raise_from(DatabaseError('failed to open'), exc)

1.12 Unified exception handling syntax.

  1. Python 2 exception handling implementation can also support two kinds of grammar.
    try:
          let_us_cause_a_NameError
    except NameError, err:
    # except NameError as err:
          print err, '--> our error message'
  2. Exception handling in Python 3 forces the use of the as keyword.
    try:
          let_us_cause_a_NameError
    except NameError as err:
          print err, '--> our error message'
  3. Compatibility skills : Unified use of as keyword exception handling.

1.13 Unified input function.

  1. Python 2 supports raw_input and input two input functions, except that the raw_input function returns only string-type objects, while the input function returns both numeric and string data type objects, and implicitly calls an eval function to return the result of its execution result when the input is an expression. Obviously, using input is a more flexible way to write it.
  2. Therefore, Python 3 uniformly uses the input function for input processing.
  3. Compatibility skills : Unified use of input built-in functions.
    # Python 2 only:
    input("Type something safe please: ")
    # Python 2 and 3
    from future.builtins import input
    eval(input("Type something safe please: "))

1.14 Unified output function.

  1. Print in Python 2 is both a key word and a built-in function. Print’Hello world!’ is a statement and print (‘Hello world!’) is a function call.
  2. The prototype of Python 3 is as follows. This change makes the output processing of Python 3 more concise, powerful and elegant. The complex code in Python 2 can be replaced by the transfer of parameters.
    print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
  3. Compatibility skills :
    ### print single string in single line
    # Python 2 only:
    print 'Hello'
    # Python 2 only:
    print 'Hello'
    ### print multiple string in single line
    # Python 2 only:
    print 'Hello', 'Guido'
    # Python 2 and 3:
    from __future__ import print_function # (at top of module)
    print('Hello', 'Guido')
    ### output redirection
    # Python 2 only:
    print >> sys.stderr, 'Hello'
    # Python 2 and 3:
    from __future__ import print_function
    print('Hello', file=sys.stderr)
    ### print and return a new line
    # Python 2 only:
    print 'Hello',
    # Python 2 and 3:
    from __future__ import print_function
    print('Hello', end='')

1.15 Unified file manipulation functions.

  1. Python 2 supports file manipulation using the file and open functions.
  2. Python 3 uniformly use open for file operation.
  3. Compatibility skills : Use the open function uniformly.
    # Python 2 only:
    f = file(pathname)
    # Python 2 and 3:
    f = open(pathname)

1.16 Unified List Iterator Generation Function.

  1. Python 2 supports the use of range and xrange functions to generate iteratable objects. The difference is that the former returns a list-type object, while the latter returns an iteration object similar to a generator (lazy evaluation), which supports infinite iteration. So when you need to generate a large sequence, it is recommended to use xrange, because it does not allocate all the memory space needed for the sequence at beginning. The xrange function is obviously more efficient if the sequence is read only, but if you want to modify the elements of the sequence or add or delete elements to the sequence, you had better generate a list object by the range function.
  2. Python 3 uniformly uses the range function to generate iterable objects, but in fact, the range of Python 3 is more like the xrange of Python 2. So in Python 3 if you want a list object that can be modified, you need to do like this:
    list(range(1,10))
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
  3. Compatibility skills : Use the range function uniformly.
    # Python 2 only:
    for i in xrange(10**8):
          ...
    # Python 2 and 3: forward-compatible
    from future.builtins import range
    for i in range(10**8):
          ...
    # Python 2 and 3: backward-compatible
    from past.builtins import xrange
    for i in xrange(10**8):
          ...

1.17 Unified iterator iteration function.

  1. Python 2 supports the use of built-in function next and the iterator object’s next() instance method to obtain the next element of the iterator object. Therefore, when implementing a custom iterator object class, the next() method must be implemented:
    # Python 2 only
    class Upper(object):
       def __init__(self, iterable):
          self._iter = iter(iterable)
       def next(self): # Py2-styface iterator interface
          return self._iter.next().upper()
       def __iter__(self):
          return self
    itr = Upper('hello')
    assert itr.next() == 'H' # Py2-style
    assert list(itr) == list('ELLO')
    
  2. However, in Python 3, it is unified to use the next built-in function to get the next element, and if you try to call the object’s next() method, the AttributeError exception is triggered. So, to implement a custom iterator in Python 3 you should implement the special  __next__  method.
  3. Compatibility skills :
    # Python 2 and 3: option 1
    from future.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')
    # Python 2 and 3: option 2
    from future.utils import implements_iterator
    @implements_iterator
    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'
    assert list(itr) == list('ELLO')