What are the drawbacks of Python?

  • 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)

    I think that this is a helpful subjective question, and it would be a shame to close it.

    @FarmBoy - Well, I certanly tried to form it as such. Too bad the closer didn't leave a comment elaborating why he feels it is not constructive.

    @Xepoch: Every language I've ever used has whitespace as tokens in its grammar.

    @Xepoch - I ment - why he feels the question is not constructive.

    There seems to be a python-fanboy here who just downvotes all anti-python answers.

    @zvrba: You get two downvotes and resort to name calling. Don't worry, you're still net positive rep.

    @Roger: whitespace as tokens, or token delimiters? Most lexers I've worked with strip out whitespace and don't even pass it to the parser.

    @TMN: That's still treating the whitespace as tokens, just not returning them — and it's exactly what Python's grammar does too.

    @Roger: the convention on SO is to comment downvotes. Since this is a site for *subjective* opinions, I see no reason for downvotes, esp. w/o comments. So, I stand by my "name-calling".

    @zvrba: Downvotes still mean "not useful", just as always.

    What exactly is an "objective opinion"?

    @Brendan Long: I think in this case "objective opinion" is an opinion that acknowledges its own limitations.

    ahah, 2 years old thread revived by hackernews. The funny thing is that I tend to see many of the drawbacks listed here as a feature. Is my mind distorted ?

  • 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: "Nested functions kind of suck in that you can't modify variables in the outer scope". Do you mean that it doesn't have closures?

    Python does have closures.

    #2: You can modify variables in the outer scope by using the nonlocal statement in python 3. #3: Python simply has no value types in that sense. Even numbers are just immutable objects.

    Clarification: Python does have closures. The thing I don't like about them is that, if you have an outer function and an inner function, you can't use the inner function to modify variables local to the outer function. You only have read-only access.

    @Winston: Thanks for the note about Python 3. I was completely unaware of this, as one of my major reasons for using Python when I use it is library support. Needless to say, this means I still use Python 2.

    @dsimcha: The workaround for nonlocal in 2.x is annoying, but seamless: make the outer variable a list and always use var[0].

    @Casey, I have to disagree. The index is horrible - try looking up the `with` statement, or methods on a `list`. Anything covered in the tutorial is basically unsearchable. I have much better luck with Microsoft's documentation for C++.

    About 5 - just use pyflakes. It's written to catch exactly those errors.

    Regarding speed: with the rise of PyPy, many Python users will now be able to handle the speed problem just by using an interpreter with a built in JIT-compiler (for now, Python 3 users and users of C extension modules not handled by cpyext do not have this option).

    Regarding documentation: Are these comments related to the current Sphinx-based documentation? Or are they related to the pre-Sphinx documentation? The former seems unlikely, as those have excellent indices and search capability, but they would be perfectly understandable for the latter.

    The most valid comment in this set is number 4. Python is *not* a low level systems programming language. Far better to reach for an appropriate tool (such as C) and use your Python code to manage the higher level stuff.

    I despise the Python docs. They're prettier than most to be sure, but many times a lot of useful information is lumped into one page, like methods on strings and lists -- and all the sequence types are lumped together as well. When I search this information, I just land on a huge tome, and have to search down the page to find what I want. I also find the index on these pages hard to read, and it's sometimes difficult to tell which section I want.

    "Slow"? Slow at what? Compared to what?

    regarding 4 - you could always code performance critical segments in c/c++ and use it from python ..

    "Slow" - have you tried PyPy? Sure it's not a silver bullet, but it sorts out those "need another language" parts.

    How can distance from the metal be an argument? Did Python ever purport itself to be a systems language?

  • 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]
    

    Immutable variables can be done with class attributes -- see this article, at the beginning, the section "Bindings".

    I so so wish Python had a "var" statement. Besides the (very good) reason you state, it would also make it a lot easier to read the code because then you can just scan over the page to spot all the variable declarations.

    It is as if the python developers ignored the lessons of the past. Not declaring variables, not declaring functions, is a mistake first made in the 1950s. Those hard to find bugs that resulted from a hard to spot typo were amazingly enough first made in the 1950s. This language mistake has been made (and later corrected) time and time again. Declaring variables is not a huge burden. It has saved my butt multiple times. I inevitably `use strict;` and `use warnings;` in perl on a script of any size. Python has stripped the developer of far too many debugging aids.

    @David, To be fair to python, it will raise an exception if you try to access a variable which has not been assigned to. Many of the languages which lack declarations would return some sort of default value. As a result, Python's version is much less problematic then those ones.

    I can only imagine the features `let` and `var` with some kind of notation to enable strict checks otherwise you break *all* existing code. That notation could work on a function/class/module level to note that checks should be enforced.

    I can't edit, but `let y # Error: Declaration of read-only variable `x` without value` uses the wrong var (x should by y)

    @yi_H The proposal wasn’t meant to be backwards compatible – or even a real proposal. The question was, “what are the drawbacks of Python” … well, not having `var` and `let` (or a similar mechanism) is a drawback. Put differently: had I been the designer of Python, I would have done something like this. *That said*, future versions *could* include this when you load a special package (similar to `__future__`). Say, `import strict`. This won’t happen though, since it requires syntactical hacks …

    Is there any popular dynamic language out there which is using these strict checks? I think Perl is the only one, but gets less and less popular. PHP and Ruby doesn't do it and Javascript is the worst of all of them since it puts the non-declared variables into global scope, which can lead to very ugly bugs. I'm more or less satisfied with the rest (non JS) languages, if you ever write test-cases to your code (which you should do anyway) you will catch 99% of the typos. I'm not saying I wouldn't enjoy a strict mode for some tasks but it's probably a feature I can live without.

    @yi_H Not that I’m aware of. I’d use it in a heartbeat – well, not Perl since that is just FUBAR. I do use Perl but I don’t relish it. As for test cases: obviously these help but I hate writing manual tests for issues that the compiler should (and could!) catch for me. This is what a type system is there for, after all. My C++ projects have (near) 100% test coverage but I don’t have to write redundant tests to get rid of errors that the type system will catch.

    @David is right about declarations, but there is an argument that most languages that require those declarations go too far the other way. There are interesting middle-grounds in static typing, and some are certainly possible (don't know any implementation) for dynamic languages. But it's not that surprising that people rejecting one extreme jump straight to the opposite extreme.

    It's perhaps less disruptive to use const for read-only variables.

    @Seun I don’t think so. On the contrary.

    +1 For adding better 'functional-like' programming abilities.

    @WinstonEwert - Perhaps but then you're just hoping that the block of code which references that variable happens to be called and yo can catch it at runtime...

    @Basic, if your testing has never run that block of code chances are its broken in more ways than just having a misspelled variable name.

    @WinstonEwert So now we need to run the whole set of unit tests to find a typo...

    @Basic, the point of my comment was that raising an exception is way better than treating all undeclared variables as NULL. As you'll see in my comment, I described it as much less problematic. I was reacting to the previous comment that claimed that Python was just repeating the same mistake as older languages, which I think was over-simplifying the issue.

    @Basic, actually, it is possible to detect many of those typos without actually running the code. Pylint, pyflakes, and pychecker can all statically analyze code and detect those errors.

  • 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.

    +1 for pylint. I was unaware of it. Next time I do a project in Python, I'll try it out. Also, multithreading seems to work fine if you use Jython instead of the reference CPython implementation. OTOH Jython is somewhat slower than CPython, so this can partially defeat the purpose.

    Threading is not well supported? The threading libraries have been in there since before 2.1.

    I know there is threading support, but compared to Java or C, the GIL will really lower your performance. That is why the multiprocessing module is preferred over threading.

    The documentation is good if you can manage to find it. Googling Java classes is *much* easier than Python.

    @Casey I've clarified the wording in the answer, since threading is supported, just exhibits some weird performance (added a reference and a few links to docs too)

    This is really more a critique of the interpreter than the language. My understanding is that Jython bypasses the GIL and uses Java Threads, which typically are system threads.

    Nevertheless so many real world Python app has module dependencies that tie it to CPython and native CPython extensions, that JYthon is immaterial to real Python use. Does Mercurial run in Jython, for instance?

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

    This is correct, though there are tools like PyChecker which can check for errors a compiler in languages like C/Java would do.

    Dynamic typing is a conscious design decision, not a drawback.

    Its the same as saying Java's weakness is lack of dynamic typing.

    @missingfaktor, @MAK, *obviously* duck typing was an intended feature. But most design decisions introduce objective benefits and drawbacks. The added code flexibility is a benefit of dynamic typing, and the additional classes of potential runtime errors is a drawback. The subjective part is whether the feature is worth it.

    Lack of static typing introduces runtime errors?

    Lack of static typing makes it easier for *programmers to write code* that has runtime errors. In C#, `int foo = 4; Console.Write(foo.Length);` doesn't compile, so the error "Int32 doesn't have a property Length" cannot accidentally make its way into published software. In python, unless you run optional secondary tools to look for errors like that, code that accesses non-existing members of objects can go undetected until it ends up causing runtime errors.

    See this for a feature similar to "static types" for Python 3, and this for Python 2.

    This seems to be more a criticism of scripting languages in general. Haskell types work like Python's (you don't have to declare types), and it figures out what types are at compile-time (basically everything is generic unless you say otherwise). Even if Python made you declare types like C, you still wouldn't know if they were right until you try to run it.

    If a project's testing is so poor that `int foo = 4; Console.Write(foo.Length)` could make it into release versions if the compiler allowed it, then I **guarantee** that the project's released versions contain many many more bugs, whether or not it was written in a statically typed language.

    @Ben: That was a trivial example. What if the declaration was in one module and the use was in another, and this usage is a corner case that's only accessed under certain circumstances by one user? But that one user happens to be one of your biggest clients, and this functionality is critical to their workflow, and if you don't fix it your program will fail UAT? (Purely hypothetical, but based on plenty of real experience.) There's a reason why people don't tend to write large, serious programs in dynamic languages. Without a compiler and a static type system, they grow unmanageable quickly.

    @Mason Wheeler: There will always be bugs that no static analysis can catch. This is true in languages with very strong static type systems, such as Haskell and Mercury, and it's even more true in commonly used statically languages such as Java and C# that have somewhat weaker type systems. If you write serious code that needs to have serious assurance of reliability, you **must** test it. If your testing is not good enough to find the subset of bugs that a type-checking compiler would find, it is not good enough to give very good assurances that there aren't other kinds of bugs either.

    I will buy the argument that dynamic typing makes it take a bit more effort to find some bugs (although you could also argue that it's easier to debug in languages where you have more flexibility, so it's not necessarily true). But in practice projects written in dynamic languages are not more buggy than ones written in static languages. Poorly tested projects have many bugs whether the language is dynamically or statically typed.

    -1 runtime errors are a reality whether or not your language is statically-typed or dynamically-typed. The compiler itself only checks syntactic errors, for everything else there's unit testing. I would trade polymorphism, object boxing, OOP relationship abuse, and other related boilerplate (ie used to make square pegs fit in round holes) for duck typing any day.

    False, Evan. Compilers don't just check for syntactic errors; they'll also detect if you're accessing a member that doesn't exist (for example, if you mistyped the name of a property) or if you're attempting operations on a value that are not supported by the type (like trying to call something that's not a function). There's a huge class of errors that will never become runtime errors in statically-typed languages because the code won't compile. Don't deny this fact and downvote; as I said, it's arguable whether the reduced safety is worth the added power; some prefer it that way.

    @Brendan Long: I do not think your statement is correct: You cannot have type errors at runtime in Haskell. Either the compiler can figure out the type of an expression, or it can't. In the former case you will not have a runtime type error. In Python you can have type errors at runtime. Or did I understand it wrong?

    @Giorgio That's exactly my point. In compiled languages (like Haskell) you can get type errors at compile time, but scripting languages (like Python) don't have a "compile time" so you can only get type errors at runtime.

    But if you could declare variable types or function signatures you could also perform some static type checking I guess.

    If dynamic typing was such a hugely problematic feature, people would stop implementing it in scripting languages. The tradeoff is performance. That is all. Everything else you hear is abject cowardice on the part of devs who've never tried it a different way or who simply can't imagine what it means to actually pay attention to the flow of data through your app.

    @ErikReppen, performance isn't the only tradeoff. Having developed with Python, I know firsthand that development aids such as refactoring tools and code autocompletion are hampered because of the lack of static typing, and you also lack build-time type checking. Nobody's saying that dynamic typing is "hugely problematic." There are just language tradeoffs.

    I'd kill to have decent .Net-level intellisense in PyDev. Unfortunately, 99% of the time, PyDev doesn't know what type it's dealing with (and even if it does, what properties/methods that type may have are impossible to determine) which means I spend an inrdinate amount of time remembering/typing names ala `customer.addresses.objects.`. And @ErikReppen Smooth argument - people who disagree with you must be cowards

    @Basic Fair enough. I was grumpy at our codebase at the time. But the problem, IMO, is the way people think they're supposed to write code. I don't have your problem when I make the switch to static and I don't typically run into a lot of type issues. People can and do write complex large-scale web apps with dynamic languages and they don't have to rethink the way they write code to do the same in static languages. If static types protect you from anything they protect from learning how not to write code that makes you paranoid about your data being touched by 10,000 different sets of hands.

  • 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.

    The self parameter is merely making explicit what other languages leave implicit. Those languages clearly have a "self" parameter.

    @Roger Pate: Yes, but that explicit need for "self" is kind of annoying (and, I would argue, a leaky abstraction). It also didn't come about as a deliberate design decision, but due to Python's "weird" scoping rules. I can't find the article quickly, but there's a email posting from Guido van Rossum that explains nicely why the "self" parameter is required.

    @mipadi: It's less leaky than you might think; in some ways it's more consistent. A few extreme examples of that in the previous comment's link. Compare to the attitude in C++ that free functions are extension methods (I can search for the blog post from a prominent programmer about that, if you like) and how extension methods (as a formal feature) in some other languages have a fixed first parameter of "this".

    @Roger Pate: In object-oriented languages, passing the target as the first parameter could still be considered an implementation detail. My point, though, isn't whether it's a good idea or not; the point is that in Python, it's *not* due to a conscious design decision, but rather to work around warts in the scoping system.

    @mipadi: This question is asking about drawbacks, if your point isn't that it's a bad idea, what is the purpose of this answer? Perhaps that's what I was feeling from your answer initially (and why I downvoted): you aren't justifying why this is a bad idea because you apparently never intended it that way?

    @Roger Pate: I expressly said in my answer that the need to pass `self` as the first parameter was a *symptom* that Python's OOP side wasn't well-planned, and that it was due to Python's somewhat-broken scoping rules. (For the record, I *don't* think explicitly passing `self` is a good idea, but that's not the point.) I also elaborated in my answer that it's use is not a design decision, but due to Python's scoping rules. Not sure how I could be more clear.

    @mipadi: The update has better reasoning (so I'll remove the downvote), but if you view len as an *operator* which you overload, it's more OO in Python. Would love to see an example or reasoning on how Python gets message-passing wrong.

    I believe http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html is the article that is being referred to. The issue essentially is that without variable decelerations its impossible to know where a value should be stored on assignment. So it is certainly true that this was produced by the design of the rest of the language. I don't see that as a bad thing, simply the fact that language design has trade offs. (Although in this case I think the explicitness is a gain not a loss)

    Explicit self is an outgrowth of the fact that methods are just functions (and, as Winston noted, implicit local variable declarations). You're free to not *like* that design decision, but calling OOP "bolted on" in a language where *everything* is accessible as an object at runtime is silly.

    In Python, everything is an object. `object` is an object. `type` is an object. When you declare a class, that class is itself an object, and is an instance of a class! I can't think of a language with a more complete "everything is an object" world view.

    @Ben: Well, there is, for example, Ruby, in which everything is not only an object, but, unlike Python, the syntax for invoking methods is consistent.

    @mipadi: everything except functions.... Which brings about the necessity of procs, blocks and lambdas, which imo is Ruby's biggest wart.

    This is likely the top or close to top answer. Threading would be the only thing I'd place as a competing complaint.

  • 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).

    Regarding (2), I think this is offset by the possibility to have nested functions.

    @KonradRudolph My main qualm with nested functions instead of multi-line lambdas is the reading order gets swapped.

    Regarding (3), what about input() (called raw_input in python 2), and sys.stdin?

    @CookieOfFortune: OTOH, if you make the inline lambda a full-blown function, it can be unit-tested and reused elsewhere. If you need complicated lambdas, they may end up biting you later.

    @rbanffy Sure, but it would just be nice to have the option of a middle ground.

    4) unicode seems so much easier in Python than every other language I've worked with, which languages offer unicode by default, and doesn't it get in the way when most of your strings are simple byte arrays anyway? Isn't it easier to have to explicitly use unicode vs having to explicitly use byte arrays?

    @wkschwartz: `raw_input` and 'sys.stdin' are pretty barebones. They don't support getting formatted input (e.g. something like "%d:%d:%d" % (hour, minute, sec) to read in time). So far Python does not have anything approaching the functionality of scanf(in C) or Scanner(Java).

    @limscoder: All strings are unicode by default in Java. I don't see a good reason to have separate str and unicode classes. IMHO, strings and arrays of bytes should _not_ be represented by the same abstraction. A string class should be for storing and manipulating text - whose internal representation we don't really care about. We should not want to do things like truncate/replace/delete/insert at a specific byte within a string - we want to do this at a specific _character_. Its easy to forget the distinction and have your code blow up when fed non-english input.

    @limscoder: if you want to see easy unicode, try Tcl. I had to switch from Tcl to Python a few years back, and boy was I surprised at how primitive python's unicode support is in comparrison. It's truly invisible in Tcl, and a major pain in python.

    for the things your answer states it is too short to the point of being misleading while not being entirely incorrect. C++/Java are better than Python for string parsing? wtf.

    @J.F.Sebastian: I think my answer states clearly what is lacking here. Python does not provide something as simple as scanf for reading formatted text. Where exactly is this misleading?

    Python is not C. You could use string manipulations, regular expressions, specialized parsers out of the box. There are several modules that provide scanf-like functionality but none are popular. So technically you're correct: there is no scanf in stdlib but it is misleading to suggest that it makes the input handling in Python more complex or less powerful compared to the alternatives.

    @J.F.Sebastian: It certainly isn't less powerful, but I disagree that it isn't more complex. My point is, having something like Scanner or scanf would make life simpler when reading simple formatted input - not saying it is impossible or even too hard to do this in Python right now.

  • 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.

    I see lots of complaints about this, but why does people insist having an empty list (that the function modifies) as a default argument? Is this really such a big problem? I.e., is this a *real* problem?

    It violates the principle of least surprise. One wouldn't expect a function's parameters to survive across calls.

    It's a consequence of it being a scripting language. You will only get stumped by this bug ONCE, and never again. Figuring this bug out for yourself really gives you a kick in the butt to remind you that yes, this is still a scripting language. And that's only because the language is that good in hiding the scripting aspect (assuming you use it correctly).

    @ZoranPavlovic out of curiosity, why is this a consequence of it being a scripting language? It seems to be a problem with when the data is bound and because lists are mutable (which are two things that are normally good, but end up being bad when combined together). The same issue could happen in a non-scripting language if you bound the data at the time of function creation rather than creating a new list each time the function was called.

    @aib: I don't think so -- the parameter here, like every other Python object -- is a pointer to an object. In this case, the object is a mutable one, and the variable is bound when the function is declared. The parameter does "survive across calls," but what survives is the reference to a mutable object.

    @PatrickCollins: I'm aware it's the reference that survives, but the important thing is the end result. // PHP has a similar problem with reference `foreach`es, also resulting in subtle bugs.

  • 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).

    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.

    regarding the scoping, it might worth it for the curious to look at http://www.python.org/dev/peps/pep-3104/ to understand the reasoning of the current method.

    Agree with +1. So, +1.

    +1, except that the strait-jacket isn't as tight as it used to be.

    Having one way to do it is an advantage. When you read someone else's code you don't have decipher ever single statement. Once the idioms are hardwired in your brain, you should have instant recognition.

    Completely agree with @rox0r. The "straight-jacket" prevents all sorts of syntax wars.

    To be honest, I very rarely need the `global` or `nonlocal` keywords in Python. So rarely that I forget this issue exists and have to re-google it the few times it's come up, despite the fact that I write Python code every day at work. To me, code that needs to modify global variables (or worse, outer non-global variables) is a code smell. There's usually (not always) a better way.

    -1: #1: IMO, that's an advantage. #2: I don't see a problem with this; like Ben, I think there are almost always better ways. #3: Python docs aren't perfect, but I can find what I need. #4: That has never really been true, but the Python philosophy does suggest that there should be one *best* way to do things; IMO, this is better than having a dozen different ways to do something and then feeling lost when you look at someone else's code in that language.

    #4:Not true. If anything, python gives you a multitude of ways to solve a lot of problems, at any level, be it low or high. It may be a scripting language, but that doesn't mean it in any way restricts you. As for #2: I think you need to learn OOP, because if you ever have to use the global keyword, you're not programming right. Using global is a very bad code-smell as Ben above mentions.

    @GreemMatt, #1 is absolutely a disadvantage! It becomes silly to replace Perl with Python to do quick and dirty shell tasks. You can't have multiple commands in one line in Python. Really annoying and limiting.

  • 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.

    yeah... I really miss LINQ style (I'm sure LINQ isn't the first to implement it, but I'm most familiar with it) list handling.

    Don't think of len(x) as a method. "len" is a function. Python has functions *and* methods and I see nothing wrong with that approach. Lack of proper functions is, usually, the source of a lot of needless typing.

    I know `len()` is a function, and what the advantages are. I also stated why I think it's a bad idea, why I think the **global** functions are a particularly bad idea, and why i think methods provide a convenient method of organizing and scoping your functionality =)

    I don't think that 42 (or is it 43?) keywords is a 'large' number. That also includes things like `def`, `class` and other non-function calls. Compare that with 100+ in most other popular languages. Also, consider the line from `import this`: `Namespaces are one honking great idea -- let's do more of those!`. I think you might misunderstand Python namespaces ;)

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM