Tricks to Transition Your Python 2.7 Code to 3.7 or Higher

Hello! Are you interested in transitioning your Python 2.7 code to Python 3.7 or higher? Well, you’ve come to the right place. Throughout this article, I’m going to show you different tricks that will make this seemingly daunting task easier for you. Don’t worry, we’ll go step-by-step. And most importantly, you’ll learn to do it in a simple and efficient way. Let’s dive in!

Understanding the Differences between Python 2 and Python 3

Before we get fully into the tricks to transition from Python 2 to Python 3, it’s essential that you understand the key differences between these two versions. Python 3 introduces changes in the language that are not backward compatible. This is the main reason why the migration process can be tricky.

For example, in Python 2, the print command is used without parentheses, while in Python 3, parentheses are required. In Python 2.7 you could use:

print "Hello, world!"

In Python 3.7 or higher, you should write it like this:

print("Hello, world!")

Using Python 3 Specific Libraries

Python 3 brings with it a series of new libraries that are not available in Python 2.7. Therefore, you will need to change your imports to match the Python 3 libraries. For instance, if you used urlib2 before, now you will have to import urllib.request, urllib.parse, and urllib.error.

Here’s an example of how your code should look:

# Python 2.7
import urllib2
response = urllib2.urlopen('http://python.org/')
html = response.read()

# Python 3.7
import urllib.request
response = urllib.request.urlopen('http://python.org/')
html = response.read()

Change in Exception Syntax

One of the most noticeable changes between Python 2 and Python 3 is how exceptions are handled. In Python 2, the syntax except Exception, e: was used. But in Python 3, you should change it to except Exception as e:. Here’s an example of how to do it:

# Python 2.7
try:
    ...
except Exception, e:
    print e

# Python 3.7
try:
    ...
except Exception as e:
    print(e)

The map() Function

The map() function in Python 2 returns a list, while in Python 3 it returns an iterable object. So, if you want to get a list as a result, you must explicitly convert the result to a list using the list() function. Here’s an example:

# Python 2.7
result = map(func, values)

# Python 3.7
result = list(map(func, values))

Dealing with Division in Python

In Python 2, the division of two integers results in another integer. In Python 3, however, the result is a floating-point number. If you want to keep the Python 2 behavior, you can use the // operator for integer division. Check out this example:

# Python 2.7
result = 7 / 2  # This results in 3

# Python 3.7
result = 7 / 2  # This results in 3.5
result = 7 // 2  # This results in 3

The Importance of __future__

