Become More Advanced: Master the 10 Most Common Python Programming Problems

View all articles

About Python

Python is an interpreted, object-oriented, high-level programming language with dynamic semantics. Its high-level built in data structures, combined with dynamic typing and dynamic binding, make it very attractive for Rapid Application Development, as well as for use as a scripting or glue language to connect existing components or services. Python supports modules and packages, thereby encouraging program modularity and code reuse.

About this article

Python’s simple, easy-to-learn syntax can mislead Python developers – especially those who are newer to the language – into missing some of its subtleties and underestimating the power of the diverse Python language.

With that in mind, this article presents a “top 10” list of somewhat subtle, harder-to-catch mistakes that can bite even some more advanced Python developers in the rear.

This Python found himself caught in an advanced Python programming problem.

(Note: This article is intended for a more advanced audience than Common Mistakes of Python Programmers, which is geared more toward those who are newer to the language.)

Common Mistake #1: Misusing expressions as defaults for function arguments

Python allows you to specify that a function argument is optional by providing a default value for it. While this is a great feature of the language, it can lead to some confusion when the default value is mutable. For example, consider this Python function definition:

>>> def foo(bar=[]):        # bar is optional and defaults to [] if not specified
...    bar.append("baz")    # but this line could be problematic, as we'll see...
...    return bar

A common mistake is to think that the optional argument will be set to the specified default expression each time the function is called without supplying a value for the optional argument. In the above code, for example, one might expect that calling foo() repeatedly (i.e., without specifying a bar argument) would always return 'baz', since the assumption would be that each time foo() is called (without a bar argument specified) bar is set to [] (i.e., a new empty list).

But let’s look at what actually happens when you do this:

>>> foo()
["baz"]
>>> foo()
["baz", "baz"]
>>> foo()
["baz", "baz", "baz"]

Huh? Why did it keep appending the default value of "baz" to an existing list each time foo() was called, rather than creating a new list each time?

The more advanced Python programming answer is that the default value for a function argument is only evaluated once, at the time that the function is defined. Thus, the bar argument is initialized to its default (i.e., an empty list) only when foo() is first defined, but then calls to foo() (i.e., without a bar argument specified) will continue to use the same list to which bar was originally initialized.

FYI, a common workaround for this is as follows:

>>> def foo(bar=None):
...    if bar is None:		# or if not bar:
...        bar = []
...    bar.append("baz")
...    return bar
...
>>> foo()
["baz"]
>>> foo()
["baz"]
>>> foo()
["baz"]

Common Mistake #2: Using class variables incorrectly

Consider the following example:

>>> class A(object):
...     x = 1
...
>>> class B(A):
...     pass
...
>>> class C(A):
...     pass
...
>>> print A.x, B.x, C.x
1 1 1

Makes sense.

>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1

Yup, again as expected.

>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3

What the $%#!&?? We only changed A.x. Why did C.x change too?

In Python, class variables are internally handled as dictionaries and follow what is often referred to as Method Resolution Order (MRO). So in the above code, since the attribute x is not found in class C, it will be looked up in its base classes (only A in the above example, although Python supports multiple inheritance). In other words, C doesn’t have its own x property, independent of A. Thus, references to C.x are in fact references to A.x. This causes a Python problem unless it’s handled properly. Learn more aout class attributes in Python.

Common Mistake #3: Specifying parameters incorrectly for an exception block

Suppose you have the following code:

>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except ValueError, IndexError:  # To catch both exceptions, right?
...     pass
...
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
IndexError: list index out of range

The problem here is that the except statement does not take a list of exceptions specified in this manner. Rather, In Python 2.x, the syntax except Exception, e is used to bind the exception to the optional second parameter specified (in this case e), in order to make it available for further inspection. As a result, in the above code, the IndexError exception is not being caught by the except statement; rather, the exception instead ends up being bound to a parameter named IndexError.

The proper way to catch multiple exceptions in an except statement is to specify the first parameter as a tuple containing all exceptions to be caught. Also, for maximum portability, use the as keyword, since that syntax is supported by both Python 2 and Python 3:

>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except (ValueError, IndexError) as e:  
...     pass
...
>>>

