3 Common Python Errors
3 Common Python Errors
to code in Python
These errors created big problems that took hours to solve.
28
up
7 comments
Image by :
opensource.com
It's never easy to admit when you do things wrong, but making errors is part o
any learning process, from learning to walk to learning a new programming
language, such as Python.
Here's a list of three things I got wrong when I was learning Python, presented
so that newer Python programmers can avoid making the same mistakes.
These are errors that either I got away with for a long time or that that created
big problems that took hours to solve.
Take heed young coders, some of these mistakes are afternoon wasters!
On the face of it, this looks like perfectly normal Python, and indeed it is. It
works. But there are issues with it. If we supply a list for the add_toparameter,
it works as expected. If, however, we let it use the default, something
interesting happens.
fn(3)
fn(4)
fn(5)
[3]
[4]
[5]
Why? Well, you see, the same list is used each time. In Python, when we
write the function like this, the list is instantiated as part of the function's
definition. It is not instantiated each time the function is run. This means that
the function keeps using the exact same list object again and again, unless of
course we supply another one:
fn(3, [4])
[4, 3]
Just as expected. The correct way to achieve the desired result is:
This moves the instantiation from module load time so that it happens every
time the function runs. Note that for immutable data types,
like tuples, strings, or ints, this is not necessary. That means it is perfectly
fine to do something like:
class URLCatcher(object):
urls = []
This code looks perfectly normal. We have an object with a storage of URLs.
When we call the add_url method, it adds a given URL to the store. Perfect
right? Let's see it in action:
a = URLCatcher()
a.add_url('http://www.google.')
b = URLCatcher()
b.add_url('http://www.bbc.co.')
b.urls
['http://www.google.com', 'http://www.bbc.co.uk']
a.urls
['http://www.google.com', 'http://www.bbc.co.uk']
Turns out it's kinda the same problem as in the first example. The URLs list is
instantiated when the class definition is created. All instances of that class use
the same list. Now, there are some cases where this is advantageous, but the
majority of the time you don't want to do this. You want each object to have a
separate store. To do that, we would modify the code like:
class URLCatcher(object):
def __init__(self):
self.urls = []
def add_url(self, url):
self.urls.append(url)
Now the URLs list is instantiated when the object is created. When we
instantiate two separate objects, they will be using two separate lists.
Now let's assume we want to take that dict and use it someplace else, leaving
the original intact.
b = a
b['3'] = 'three'
Simple eh?
Now let's look at our original dict, a, the one we didn't want to modify:
Wait what? But… let's step back and see what happens with our other
immutable types, a tuple for instance:
c = (2, 3)
d = c
d = (4, 5)
Now c is:
(2, 3)
While d is:
(4, 5)
Does the same thing happen with lists? Yes. So how do we get around it?
Well, we have to be very careful. If we really need to copy a list for
processing, we can do so like:
b = a[:]
This will go through and copy a reference to each item in the list and place it
in a new list. But be warned: If any objects in the list are mutable, we will
again get references to those, rather than complete copies.
Dicts function in the same way, and you can create this expensive copy by
doing:
b = a.copy()
Again, this will only create a new dictionary pointing to the same entries that
were present in the original. Thus, if we have two lists that are identical and
we modify a mutable object that is pointed to by a key from dict 'a', the dict
object present in dict 'b' will also see those changes.
The trouble with mutable data types is that they are powerful. None of the
above are real problems; they are things to keep in mind to prevent issues.
The expensive copy operations presented as solutions in the third item are
unnecessary 99% of the time. Your program can and probably should be
modified so that those copies are not even required in the first place.
Topics
Python
Programming
Pete Savage - Peter is a passionate Open Source enthusiast who has been promoting and
using Open Source products for the last 10 years. He has volunteered in many different areas,
starting in the Ubuntu community, before moving off into the realms of audio production and
later into writing. Career wise he spent much of his early years managing and building
datacenters as a sysadmin, before ending up working for Red Hat as a Principal Quailty
Engineer for the CloudForms product. He occasionally pops out a