147

Python seems all the rage these days, and not undeservingly - for it is truly a language with which one almost enjoys being given a new problem to solve. But, as a wise man once said (calling him a wise man only because I've no idea as to who actually said it; not sure whether he was that wise at all), to really know a language one does not only know its syntax, design, etc., advantages but also its drawbacks. No language is perfect, some are just better than others.

So, what would be in your opinion, objective drawbacks of Python.

Note: I'm not asking for a language comparison here (i.e. C# is better than Python because ... yadda yadda yadda) - more of an objective (to some level) opinion which language features are badly designed, whether, what are maybe some you're missing in it and so on. If must use another language as a comparison, but only to illustrate a point which would be hard to elaborate on otherwise (i.e. for ease of understanding)

Rook
  • 19,947

24 Answers24

108

I use Python somewhat regularly, and overall I consider it to be a very good language. Nonetheless, no language is perfect. Here are the drawbacks in order of importance to me personally:

  1. It's slow. I mean really, really slow. A lot of times this doesn't matter, but it definitely means you'll need another language for those performance-critical bits.

  2. Nested functions kind of suck in that you can't modify variables in the outer scope. Edit: I still use Python 2 due to library support, and this design flaw irritates the heck out of me, but apparently it's fixed in Python 3 due to the nonlocal statement. Can't wait for the libs I use to be ported so this flaw can be sent to the ash heap of history for good.

  3. It's missing a few features that can be useful to library/generic code and IMHO are simplicity taken to unhealthy extremes. The most important ones I can think of are user-defined value types (I'm guessing these can be created with metaclass magic, but I've never tried), and ref function parameter.

  4. It's far from the metal. Need to write threading primitives or kernel code or something? Good luck.

  5. While I don't mind the lack of ability to catch semantic errors upfront as a tradeoff for the dynamism that Python offers, I wish there were a way to catch syntactic errors and silly things like mistyping variable names without having to actually run the code.

  6. The documentation isn't as good as languages like PHP and Java that have strong corporate backings.

dsimcha
  • 17,284
66

I hate that Python can’t distinguish between declaration and usage of a variable. You don’t need static typing to make that happen. It would just be nice to have a way to say “this is a variable that I deliberately declare, and I intend to introduce a new name, this is not a typo”.

Furthermore, I usually use Python variables in a write-once style, that is, I treat variables as being immutable and don’t modify them after their first assignment. Thanks to features such as list comprehension, this is actually incredibly easy and makes the code flow more easy to follow.

However, I can’t document that fact. Nothing in Python prevents me form overwriting or reusing variables.

In summary, I’d like to have two keywords in the language: var and let. If I write to a variable not declared by either of those, Python should raise an error. Furthermore, let declares variables as read-only, while var variables are “normal”.

Consider this example:

x = 42    # Error: Variable `x` undeclared

var x = 1 # OK: Declares `x` and assigns a value.
x = 42    # OK: `x` is declared and mutable.

var x = 2 # Error: Redeclaration of existing variable `x`

let y     # Error: Declaration of read-only variable `y` without value
let y = 5 # OK: Declares `y` as read-only and assigns a value.

y = 23    # Error: Variable `y` is read-only

Notice that the types are still implicit (but let variables are for all intents and purposes statically typed since they cannot be rebound to a new value, while var variables may still be dynamically typed).

Finally, all method arguments should automatically be let, i.e. they should be read-only. There’s in general no good reason to modify a parameter, except for the following idiom:

def foo(bar = None):
    if bar == None: bar = [1, 2, 3]

This could be replaced by a slightly different idiom:

def foo(bar = None):
    let mybar = bar or [1, 2, 3]
44

My main complaint is threading, which is not as performant in many circumstances (compared to Java, C and others) due to the global interpreter lock (see "Inside the Python GIL" (PDF link) talk)

However there is a multiprocess interface that is very easy to use, however it is going to be heavier on memory usage for the same number of processes vs. threads, or difficult if you have a lot of shared data. The benefit however, is that once you have a program working on with multiple processes, it can scale across multiple machines, something a threaded program can't do.

I really disagree on the critique of the documentation, I think it is excellent and better than most if not all major languages out there.

Also you can catch many of the runtime bugs running pylint.

dbr
  • 161
cmcginty
  • 729
28

Arguably, the lack of static typing, which can introduce certain classes of runtime errors, is not worth the added flexibility that duck typing provides.

Jacob
  • 382
27

I think the object-oriented parts of Python feel kind of "bolted on". The whole need to explicitly pass "self" to every method is a symptom that it's OOP component wasn't expressly planned, you could say; it also shows Python's sometimes warty scoping rules that were criticized in another answer.

Edit:

When I say Python's object-oriented parts feel "bolted on", I mean that at times, the OOP side feels rather inconsistent. Take Ruby, for example: In Ruby, everything is an object, and you call a method using the familiar obj.method syntax (with the exception of overloaded operators, of course); in Python, everything is an object, too, but some methods you call as a function; i.e., you overload __len__ to return a length, but call it using len(obj) instead of the more familiar (and consistent) obj.length common in other languages. I know there are reasons behind this design decision, but I don't like them.

Plus, Python's OOP model lacks any sort of data protection, i.e., there aren't private, protected, and public members; you can mimic them using _ and __ in front of methods, but it's kind of ugly. Similarly, Python doesn't quite get the message-passing aspect of OOP right, either.

mipadi
  • 7,533
19

Things I don't like about Python:

  1. Threading (I know its been mentioned already, but worth mentioning in every post).
  2. No support for multi-line anonymous functions (lambda can contain only one expression).
  3. Lack of a simple but powerful input reading function/class (like cin or scanf in C++ and C or Scanner in Java).
  4. All strings are not unicode by default (but fixed in Python 3).
MAK
  • 1,073
18

Default arguments with mutable data types.

def foo(a, L = []):
    L.append(a)
    print L

>>> foo(1)
[1]
>>> foo(2)
[1, 2]

It's usually the result of some subtle bugs. I think it would be better if it created a new list object whenever a default argument was required (rather than creating a single object to use for every function call).

Edit: It's not a huge problem, but when something needs to be referred in the docs, it commonly means it's a problem. This shouldn't be required.

def foo(a, L = None):
    if L is None:
        L = []
    ...

Especially when that should have been the default. It's just a strange behavior that doesn't match what you would expect and isn't useful for a large number of circumstances.

jsternberg
  • 1,541
14

Some of Python's features that make it so flexible as a development language are also seen as major drawbacks by those used to the "whole program" static analysis conducted by the compilation and linking process in languages such as C++ and Java.

  • Implicit declaration of local variables

Local variables are declared using the ordinary assignment statement. This means that variable bindings in any other scope require explicit annotation to be picked up by the compiler (global and nonlocal declarations for outer scopes, attribute access notation for instance scopes). This massively reduces the amount of boilerplate needed when programming, but means that third party static analysis tools (such as pyflakes) are needed to perform checks that are handled by the compiler in languages that require explicit variable declarations.

  • "Monkey patching" is supported

The contents of modules, class objects and even the builtin namespace can be modified at runtime. This is hugely powerful, allowing many extremely useful techniques. However, this flexibility means that Python does not offer some features common to statically typed OO languages. Most notably, the "self" parameter to instance methods is explicit rather than implicit (since "methods" don't have to be defined inside a class, they can be added later by modifying the class, meaning that it isn't particularly practical to pass the instance reference implicitly) and attribute access controls can't readily be enforced based on whether or not code is "inside" or "outside" the class (as that distinction only exists while the class definition is being executed).

  • Far from the metal

This is also true of many other high level languages, but Python tends to abstract away most hardware details. Systems programming languages like C and C++ are still far better suited to handling direct hardware access (however, Python will quite happily talk to those either via CPython extension modules or, more portably, via the ctypes library).

ncoghlan
  • 3,609
12
  1. Using indentation for code blocks instead of {} / begin-end, whatever.
  2. Every newer modern language has proper lexical scoping, but not Python (see below).
  3. Chaotic docs (compare with Perl5 documentation, which is superb).
  4. Strait-jacket (there's only one way to do it).

Example for broken scoping; transcript from interpreter session:

>>> x=0
>>> def f():
...     x+=3
...     print x
... 
>>> f()
Traceback (most recent call last):
  File "", line 1, in ?
  File "", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment

global and nonlocal keywords have been introduced to patch this design stupidity.

zvrba
  • 3,480
11

I find python's combination of object-oriented this.method() and procedural/functional method(this) syntax very unsettling:

x = [0, 1, 2, 3, 4]
x.count(1)
len(x)
any(x)
x.reverse()
reversed(x)
x.sort()
sorted(x)

This is particularly bad because a large number of the functions (rather than methods) are just dumped into the global namespace: methods relating to lists, strings, numbers, constructors, metaprogramming, all mixed up in one big alphabetically-sorted list.

At the very least, functional languages like F# have all the functions properly namespaced in modules:

List.map(x)
List.reversed(x)
List.any(x)

So they aren't all together. Furthermore, this is a standard followed throughout the library, so at least it's consistent.

I understand the reasons for doing the function vs method thing, but i still think it's a bad idea to mix them up like this. I would be much happier if the method-syntax was followed, at least for the common operations:

x.count(1)
x.len()
x.any()
x.reverse()
x.reversed()
x.sort()
x.sorted()

Whether the methods are mutating or not, having them as methods on the object has several advantages:

  • Single place to look up the "common" operations on a data-type: other libraries/etc. may have other fancy things they can do to the datatypes but the "default" operations are all in the object's methods.
  • No need to keep repeating the Module when calling Module.method(x). Taking the functional List example above, why do i have to keep saying List over and over? It should know that it's a List and I don't want to call the Navigation.map() function on it! Using the x.map() syntax keeps it DRY and still unambiguous.

And of course it has advantages over the put-everything-in-global-namespace way of doing it. It's not that the current way is incapable of getting things done. It's even pretty terse (len(lst)), since nothing is namespaced! I understand the advantages in using functions (default behavior, etc.) over methods, but I still don't like it.

It's just messy. And in big projects, messiness is your worst enemy.

Haoyi
  • 1
8

Lack of homoiconicity.

Python had to wait for 3.x to add a "with" keyword. In any homoiconic language it could have trivially been added in a library.

Most other issues I've seen in the answers are of one of 3 types:

1) Things that can be fixed with tooling (e.g. pyflakes) 2) Implementation details (GIL, performance) 3) Things that can be fixed with coding standards (i.e. features people wish weren't there)

#2 isn't a problem with the language, IMO #1 and #3 aren't serious problems.

gnat
  • 20,543
  • 29
  • 115
  • 306
Jason
  • 101
7

Python is my favourite language as it is very expressive, but still keeps you from making too many mistakes. I still have a few things that annoy me:

  • No real anonymous functions. Lambda can be used for single-statement functions, and the with statement can be used for many things where you'd use a code block in Ruby. But in some situations it makes things a bit more clumsy than they would have to be. (Far from as clumsy as it would be in Java, but still...)

  • Some confusion in the relation between modules and files. Running "python foo.py" from the command line is different from "import foo". Relative imports in Python 2.x can also cause problems. Still, Python's modules is so much better than the corresponding features of C, C++ and Ruby.

  • Explicit self. Even though I understand some of the reasons for it, and even though I use Python daily, I tend to make the mistake of forgetting it. Another issue with it is that it becomes a bit tedious to make a class out of a module. Explicit self is related to the limited scoping that others have complained about. The smallest scope in Python is the function scope. If you keep your functions small, as you should, that isn't a problem by itself and IMO often gives cleaner code.

  • Some global functions, such as len, that you'd expect to be a method (which it actually is behind the scenes).

  • Significant indentation. Not the idea itself, which I think is great, but since this is the single thing that keeps so many people from trying Python, perhaps Python would be better off with some (optional) begin/end symbols. Ignoring those people, I could totally live with an enforced size for the indentation too.

  • That it is not the built-in language of web browsers, instead of JavaScript.

Of these complaints, it's only the very first one that I care enough about that I think it should be added to the language. The other ones are rather minor, except for the last one, which would be great if it happened!

Martin Vilcans
  • 1,137
  • 1
  • 8
  • 8
5

Python is not fully mature: the python 3.2 language at this moment in time has compatibility problems with most of the packages currently distributed (typically they are compatible with python 2.5). This is a big drawback which currently requires more development effort (find the package needed; verify compatibility; weigh choosing a not-as-good package which may be more compatible; take the best version, update it to 3.2 which could take days; then begin doing something useful).

Likely in mid-2012 this will be less of a drawback.

Note that I guess I got downvoted by a fan-boy. During a developer discussion our high level developer team reached the same conclusion though.

Maturity in one main sense means a team can use the technology and be very quickly up & running without hidden risks (including compatibility problems). 3rd party python packages and many apps do not work under 3.2 for the majority of the packages today. This creates more work of integration, testing, reimplementing the technology itself instead of solving the problem at hand == less mature technology.

Update for June 2013: Python 3 still has maturity problems. Every so often a team member will mention a package needed then say "except it is only for 2.6" (in some of these cases I've implemented a workaround via localhost socket to use the 2.6-only package with 2.6, and the rest of our tools stay with 3.2). Not even MoinMoin, the pure-python wiki, is written in Python 3.

4

Python's scoping is badly broken, which makes object-oriented programming in Python very awkward.

Mason Wheeler
  • 83,213
4

My gripes about Python:

  • Bolted-on OOP (See @mipadi's answer for elaboration on this)
  • Broken implementation of lambdas
  • Scope issues
  • No persistent collections in the standard library
  • Poor amenability to embedded DSLs
4

Access modifiers in Python are not enforcable - makes it difficult to write well structured, modularized code.

I suppose that's part of @Mason's broken scoping - a big problem in general with this language. For code that's supposed to be readable, it seems quite difficult to figure what can and should be in scope and what a value will be at any given point in time - I'm currently thinking of moving on from the Python language because of these drawbacks.

Just because "we're all consenting adults" doesn't mean that we don't make mistakes and don't work better within a strong structure, especially when working on complex projects - indentation and meaningless underscores don't seem to be sufficient.

Vector
  • 3,241
3

Multiple dispatch does not integrate well with the established single-dispatch type system and is not very performant.

Dynamic loading is a massive problem on parallel file systems where POSIX-like semantics lead to catastrophic slow-downs for metadata-intensive operations. I have colleagues that have burned a quarter million core-hours just getting Python (with numpy, mpi4py, petsc4py, and other extension modules) loaded on 65k cores. (The simulation delivered a significant new science results, so it was worth it, but it is a problem when more than a barrel of oil is burned to load Python once.) Inability to link statically has forced us to go to great contortions to get reasonable load times at scale, including patching libc-rtld to make dlopen perform collective file system access.

Jed
  • 141
3
  • quite a bunch of very mainstream 3rd party libraries and software that are widely used, are quite not pythonic. A few examples : soaplib, openerp, reportlab. Critique is out-of-scope, it's there, it's widely used, but it makes the python culture confusing ( it hurts the motto that says " There should be one-- and preferably only one --obvious way to do it "). Known pythonic successes ( such as django or trac ) seem to be the exception.
  • the potentially unlimited depth of abstraction of instance, class, metaclass is conceptually beautiful and unique. But to master it you have to deeply know the interpreter ( in which order python code is interpreted, etc. ). It's not widely known and used ( or used correctly ), while similar black magic such as C# generics, that is conceptually more convoluted ( IMHO ) seems more widely known and used, proportionally.
  • to get a good grasp of memory and threading model, you have to be quite experienced with python, because there's no comprehensive spec. You just know what works, maybe because you read the interpreter's sources or experienced quirks and discovered how to fix them. For instance, there are only strong or weak references, not the soft and phantom refs of java. Java has a thread for garbage collection while there is no formal answer about when garbage collection happens in python ; you can just observe that garbage collection doesn't happen if no python code is executed, and conclude it's probably happening sometimes when trying to allocate memory. Can be tricky when you don't know why a locked resource wasn't released ( my experience about that was mod_python in freeswitch ).

Anyhow, python is my main language for 4 years now. Being fanboys, elitists or monomaniacs is not a part of the python culture.

vincent
  • 101
3
  1. The performance is not good, but is improving with pypy,
  2. The GIL prevents the use of threading to speed up code, (although this is usually a premature optimization),
  3. It's only useful for application programming,

But it has some great redeeming features:

  1. It's perfect for RAD,
  2. It's easy to interface with C (and for C to embed a python interpreter),
  3. It's very readable,
  4. It's easy to learn,
  5. It's well documented,
  6. Batteries really are included, it's standard library is huge and pypi contains modules for practically everything,
  7. It has a healthy community.
3

I do favor python and the first disadvantage that comes to my mind is when commenting out a statement like if myTest(): then you must change the indentation of the whole executed block which you wouldn't have to do with C or Java. In fact in python instead of commenting out an if-clause instead I've started to comment it out this way: `if True:#myTest() so I won't also have to change the following code block. Since Java and C don't rely on indentation it makes commenting out statements easier with C and Java.

2
  • Strange OOP:
    • len(s) through __len__(self) and other "special methods"
    • extra special methods which could be derived from other special methods (__add__ and __iadd__ for + and +=)
    • self as first method parameter
    • you can forget to call base class constructor
    • no access modifiers (private, protected ...)
  • no constant definitions
  • no immutability for custom types
  • GIL
  • poor performance which leads to a mix of Python and C and troubles with builds (looking for C libs, platform dependencies ...)
  • bad documentation, especially in third party libs
  • incompatibility between Python 2.x and 3.x
  • poor code analysis tools (compared to what is offered for statically typed languages such as Java or C#)
deamon
  • 886
0

"Immutability" is not exactly it's strong point. AFAIK numbers, tuples and strings are immutable, everything else (i.e. objects) is mutable. Compare that to functional languages like Erlang or Haskell where everything is immutable (by default, at least).

However, Immutability really really shines with concurrency*, which is also not Python's strong point, so at least it's consequent.

(*= For the nitpickers: I mean concurrency which is at least partially parallel. I guess Python is ok with "single-threaded" concurrency, in which immutability is not as important. (Yes, FP-lovers, I know that immutability is great even without concurrency.))

Kosta
  • 181
0

I'd love to have explicitly parallel constructs. More often than not, when I write a list comprehension like

[ f(x) for x in lots_of_sx ]

I don't care the order in which the elements will be processed. Sometimes, I don't even care in which order they are returned.

Even if CPython can't do it well when my f is pure Python, behavior like this could be defined for other implementations to use.

rbanffy
  • 423
0

Python has no tail-call optimization, mostly for philosophical reasons. This means that tail-recursing on large structures can cost O(n) memory (because of the unnecessary stack that is kept) and will require you to rewrite the recursion as a loop to get O(1) memory.

Walter
  • 16,136
  • 8
  • 59
  • 95
a3nm
  • 101