Common Mistake #4: Misunderstanding Python scope rules

Python scope resolution is based on what is known as the LEGB rule, which is shorthand for Local, Enclosing, Global, Built-in. Seems straightforward enough, right? Well, actually, there are some subtleties to the way this works in Python, which brings us to the common more advanced Python programming problem below. Consider the following:

>>> x = 10
>>> def foo():
...     x += 1
...     print x
...
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment

What’s the problem?

The above error occurs because, when you make an assignment to a variable in a scope, that variable is automatically considered by Python to be local to that scope and shadows any similarly named variable in any outer scope.

Many are thereby surprised to get an UnboundLocalError in previously working code when it is modified by adding an assignment statement somewhere in the body of a function. (You can read more about this here.)

It is particularly common for this to trip up developers when using lists. Consider the following example:

>>> lst = [1, 2, 3]
>>> def foo1():
...     lst.append(5)   # This works ok...
...
>>> foo1()
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2():
...     lst += [5]      # ... but this bombs!
...
>>> foo2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment

Huh? Why did foo2 bomb while foo1 ran fine?

The answer is the same as in the prior example problem, but is admittedly more subtle. foo1 is not making an assignment to lst, whereas foo2 is. Remembering that lst += [5] is really just shorthand for lst = lst + [5], we see that we are attempting to assign a value to lst (therefore presumed by Python to be in the local scope). However, the value we are looking to assign to lst is based on lst itself (again, now presumed to be in the local scope), which has not yet been defined. Boom.

Common Mistake #5: Modifying a list while iterating over it

The problem with the following code should be fairly obvious:

>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> for i in range(len(numbers)):
...     if odd(numbers[i]):
...         del numbers[i]  # BAD: Deleting item from a list while iterating over it
...
Traceback (most recent call last):
  	  File "<stdin>", line 2, in <module>
IndexError: list index out of range

Deleting an item from a list or array while iterating over it is a Python problem that is well known to any experienced software developer. But while the example above may be fairly obvious, even advanced developers can be unintentionally bitten by this in code that is much more complex.

Fortunately, Python incorporates a number of elegant programming paradigms which, when used properly, can result in significantly simplified and streamlined code. A side benefit of this is that simpler code is less likely to be bitten by the accidental-deletion-of-a-list-item-while-iterating-over-it bug. One such paradigm is that of list comprehensions. Moreover, list comprehensions are particularly useful for avoiding this specific problem, as shown by this alternate implementation of the above code which works perfectly:

>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> numbers[:] = [n for n in numbers if not odd(n)]  # ahh, the beauty of it all
>>> numbers
[0, 2, 4, 6, 8]

Common Mistake #6: Confusing how Python binds variables in closures

Considering the following example:

>>> def create_multipliers():
...     return [lambda x : i * x for i in range(5)]
>>> for multiplier in create_multipliers():
...     print multiplier(2)
...

You might expect the following output:

0
2
4
6
8

But you actually get:

8
8
8
8
8

Surprise!

This happens due to Python’s late binding behavior which says that the values of variables used in closures are looked up at the time the inner function is called. So in the above code, whenever any of the returned functions are called, the value of i is looked up in the surrounding scope at the time it is called (and by then, the loop has completed, so i has already been assigned its final value of 4).

The solution to this common Python problem is a bit of a hack:

>>> def create_multipliers():
...     return [lambda x, i=i : i * x for i in range(5)]
...
>>> for multiplier in create_multipliers():
...     print multiplier(2)
...
0
2
4
6
8

Voilà! We are taking advantage of default arguments here to generate anonymous functions in order to achieve the desired behavior. Some would call this elegant. Some would call it subtle. Some hate it. But if you’re a Python developer, it’s important to understand in any case.

Common Mistake #7: Creating circular module dependencies

Let’s say you have two files, a.py and b.py, each of which imports the other, as follows:

In a.py:

import b

def f():
    return b.x
	
print f()

And in b.py:

import a

x = 1

def g():
    print a.f()

First, let’s try importing a.py:

>>> import a
1

Worked just fine. Perhaps that surprises you. After all, we do have a circular import here which presumably should be a problem, shouldn’t it?