The futurelibrary can be your best friend during migration. It allows you to use Python 3 features in your Python 2.7 code, making your migration task a whole lot easier. For example, you can use the Python 3print()` function in your Python 2.7 code like this:

from __future__ import print_function
print("Hello, world!")

List Comprehensions and Variable Scope

Another important change in Python 3 is how it handles variable scope in list comprehensions. In Python 2, the variables used in list comprehensions leak into the main scope. Python 3 fixed this issue by encapsulating the variable scope within the list comprehension. Here’s an example:

# Python 2.7
x = 1
print [x for x in range(5)]
print x  # This prints 4, not 1

# Python 3.7
x = 1
print([x for x in range(5)])
print(x)  # This prints 1

Dictionary Keys are Views in Python 3

In Python 3, dictionary.keys(), dictionary.values(), and dictionary.items() return view objects rather than lists. If you need to get a list, you need to convert the result to a list explicitly. Check out the following example:

# Python 2.7
dictionary = {'one': 1, 'two': 2}
keys = dictionary.keys()  # This is a list

# Python 3.7
dictionary = {'one': 1, 'two': 2}
keys = list(dictionary.keys())  # Now this is a list

Goodbye to xrange()

In Python 3, the xrange() function has been replaced by range(), which now returns an iterable object. In Python 2.7, if you want to get a list from range(), you need to convert it explicitly to a list:

# Python 2.7
x = xrange(10)  # This is an iterable

# Python 3.7
x = list(range(10))  # This is a list

Escape Codes in Strings

Python 2 allows the use of escape codes in non-formatted strings, while Python 3 does not allow it. If you want to use escape codes in Python 3, you need to use formatted strings. Here’s how to do it:

# Python 2.7
x = '\100'

# Python 3.7
x = r'\100'

The input() Function

In Python 2, input() evaluates the user input, which can be a security issue. In Python 3, input() just reads the user input as a string. If you are migrating from Python 2 to Python 3, you should be careful with this change. Here’s an example of how to use input() in Python 3:

# Python 2.7
x = input("Enter a number: ")  # This evaluates the user input

# Python 3.7
x = input("Enter a number: ")  # This takes the user input as a string

The round() Function

Another difference between Python 2 and Python 3 is how they handle the round() function. In Python 2, round(0.5) rounds to the nearest even number. In Python 3, it rounds to the nearest integer. Here’s an example:

# Python 2.7
print(round(0.5))  # This prints 0

# Python 3.7
print(round(0.5))  # This prints 1

Changes in Comparison Operators

In Python 2, you could compare unorderable objects. Python 3 will throw an exception in these cases. Therefore, you should review your code to ensure you are not trying to compare unorderable objects. For example:

# Python 2.7
print(1 < '1')  # This is True

# Python 3.7
print(1 < '1')  # This throws a TypeError exception

Using next()

The next() function is used to get the next item from an iterator. In Python 2, you could use the iterator.next() method. In Python 3, you should use the next(iterator) function. Here’s an example:

# Python 2.7
iterator = iter([1, 2, 3])
print iterator.next()  # This prints 1

# Python 3.7
iterator = iter([1, 2, 3])
print(next(iterator))  # This prints 1

Default Encoding

Python 2 uses ASCII as the default encoding, while Python 3 uses UTF-8. This can cause problems if your Python 2 code handles strings that are not ASCII. Here’s how you can handle this in Python 3:

# Python 2.7
string = '¡Hola, mundo!'

# Python 3.7
string = '¡Hola, mundo!'

__eq__() or __cmp__()?

In Python 2, you could implement the __cmp__() method in your classes to perform comparisons between objects. Python 3 eliminates this method and instead, you should implement the __eq__() and __lt__() methods for equality and comparison respectively. Here’s an example:

# Python 2.7
class Test(object):
    def __cmp__(self, other):
        return self.value - other.value

# Python 3.7
class Test(object):
    def __eq__(self, other):
        return self.value == other.value

    def __lt__(self, other):
        return self.value < other.value

Changes in Class Variables

In Python 2, class variables are accessible through the class instance. In Python 3, you can’t change class variables through the instance. Therefore, when migrating your Python 2 code to Python 3, you should keep this in mind. Here’s an example:

# Python 2.7
class Test(object):
    value = "Hello, world!"
    
    def change_value(self, new_value):
        self.value = new_value

test = Test()
test.change_value("Goodbye, world!")
print(test.value)  # Prints: Goodbye, world!

# Python 3.7
class Test(object):
    value = "Hello, world!"
    
    def change_value(self, new_value):
        self.value = new_value

test = Test()
test.change_value("Goodbye, world!")
print(test.value)  # Prints: Hello, world!
print(Test.value)  # Prints: Goodbye, world!

Adding *args and **kwargs in Class Methods

Python 2 allows class methods to accept an arbitrary number of positional and keyword arguments, even if they are not defined in the method. Python 3 does not allow this, so you should make sure to include *args and **kwargs in the method definition if you want it to accept an arbitrary number of arguments. Here’s an example:

# Python 2.7
class Test(object):
    def method(self, x):
        print(x)

test = Test()
test.method(1, 2, 3)  # This is valid

# Python 3.7
class Test(object):
    def method(self, x, *args, **kwargs):
        print(x, args, kwargs)

test = Test()
test.method(1, 2, 3)  # This is valid

Iterators in Python 3

In Python 2, the methods dict.iterkeys(), dict.itervalues(), and dict.iteritems() return iterators. Python 3 replaces them with dict.keys(), dict.values(), and dict.items(), which return dictionary views. But don’t worry, because these are also iterable and you can convert them to lists if needed.

# Python 2.7
dictionary = {'one': 1, 'two': 2}
for key in dictionary.iterkeys():
    print(key)

# Python 3.7
dictionary = {'one': 1, 'two': 2}
for key in dictionary.keys():
    print(key)

With all these tricks, I’m sure your migration from Python 2.7 to Python 3.7 will be smoother. Don’t forget that migrating your code to Python 3 is not only important because of the improvements it offers, but also because Python 2 is no longer officially maintained. Good luck on your journey towards Python 3!

Leave a Reply