The answer is that the mere presence of a circular import is not in and of itself a problem in Python. If a module has already been imported, Python is smart enough not to try to re-import it. However, depending on the point at which each module is attempting to access functions or variables defined in the other, you may indeed run into problems.

So returning to our example, when we imported a.py, it had no problem importing b.py, since b.py does not require anything from a.py to be defined at the time it is imported. The only reference in b.py to a is the call to a.f(). But that call is in g() and nothing in a.py or b.py invokes g(). So life is good.

But what happens if we attempt to import b.py (without having previously imported a.py, that is):

>>> import b
Traceback (most recent call last):
  	  File "<stdin>", line 1, in <module>
  	  File "b.py", line 1, in <module>
    import a
  	  File "a.py", line 6, in <module>
	print f()
  	  File "a.py", line 4, in f
	return b.x
AttributeError: 'module' object has no attribute 'x'

Uh-oh. That’s not good! The problem here is that, in the process of importing b.py, it attempts to import a.py, which in turn calls f(), which attempts to access b.x. But b.x has not yet been defined. Hence the AttributeError exception.

At least one solution to this is quite trivial. Simply modify b.py to import a.py within g():

x = 1

def g():
    import a	# This will be evaluated only when g() is called
    print a.f()

No when we import it, everything is fine:

>>> import b
>>> b.g()
1	# Printed a first time since module 'a' calls 'print f()' at the end
1	# Printed a second time, this one is our call to 'g'

Common Mistake #8: Name clashing with Python Standard Library modules

One of the beauties of Python is the wealth of library modules that it comes with “out of the box”. But as a result, if you’re not consciously avoiding it, it’s not that difficult to run into a name clash between the name of one of your modules and a module with the same name in the standard library that ships with Python (for example, you might have a module named email.py in your code, which would be in conflict with the standard library module of the same name).

This can lead to gnarly problems, such as importing another library which in turns tries to import the Python Standard Library version of a module but, since you have a module with the same name, the other package mistakenly imports your version instead of the one within the Python Standard Library. This is where bad Python errors happen.

Care should therefore be exercised to avoid using the same names as those in the Python Standard Library modules. It’s way easier for you to change the name of a module within your package than it is to file a Python Enhancement Proposal (PEP) to request a name change upstream and to try and get that approved.

Common Mistake #9: Failing to address differences between Python 2 and Python 3

Consider the following file foo.py:

import sys

def bar(i):
    if i == 1:
        raise KeyError(1)
    if i == 2:
        raise ValueError(2)

def bad():
    e = None
    try:
        bar(int(sys.argv[1]))
    except KeyError as e:
        print('key error')
    except ValueError as e:
        print('value error')
    print(e)

bad()

On Python 2, this runs fine:

$ python foo.py 1
key error
1
$ python foo.py 2
value error
2

But now let’s give it a whirl on Python 3:

$ python3 foo.py 1
key error
Traceback (most recent call last):
  File "foo.py", line 19, in <module>
    bad()
  File "foo.py", line 17, in bad
    print(e)
UnboundLocalError: local variable 'e' referenced before assignment

What has just happened here? The “problem” is that, in Python 3, the exception object is not accessible beyond the scope of the except block. (The reason for this is that, otherwise, it would keep a reference cycle with the stack frame in memory until the garbage collector runs and purges the references from memory. More technical detail about this is available here).

One way to avoid this issue is to maintain a reference to the exception object outside the scope of the except block so that it remains accessible. Here’s a version of the previous example that uses this technique, thereby yielding code that is both Python 2 and Python 3 friendly:

import sys

def bar(i):
    if i == 1:
        raise KeyError(1)
    if i == 2:
        raise ValueError(2)

def good():
    exception = None
    try:
        bar(int(sys.argv[1]))
    except KeyError as e:
        exception = e
        print('key error')
    except ValueError as e:
        exception = e
        print('value error')
    print(exception)

good()

Running this on Py3k:

$ python3 foo.py 1
key error
1
$ python3 foo.py 2
value error
2

Yippee!

(Incidentally, our Python Hiring Guide discusses a number of other important differences to be aware of when migrating code from Python 2 to Python 3.)

Common Mistake #10: Misusing the __del__ method

Let’s say you had this in a file called mod.py:

import foo

class Bar(object):
   	    ...
    def __del__(self):
        foo.cleanup(self.myhandle)

And you then tried to do this from another_mod.py:

import mod
mybar = mod.Bar()

You’d get an ugly AttributeError exception.

Why? Because, as reported here, when the interpreter shuts down, the module’s global variables are all set to None. As a result, in the above example, at the point that __del__ is invoked, the name foo has already been set to None.

A solution to this somewhat more advanced Python programming problem would be to use atexit.register() instead. That way, when your program is finished executing (when exiting normally, that is), your registered handlers are kicked off before the interpreter is shut down.

With that understanding, a fix for the above mod.py code might then look something like this:

import foo
import atexit

def cleanup(handle):
    foo.cleanup(handle)


class Bar(object):
    def __init__(self):
        ...
        atexit.register(cleanup, self.myhandle)

This implementation provides a clean and reliable way of calling any needed cleanup functionality upon normal program termination. Obviously, it’s up to foo.cleanup to decide what to do with the object bound to the name self.myhandle, but you get the idea.

Wrap-up

Python is a powerful and flexible language with many mechanisms and paradigms that can greatly improve productivity. As with any software tool or language, though, having a limited understanding or appreciation of its capabilities can sometimes be more of an impediment than a benefit, leaving one in the proverbial state of “knowing enough to be dangerous”.

Familiarizing oneself with the key nuances of Python, such as (but by no means limited to) the moderately advanced programming problems raised in this article, will help optimize use of the language while avoiding some of its more common errors.

You might also want to check out our Insider’s Guide to Python Interviewing for suggestions on interview questions that can help identify Python experts.

We hope you’ve found the pointers in this article helpful and welcome your feedback.

Hiring? Meet the Top 10 Python Developers for Hire in October 2014
Like what you're reading?
Get the latest updates first.
Don't miss out.
Get the latest updates first.

Comments

Hubert OG
Common Mistake #0: Programming in Python
Dan Riti
For Common Mistake #3, you should be using the `as` syntax for capturing exceptions, as it's both Python2.6+ & Python3+ compatible: https://docs.python.org/3/howto/pyporting.html#capturing-the-currently-raised-exception
not me
Common Mistake #-1: Being a programmer
Oskar Olsson
So brave.
Alex
so common, very python
Adam Hitchcock
Why not Python? What alternative would you propose? And why is it specifically better than Python?
cocoflunchy
Any reason why you're using a slice here? >>> numbers[:] = [n for n in numbers if not odd(n)] I'm thinking that doing >>> numbers = [n for n in numbers if not odd(n)] wouldn't be a problem since the assignment is executed after the computation of the list comprehension.
Andres Moreno
for common mistake #6 you can replace the list comprehension with a generator (substitute the square brackets for parentheses) and the code will work fine.... In [6]: def create_multipliers(): ...: return (lambda x: x*i for i in range(5)) ...: In [7]: for multiplies in create_multipliers(): ...: print multiplies(2) ...: 0 2 4 6 8
Kim Scheibel
Re. #1: If you're mutating arguments that have been passed by reference, and you're surprised that this has sideeffects, then you have problems more generally than just when handling defaulted arguments. For example, what happens when the client actually does pass in a list (which in Python is always a reference) and your function blithely changes it? I know this is a minority view, but I don't think that the solution is to do the "if arg is None" dance (notwithstanding the Google style guide, etc). The solution is for the implementor of any function to understand that changing an argument that was passed in by reference, generally, has sideeffects, and that this should only be done very deliberately, and then only when the function is named to make it obvious that it's going to mess with its arguments.
Adam Hitchcock
I found the <code>numbers[:]</code> an odd way of doing that assignment. So I looked it up. It looks like it keeps the same object. <code class="python"> >>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> id(numbers) 4424446032 >>> numbers = [n for n in numbers if not odd(n)] # ahh, the beauty of it all >>> id(numbers) 4424505176 >>> numbers [0, 2, 4, 6, 8] >>> >>> >>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> id(numbers) 4424446032 >>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all >>> id(numbers) 4424446032 >>> numbers [0, 2, 4, 6, 8] </code> Cool, it looks like it keeps the same id/object. What do we get out of this though? <code class="python"> >>> from timeit import timeit >>> >>> setup = """ ... odd = lambda x : bool(x % 2) ... numbers = [n for n in range(10)] ... """ >>> stmnt_1 = "numbers[:] = [n for n in numbers if not odd(n)]" >>> stmnt_2 = "numbers = [n for n in numbers if not odd(n)]" >>> >>> timeit(stmnt_1, setup, number=1000000) 1.7668359279632568 >>> timeit(stmnt_1, setup, number=10000000) 17.681034088134766 >>> timeit(stmnt_2, setup, number=1000000) 1.7781000137329102 >>> timeit(stmnt_2, setup, number=10000000) 17.093994855880737 >>> # to account for nutrinos: >>> s = [(timeit(stmnt_1, setup, number=1000000), timeit(stmnt_2, setup, number=1000000)) for _ in range(10)] >>> s [(1.7270538806915283, 1.7055668830871582), (1.7197210788726807, 1.672379970550537), (1.7634928226470947, 1.6488819122314453), (1.717656135559082, 1.7615430355072021), (1.8057420253753662, 1.690622091293335), (1.7476260662078857, 1.7099308967590332), (1.8953900337219238, 1.6794281005859375), (1.8406779766082764, 1.839482069015503), (1.8078999519348145, 1.8191499710083008), (1.7242348194122314, 1.8715410232543945)] >>> [a - b for a, b in s] [0.021486997604370117, 0.047341108322143555, 0.11461091041564941, -0.04388689994812012, 0.11511993408203125, 0.03769516944885254, 0.21596193313598633, 0.0011959075927734375, -0.011250019073486328, -0.14730620384216309] </code> Weird, it actually seems (a very very small amount) slower. Any other reasons the [:] syntax could be beneficial?
Adam Hitchcock
You mean mistake #0.
Hubert OG
A sense of humor, perhaps.
Adam Hitchcock
see my analysis here: http://www.toptal.com/python/top-10-mistakes-that-python-programmers-make#comment-1375427290
Adam Hitchcock
Oh, sorry, I left that at home today. To heavy to carry on bike to work day.
H. Singer
Good point! We updated this accordingly. Thanks for the input. [Toptal blog editor]
Martin Chikilian
As you correctly pointed out the form without the empty slice `numbers = ...` creates a new list and binds it to a new `numbers` name whereas using the `numbers[:] = ...` version modifies the already existent object. Code in this example wasn't written taking performance into consideration and the use of the empty slice notation is only to reflect that the same list as before has been modified ('[n for n in range(10)]')
Martin Chikilian
Very nice one!
Mario Campos
I noticed something strange about common mistake #5. You used `numbers = [n for n in range(10)]` as opposed to just `numbers = range(10)`. Curiously, I wanted to see what difference this made: ``` >>> from timeit import timeit >>> timeit("""n = [x for x in range(10)]""", number=1000000) 1.0391340255737305 >>> timeit("""n = range(10)""", number=1000000) 0.27240204811096191 >>> timeit("""n = [x for x in range(10)]""", number=10000000) 10.304471969604492 >>> timeit("""n = range(10)""", number=10000000) 2.7591819763183594 ``` Quite the difference! A whole factor of 5!
Martin Chikilian
Good comment, glad that you bring this up! Actually doing just `numbers = range(10)` doesn't make a lot of sense in Python 3 as `range` produces (initializes) a different kind of object than a list in this version (object `range` to be more specific) so the intention behind writing down with list comprehension syntax here was for readability.
Martin Chikilian
Good point! As I explained to @NorthIsUp:disqus here the reason is to make it clear we were modifying (and deleting) the previous list, performance hasn't been considered (although the difference in time is quite minimal across multiple executions of the same block). Thanks for asking!
Eli Collins
For mistake #4, might want to point out that Python 3 added the `nonlocal` keyword ([PEP 3104](http://legacy.python.org/dev/peps/pep-3104/)), which indicates that a variable should be shared with the enclosing scope rather than shadow it.
killermonkey
For #5 you should just do: numbers = [n for n in range(10) if not odd(n)] Your common mistake contained a common mistake... that is doing more work than is necessary in your program to achieve the same result. Plus this 1 line of code is much more readable.
22bytes
Lambda has rare use lately. P.S. Your correction should rather be numbers = [ n for n in range(10) if not odd(n) ] You forgot `n`. :)
22bytes
For lists, <code>+=</code> is not a shorthand for <code>lst = lst + iterable</code>. The longer version creates a new object (with a different <code>id</code>). Also unlike the shorthand, it only accepts lists as operands of the <code>+</code> operator.
Jamis Johnson
I felt #5 could be slightly cleaned up. I made sure the same IndexError still occurred. https://gist.github.com/jamiis/9075c95477d4becd49c9 odd = lambda x : bool(x % 2) numbers = range(10) for i in numbers: if odd(numbers[i]): del numbers[i]
jimbobx
Was #6 (and maybe #1) stolen from http://docs.python-guide.org/en/latest/writing/gotchas/#late-binding-closures ? The examples date from 2012 and I don't see any attribution as required under their creative commons Attribution-NonCommercial-ShareAlike license. ( http://creativecommons.org/licenses/by-nc-sa/3.0/ )
Mihai Caraman
Also list() is just a bit faster. ```python >>> timeit("""n = [x for x in range(10)]""", number=10000000) 8.42811918258667 >>> timeit("""n = list(range(10))""", number=10000000) 5.411137104034424 ```
Martin Chikilian
I don't necessarily agree with you on the statement "Lambda has rare use lately", most people used to style their code according to the functional programming paradigm use it and the rest of the people too (maybe less frequently). Although personally I prefer not to use them in favor of named functions, people coming from other languages and used to functional programming too will feel at home when they see Python has support for anonymous functions.
Martin Chikilian
Good point! Although I'd write the code on any program just like you did, the intention behind separating it out into two lines in the example was to make it more readable (when you first see the two separate lines you know exactly what's going on with `numbers`).
lcfseth
Mistake #0: Python is not an interpreted language but a compiled one (yep, compiled).
alexeiramone
Cool. i'm glad i'm not making any of these...
Uri Laserson
Great list! For #8, PEP 328 largely addresses the issue.
Pooya Eghbali
You can replace: if bar is None: bar = [] with: bar = bar or []
Myautsai
Common Mistake #11: for each in (STR_CONST1, STR_CONST2):... But one day we do not need STR_CONSTS2 anymore: for each in (STR_CONST1):... #wrong for each in (STR_CONST1, ):... #right
introom
#!/usr/bin/env python3 import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def bad(): a = None try: bar(int(1)) except KeyError as e: a = e print('key error') except ValueError as e: print('value error') print(a) bad() One question. In the above code, watch the assignment a = e changes the final print(a) outcome. So we can guess the scope inside except as KeyError as e is the same as the function scope. However, as discussed in the article, the name e is only accessible in that except scope. How to explain this phenomenon?
Ruitao Xu
In #2,why is B.x still 2 ?
Udi Oron
Regarding common mistake #5, your mistake is to iterate using len(numbers). There is no problem modifying a list you iterate over if you use enumerate: for i, x in enumerate(numbers): if odd(x): del numbers[i]
Udi Oron
Regarding #6, although the solution below is much longer, I find it the easiest to grasp, and I will tend to use it if I will need this in my real life code: <pre><code class="python"> def create_multiplier(i): def f(x): return x * i return f def create_multipliers(): return [create_multiplier(i) for i in range(5)] for multiplier in create_multipliers(): print multiplier(2) </code></pre>
Udi Oron
Regarding circular imports and #7: The main problem in importing is when using the <code class="python">from mymodule import mysymbol</code>. You have solved this issue properly by using <code class="python">import mymodule</code>, although this might cause some more problem if your design is wrong. What you introduce here is a design problem. Calling <code class="python">f()</code> from the module (library) code itself is a very bad idea. Instead one should do this: a.py: <pre><code class="python"> import b def f(): return b.x </code></pre> b.py: <pre><code class="python"> import a x = 1 def g(): print a.f() </code></pre> main.py: <pre><code class="python"> import a a.f() </code></pre>
Udi Oron
Regarding name clashing (#8): Hopefully your code (modules) are already in packages. To avoid name clashing (in packages) use absolute imports or explicit relative imports as suggested by <a href="http://legacy.python.org/dev/peps/pep-0008/#imports">PEP8</a>, which also states: > Implicit relative imports should never be used and have been removed in Python 3. i.e., one should use one of <pre><code class="python"> from mypkg import email from . import email import mypkg.email #or: from .email import foo() from ..email import foo() </code></pre> etc.
katisss@gmx.de
>>> def foo(bar=None): ... if bar is None: # or if not bar: ... bar = [] How about bar=bar or [] Shorter and easier to read?
Laurent
One reason where I would see a use of [:] is if you have other names pointing to the array, and you want them to see the change.
wittawatj
About common mintake #6: the binding in lambda expressions. Apart from using the generator, another way: def create_multipliers(): def make_lambda(i): return lambda x: i*x; return [make_lambda(i) for i in range(5)] for multiplier in create_multipliers(): print multiplier(2)
TheKestrel
In terms of scripting languages, Python is better than Ruby, but I would say Javascript is the best of the bunch if for no other reason because it is so much more widely used that it has forced folks to confront more of the gremlins - either by language enhancements or libraries such as Underscore and Backbone. Also, it's variable scoping, while containing a gotcha or two, is generally better - especially when 'use strict' is applied so that all declarations are explicit. If we're including non-scripting languages, there are a few candidates - C# has the best data querying syntax I've seen in any language courtesy of LINQ, and now that it supports dynamically typed variables it has the ability to do virtually everything Python can, with the (in my humble opinion) much wiser choice to make dynamic typing the 1% use-case and static typing the 99%.
22bytes
You know, Python is not Haskell or for that matter any other programming language. Past experience in one language is not a license to write forgotten Python. You're better served discussing the state of <code>lambda</code> in #python.
introom
OK. In case it confuses someone like me. The except clause doesn't introduce a new scope. It just deletes the name 'e' after its execution. It is documented on the website.
Tetsuya Morimoto
No. The boolean value is able to change with a special method if bar is a custom object. - py2: __nonzero__ - py3:__bool__
Tetsuya Morimoto
Good article! Can I translate this article into Japanese?
Abhishek Anurag
Good point. Also, I'm not sure if going by the suggestion "#or not bar" works correctly in all cases, I tried that with a list in the code below and it does not work as is explained. def inorder(self, inorder_opt=None): if inorder_opt is None: #if not bar doesn't work correctly here, get's reinitialized again in recursive calls inorder_opt = [] if (self.left != None): self.left.inorder(inorder_opt) if (self.key != None): inorder_opt.append(self.key) if (self.right != None): self.right.inorder(inorder_opt) return inorder_opt
22bytes
I prefer <code>if bar is None: ...</code>. There's a lot of Python values that coerce to false including the empty list.
Ionel Cristian Mărieș
I'm a bit surprised (and disapointed) there's nothing here about 'too broad exception handling'. Ever did a bare <code>except:</code> ? How about <code>except: pass</code> ? That's a fairly common mistake - still rampaging today ...
Meep Johnston
Assigning to B.x in the example directly above gives B its own name x and thusly detaches it from A.x. Had B.x not been assigned directly first, B.x and C.x would both reflect the new value of A.x
Vaquero de Oro
This post should be renamed "Top 10 Mistakes Made in Python's Design" (which most often confuse sensible people), IMO.
etermory
#6: another way. <pre><code class="python"> def create_multipliers(): for i in range(5): yield lambda x : i * x for multiplier in create_multipliers(): print(multiplier(2)) </code></pre>
comments powered by Disqus
Subscribe
Free email updates
Get the latest content first.
Trending articles
Relevant technologies
Toptal Authors
iOS Engineer
Breanden Beneschott
Co-Founder / COO @ Toptal
Software Engineer
Software Engineer
Software Engineer