What are the drawbacks of Python?
programmers.stackexchange.com
programmers.stackexchange.com
The speed issue is a significant one, but not usually for the reasons given. The big drawback of being slow means a lots of its implementation is not written in python.
IDE/debugger is another issue, although some of it is a matter of people not knowing the tool. Especially on bad codebases, even something as simple as pyflakes running in the background is invaluable, and many people don't use this.
Exactly. Dynamic typing can provide a temporary fig leaf for bad design in ways where static type checking would be less forgiving. Doesn't make dynamic typing wrong, it just means that bad programmers can get in over their heads more completely and more quickly.
This can be a problem in static languages with good type inference, too. When reading code in older static languages like C++, you'll find a bunch of variables and types declared, but powerful and concise languages such as Scala have much less use for intermediate variables, and type inference means that intermediate variables are almost never declared with a type. Field types and method return types often aren't declared either. I don't know if this is a good thing because it forces you to read a lot of code, or if it's a bad thing because it forces you to read many unrelated bits of code when you're trying to get something simple done.
Python is dynamically typed but again weakly.
ML is strongly and dynamically typed.
Anything that treats anything as a string or automatically casts from strings fills me with terror for general programming.
int x = 42;
printf("%c\n", *(char *) &x);
that is, access the underlying bits which represent a value as if they were of some completely different type. The results are deliberately not standardized and vary with your machine. Python doesn't provide any way to do this; at worst you get a TypeError for a conversion that isn't implemented, or AttributeError for a slot that an object doesn't have.Implicitly parsing strings to produce numbers isn't weak typing, merely reckless and prone to mistakes. Weak typing can corrupt the state of your program so severely that even correct code begins to malfunction.
This, in and of itself, means absolutely nothing. And neither does:
> Implicitly parsing strings to produce numbers isn't weak typing
There is no such thing as a widely accepted, let alone formal, definition for a strong/weak axis, every other guy out there has his own personal definition. What the bloody hell is the point of words for which no two persons share the same definition? No point. None at all. See comment above, it merely marks you as a guy without a clue.
What makes python seem weakly typed to many people is its implicit variable shadowing. So when you do a="hello", a is a str, and can only be used where a str is expected. However, python lets you do: >a="hello" >a=1
at which point a is shadowed to an int.
Dynamic typing has some (not all) of the problems weak typing has, but they are not the same thing
These words, they don't mean jack shit.
> Dynamic typing has some (not all) of the problems weak typing has, but they are not the same thing
Well of course they aren't, for starters "dynamic typing" actually means something.
It might help you to do research, ask clarifying questions, or explain why you think someone is wrong; instead of assuming that anyone saying something you disagree with has no idea what they are talking about.
[0]http://wiki.python.org/moin/Why%20is%20Python%20a%20dynamic%...
When it comes to that precise subject, I happen to have already done so and to know the count rather well. Stupidity is stupidity, even if it's on the python.org wiki.
> or explain why you think someone is wrong
I did so. Twice if not thrice. There is no well-defined and shared — let alone formal — semantics (and meaning) to the strong/weak "axis", every other person has his own pet definition of it[0] and the definitions are quite often incompatible if not downright opposed.
It is therefore garbage and utterly useless to communication.
> instead of assuming that anyone saying something you disagree with has no idea what they are talking about.
It's not "anyone saying something [I] disagree with", just "anyone using the strong/weak axis as if it actually existed".
[0] From "no memory corruption" to "no possible runtime error" through "no UB", "dynamically typed" and all sorts of takes on "no implicit conversions", usually in order to declare one's pet language is "strong" and somebody else's is "weak". I've seen all of C, C++, Python, Javascript, Perl, Java, Tcl, Lua, Smalltalk or Pascal declared both strong and weak by various (usually different) people.
Is it not the case that in Lisp people tend to use values — lists of (probably immutable) data — rather than objects? Objects are in and of themselves their own flavor of custom, specialized language for interacting with a data structure. In Lisp, the language for interacting with lists of lists or what have you is, well, Lisp.
IME duck typing is not a real answer in this situation; that helps when you are programming to an interface in the standard library, or in an API you are using. Otherwise I end up hunting down information about a data type through N layers of method and function calls. Not a good time. :( This actually drove me towards Go as a language with comparable productivity but with a type checker, et al.
I'm not sure where Lisp macros fit into this, if they do. I expect they have a tendency to boil down frequently repeated patterns into something very compact and less error-prone.
As for IDE support, Rob Pike suggested (not in so many words) that if you need an IDE for your programming language, it suggests that something's probably wrong with that language. He's not disputing that people genuinely need them, because yes, jesus h, I really do need Eclipse when you're programming against a reasonably large API. I think I find this line of reasoning more convincing than I would have, say, a year or two ago.
Anyway, for my part, ST2 was enough of a Python IDE for me. And thanks for the tip about Pyflakes, by the way. There's guard[1], which I figured you could rig up with python -c or whatever the flag is to compile. Pyflakes looks easier.
Look up flake8, it runs both pyflakes and pep8, and can optionally warn about cyclomatic complexity as well.
One thing that is clearly overused in python code is dictionaries to pass data where the set of keys is fixed and known, or worse passing dict with different set of keys instead of proper 'modelling'. Things like traits (http://code.enthought.com/projects/traits - disclaimer, I work for Enthought ), or even simple libraries like remoteobjects (https://github.com/saymedia/remoteobjects) can be helpful in those cases.
I myself don't use IDE much: I am too entrenched with vim to gain enough from an IDE, although pycharm is quite nice. I would much rather see libraries around things IDE do ('live' 'AST' of a whole project, good refactoring, debugging) so that one can integrate them in whatever environment people use. I think we are going there (https://groups.google.com/forum/?fromgroups#!forum/python-st...).
Go does look nice, but it misses two killer features for me: NumPy and the whole scipy ecosystem, and C integration (in both directions). I think the python community itself does not realize how big and significant the scipy ecosystem is (nothing comes anywhere near in any common language AFAIK).
Though I generally agree, that's as long as you mandate linting. JS's implicit globals are even worse than Python's implicit scoping.
On the other hand, you can work around that particularly stupidity just by being careful. Working around the Python limitations requires either something hacky like using a one-element list instead of a variable or (only in 3, I think) a rather ugly "nonlocal" statement at the top of your scope.
http://mail.python.org/pipermail/python-3000/2006-October/00...
def demo (n):
def multiplier (m):
return n * m
return multiplier
x5 = demo(5);
print x5(4)
You can do anything in the inner closure that you might want. The only surprise is scoping - if you want to modify state you need to use a mutable data type because otherwise you'll be instantiating a private variable. But that hoop is easy to handle. def demo ():
counter = [0]
def increment ():
counter[0] += 1
def peek ():
return counter[0]
return (increment, peek)
(increment1, peek1) = demo()
(increment2, peek2) = demo()
increment1()
increment1()
increment2()
print peek1()
print peek2()
So there. Function literals. They work.But, you say, you really want anonymous classes? Well look up metaprogramming then. Python really has quite good support for it. There is no trouble creating anonymous classes on the fly with their own attributes, functions, and so on. They can even inherit from each other. http://www.voidspace.org.uk/python/articles/metaclasses.shtm... might be a good starting place on this for you.
(Note, I do not recommend metaprogramming unless the solution you make will be reused a lot.)
lambdas. Your example isn't a literal, it's just a declared function named "multiplier". Sure, it has equivalent functionality, in the same sense that Python could remove collection literals like [1,2,3] and you could still create lists and maps by hand. But that would be silly.
even if you have to do a bit of work to get them
And that work means that useful techniques become too unwieldy to use in practice.
- Broken scoping by default (the thread does raise this)
- Unicode is very painful in python2 (addressed very well by python3)
- Duck typing sometimes prevents early detection of genuine programming mistakes because of overuse: False + 1 == True
- No good isolation tools (virtualenv, the most common tool, silently fails to isolate dependencies when moved, which is obviously makes it tough to deploy using it)
- Poor packaging tools (only a few tools than they all have serious limitations such as not being able to package c-extensions).
- Some of the standard library is downright terrible. csv and robotsparser are good examples. This has the pernicious effect of widely-distributing libraries that have subtle but serious drawbacks.
Python stdlib will let you package a C extension. I am not sure why you think the opposite. http://docs.python.org/extending/index.html
> No good isolation tools (virtualenv, the most common tool, silently fails to isolate dependencies when moved)
If you mean it's not relocatable, there's a new option for this http://www.virtualenv.org/en/latest/index.html#making-enviro...
But being not relocatable is not really an issue, as you can simply re-run virtualenv in the new place. All libs you previously installed will work without having to install them again
> Some of the standard library is downright terrible. csv and robotsparser are good examples. This has the side effect of widely-distributing libraries that have subtle but serious drawbacks.
Some stuff in the stdlib are not good that's true, but we have a gigantic library ecosystem at pypi.python.org will very well written piece of libs. Using Python seriously, I have never ever felt the serious drawbacks you are talking about except with tarlib in python 2.4 which was seriously broken.
But c-extensions aren't put into the same package so you still have to include the shared object files yourself so it's not really a package. One of the best things about the JVM's jar is that you can pack all your other jars into one jar for deployment.
> If you mean it's not relocatable, there's a new option for this: (--relocatable)
"In theory virtualenvs have a --relocatable flag but that one is heavily broken and conceptionally can't work properly because it uses the system Python interpreter to switch to the intended environment."
http://lucumr.pocoo.org/2012/6/22/hate-hate-hate-everywhere/
> pypi.python.org
Yes, it's great, but I'm talking about the standard library. I really wish some of the worse parts of it would be improved or removed.
The stdlib. It has some great parts and overall is just good enough (and desire to not break everyone's code) to not cause revolt and wholesale replacement.
FTPlib is atrocious.
httplib/urlib mess -> requests
os os.path, shutil subprocess is a ridiculous mish-mash of legacy, obscurity and surprising behavior. One example. os.mkdir (but only the leaf, and exception if it exists), want mkdir -p behavior. Is that an option to os.mkdir, fuck no! We need a whole new function for such dramatically different feature. Must be called mkdirs, fuck no! That would be too easy to remember and no one every look at our beautiful docs. os.makedirs is the obvious choice.
Not only does it make bindings awkward (and sometimes broken, in different ways depending on the way "scope inference" is implemented) and tends to hide errors (e.g. typo) or generate them much later than possible (even though lexical checking is very cheap to perform at parse time, with explicit scopes), it has pretty much no actual benefit I can think of.
Especially in Python where assignment is a statement, not an expression, so it's not like explicit scoping would prevent putting assignments in place they're currently allowed.
[0] and it's one of the things I find utterly retarded in coffeescript.
import sys
module = sys.modules[__name__]
def foo(k,v):
setattr(module, k, v)
foo('spam','eggs')
assert(spam == 'eggs')I don't consider that working, it's a piling up of hacks. Which is great when you need them and they work, but...
Also, there's a simpler way to implement your snippet:
globals().__setitem__('spam','eggs')2nd choice: makedir(force=False)
3rd choice: makedir() and makedirs()
Never ever would I want/expect two related functions one abbreviated one not. Yes, I know history of why it's called mkdir. Should learn from history, not repeat it.
I think it would be more accurate to say that if you learn other languages, you'll learn not to hate Python. Python is kind of mediocre in a lot of ways, but it's the only language without any deal breakers.
Interesting thought. Can you elaborate?
You can't write command line tools in any JVM language because of that long startup and warmup period, and you're always going to suffer from excessive memory consumption.
Also, lack of easy access to C/C++ code is problematic for some types of software.
And if you’re working on anything that needs to do a small job quickly, such as command line tools as fauigerzigerk mentioned, the overhead for something like the JVM will almost certainly be prohibitive because of the delay at start-up.
The main downside of Python compared to, Ruby, for example, is that Guido made some serious mistakes in the initial design and in the process of fixing them added a lot of extra complexity to the language. Python doesn't cleanly "fit in the head" the way Ruby or JavaScript do, at least for me.
But instead of wasting time micro-optimizing in such a narrow language space, pick Python or Ruby or whatever and spend your extra cycles on something different enough to be worthwhile like Go or Haskell.
Guido always said he could only afford to break python once but it's not clear to me he'll even get away with once.
I find the severe breakages and insanities of Javascript (such as tracking what `this` is) take much more brain-space than Python ever did, personally.
When going from Python to Haskell, or Ruby to Erlang, you can't get confused between the two.
Learning easy things and getting used to them makes learning difficult things tougher later.
So I don't understand your comment about it being a narrow language space.
As well as blocks not being first-class objects. Especially knowing a smattering of Smalltalk, that still feels weird.
I've been replacing my shell scripts with Haskell scrips lately and the transition is going well. Soon the only two languages I will ever need are Haskell and elisp!
I imagine the order on which you learn is important. I learned OOP with Smalltalk and C++ not only didn't fit in my head, it created an impressive allergic reaction. It took me 8 years to be able to get back to it and finally learn.
And it still feels a horrible kludge.
I learned Python before I had contact with Ruby and I find the irregular syntax hard to parse.
In retrospective, I feel the python as a language was a good choice, it helped us move very fast and do a lot of things :
- One good pythonner is very productive
- It was easier to get someone aboard, it is easy to get python and to reasonnably efficient with it.
- there are libraries for everything.
- going to cython or c++ was always an easy alternative for those crtical part of the code.
The only cases where python can be problematic are :
- high reliability code like trains or planes software, because python code is not provable.
- very high performance, where the slowness of python in the not critical part of code can be a problem.
Besides, It doesn't protect you against the classic problems of a big codebase built by a different of people over a period of time. Tools like pynocle or pylint were good for this.
So, in the end, python has obvious weakness. Most of the time It seems like a solid choice (among others). Right tools for the job, etc.
[EDIT: Turns out it's because all built-in constants are capitalized: http://stackoverflow.com/questions/521476/why-true-false-is-... . Learn something new every day. Still don't like it though.]
Also object-oriented programming in Python is very awkward. Then again, if I wanted OOP, I'd use C#.
So yeah, I agree, python's OOP is kind of awkward. I love the ease of composition though, something I use extensively even in other languages since I know python.
No, it does not.
something like
class Foo:
bar = 1
means that bar is now a static class member (static in Java's sense). It needs to be assigned to "1" explicitly in __init__ as far as I understand. That's just unintuitive and leads to lots of boilerplate, just so that you don't need specifiers like 'static'. Sometimes the fear of verbosity gets in the way of intuitiveness.I don't think so. You do have to assign anything to it if you want to use it as a 'static' (actually 'class' attribute). You can access bar from Foo, as in Foo.bar or from an instance of Foo as in foo=Foo(); foo.bar.
What probably is confusing to you is if you assign a value bar in an instance then that gets assigned to the instance not the class. That is if you do foo.bar=100 then now foo instance has an object attribute called bar=100 that overrides Foo.bar=1. But Foo.bar still stays at 1.
There's no default anything, what you wrote sets "bar = 1" on the class itself, nothing more an nothing less, just as if you'd written:
Foo = type("Foo", (object,), {'bar': 1})
(in fact that's pretty much what the class statement is sugar for)If you want to set it on the instance, set it on the instance.
Python's class context is a namespace like every other, you can do any computation you wish in there, and at the end the ``class`` statements collects all bindings of the namespace and sets them on the class.
Of course it will, Python's attribute-resolution (as do most languages) tries the instance, then the class, then the superclass, then... why wouldn't it work?
> I just remembered there to be some case where it didn't behave the way I expected it to (e.g. instance member assignment overriding the content of another instance)
Only two way I see for that to happen:
1. Re-setting the member on the class itself, not the instance
2. Or having the member be mutable and mutating it, since it's set on the class it's shared by all instances.
class Foo:
bar = [1]
foo1 = Foo()
foo2 = Foo()
foo2.bar.append(2)
print foo1.bar
print foo2.bar
prints
[1, 2]
[1, 2]And that's the reason why I don't like that the standard way I define common instance members is an init function. Are there any decorators I could use to do that outside of any class function?
(no, I don't want to call [0,1,3].__len__() . Why would I want to add all of those underscores.)
Because they don't depend on a given class, they're protocols. There's no "master class" in which to put them, so they go in some sort of bastardized CLOS-like generic method instead.
''.join([1, 2, 3])By the way, your example has an error, since you can't join a list of numbers. You can only join lists of strings. I.e.
' '.join("Hello", "World")string.join(['1','2','3'], ' ') == ' '.join(['1','2','3'])
If there is a valid reason for this, it would go a long way in explaining away one of the major reasons I dislike python.
Edit: Appears to be a bug and I cannot respond to your post 'masklinn'. Can you rework the example to include the result as 42 being an instance variable instead? Thanks.
This declaration makes no sense.
> This really means there isn't precisely methods at all
Of course there is:
>>> class Foo(object):
... def bar(self): return 42
...
>>> type(Foo().bar)
<type 'instancemethod'>
>>> m = Foo().bar
>>> m()
42
> If there is a valid reason for this, it would go a long way in explaining away one of the major reasons I dislike python.I don't know (and don't really care) if you'll consider it "a valid reason", but here's GvR's reasoning: http://neopythonic.blogspot.be/2008/10/why-explicit-self-has...
> instance.method(instance)
Borrowing masklinn's example:
>>> class Foo(object):
... def __init__(self):
... self.n = 42
... def bar(self):
... return self.n
...
>>> f = Foo()
>>> f.bar()
42 f = Foo()
Foo.bar(f)
note that it will typecheck the first argument to ensure it's an instance of `Foo`, it's not a function, it's an unbound method: >>> class Bar(object): pass
...
>>> b = Bar()
>>> b.n = 66
>>> Foo.bar(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method bar() must be called with Foo instance as first argument (got Bar instance instead)I had to extract that as a concise summation of my subjective view of the language as a whole. (Note: hacked != bad.)
Having to specify self or cls as the first argument of an instance or class method. This one is weird when you are learning Python because you don't pass in the first variable but it gets magically inserted for you.
Creating a class method using a method decorator. Using a decorator to do something like this seems like a hack to me.
Not having a clean way to specify what all the instance variables are outside of the constructor. People with a java background are used to putting instance variable declarations as the first thing inside a class definition. In Python, these will be static variables. This seems weird and inconsistent with the how method declarations work (methods only become class methods when a decorator is used).
BTW, I like Python.
That's been changed in Python 3. Although it brings other issues.
> Creating a class method using a method decorator. Using a decorator to do something like this seems like a hack to me.
Why? Even more so, why is a decorator more of a hack than adding a new keyword (java) or special-casing method declaration (ruby) itself? Note that you can also create class methods without decorators if you wish to, by using a metaclass (I believe the method won't be accessible from the instance though)
> This seems weird and inconsistent with the how method declarations work.
It's actually very coherent: every binding created in the class scope is collected and set on the class object, as if passed as the third argument to `type`. That's it. Some objects (functions) are processed a bit more, but they still end up in the same place.
> methods only become class methods when a decorator is used
That's because the decorator is basically a flag to tell the attribute-resolution process to handle this method differently from the normal method-resolution process.
class Foo(object):
def class_method(Foo):
pass
then suddenly explicit self gains a meaning (Hey! Instance method!) and class method declaration doesn't go through a completely unrelated mechanism.>> methods only become class methods when a decorator is used
> That's because the decorator is basically a flag to tell the attribute-resolution process to handle this method differently from the normal method-resolution process.
Do you see how that's just special-casing method declaration in a similar way to Ruby's mechanism, just relying on the programmer to drive the mechanism himself? To a not-so-casual observer it looks like Guido wasn't willing or able to change enough semantics to make a syntactic fix work for class method declaration, despite there being an obvious space in the syntax for it to fit in, so lent on a convenient implementation detail - a hack - to get the same effect with more work for the programmer.
Of course, it's equally possible that he believes that class methods are a code smell and should be explicitly made ugly, which I could have a certain sympathy for, but to me the current situation just looks really inelegant.
No, actually, there have been situations in the past where using `self` was not the best choice, and I didn't. Using `self` is extremely confusing when defining methods on metaclasses for instance (self turns out to be a class object)
> If you could declare a class method like this:
You completely change the semantics of defining methods and potentially break explicit code.
> Do you see how that's just special-casing method declaration in a similar way to Ruby's mechanism, just relying on the programmer to drive the mechanism himself?
The programmer is always the driver, the difference is that decorators allow doing this without adding a pointless syntactic special case.
Here's somewhere that Python could have made things easier for you. What's wrong with `self` being a class object? It's something Ruby gets right, in my opinion.
> You completely change the semantics of defining methods and potentially break explicit code.
Yep. In a good way. Guido had a chance to fix this at Python 3, and didn't. Do you seriously not see the benefits of the code I posted?
> The programmer is always the driver, the difference is that decorators allow doing this without adding a pointless syntactic special case.
Yes, the programmer's always the driver, but my point is that in Python the programmer has more driving to do than necessary. The reason this is confusing and inelegant is precisely because it's not a pointless case. Decorators allow doing it, but they're absolutely not the best way, its a (ab)use of a conceptually unrelated mechanic.
Which is not just reductionist, but downright silly. C# is not a very good OO language, in the same way Java isn't.
When I want OOP, I play with Smalltalk. Or Self.
I'm reasonably sure there was an implicit "and be productive" in there. Smalltalk and Self are neat, but they don't do well on the get shit done test.
(Yes, I know it is possible to get shit done in those. Compare the difficulty of getting-shit-done in them to an environment like Java or C#, and an unbiased source is going to have to admit they fail pretty hard if only because of stuff like Maven or, to a lesser extent, NuGet.)
Right, let's move the goalposts and reduce the target, was there also the implicit assumption that the language should have a C-style syntax, be statically typed, run on a JITted runtime and be maintained by a big company?
If hopping up and down and yelling about your pet likes when they don't really fit the topic is your bag, more power to you, but...
No, I think we can exclude that one with no issue at all.
Sending messages are method calls, the difference is mostly contextual. Blocks are first-class functions with closures and nonlocal returns. That part's important when you're trying to implement e.g. loops (otherwise you have to add flags or throw exceptions, as in Javascript).
In Smalltalk, iteration is a message and a block:
aCollection do: [item|
"process item"
]
but that's not exactly rare these days. On the other hand, a conditional is a message and two blocks: aBoolean ifTrue: [ "code if true" ] ifFalse: [ "code if false" ]
A repeat loop is a message to a block on a block: [ "dynamic condition" ] whileTrue: [ "repeat while true" ]
and so's a try/finally: [ "a piece of code which may blow up" ] ensure: [
"error recovery code" ]
or a try/catch: [ "a piece of code which may blow up]
on: SomeError
do: [ :sig| "error handling" ]
What this means is that there are very few special cases in the language, and the user of the language has the same power of expression as the designer of the language (much as in Lisps or Forths), the only limit is the hooks into the runtime but that's about it.Also, message cascading is a minor but way cool (and pretty much unique) feature of Smalltalk, it lets you have your cake (return interesting values from methods) and eat it too (chain calls):
someObject
aMessage
; anOtherMessage
; yetAnOther
; andOneMore
will send all 4 messages to the same `someObject` no matter what the previous message returns.That tells you what the language's priorities are.
You should have no trouble looking up what a "trampoline" is, but I'm abusing the term a bit.
There is a pattern for single-stepping using similar techniques: "user mode single stepping".
One thing to bear in mind: it was easy for us to do this in part because crashing the target program was not a big deal for us.
I really wish people liked OCaml or Haskell that much instead.
Really the main strength of Python is not the language at all, but the huge number of libraries written for it, and the wealth of tools and "community." Fair enough, it was in the right place, at the right time, but if history is going to throw its weight behind something, it is a little annoying when that thing is rather mediocre...
Something I wish python had a better support would be truly anonymous function I.e. you can't do:
lambda x: raise "foo"
It's also very hard to write nested callback (Some pythonists like to argue that is actually a good thing).Lastly, I wish the functional paradigm part of python was more consistent.
But all in all, I love python and I find it very hard to switch to something new without missing it.
It's an interesting read - he begins with "The problem is that it is not as good as it is made out to be, in other words it suffers from a degree of hype. I'll try to argue this point."
- usually constants are capitalized, but math.pi and math.e aren't
- the Decimal class has methods like is_nan(), and generally puts underscores in the method names, while the math package provides methods like isnan()
- even within the same package, you have cases like itertools.groupby() and itertools.combinations_with_replacement()
Not a deal breaker, it's just a drag when you only work in a language once every few months, and you constantly have to look at the docs to know whether something is alllowercase, ALLCAPS, camelCased, with_underscores, etc.
- Unicode (at least for Python 2)
- Debugging is a little behind other languages (pdb works)
- removal of map/filter in place of list comprehensions (they are certainly better but not faster)
So do the numerous extensions/replacements for pdb, or the multiple graphical debuggers (e.g. PyCharm's, PyDev's, PuDB, WinPDB, Kodomo IDE's, Wing's, ...). Python doesn't have a time-traveling debugger à la OCaml, but that's about it as far as I can tell.
> removal of map/filter in place of list comprehensions (they are certainly better but not faster)
What are you talking about exactly? Because I find two possible interpretations of that phrase, and neither comes close to being correct.
My test case is: f = [x^2 for x in l if (x % 2) == 0] (and something similar with map and filter)
(where you read x^2 please consider x(double asterisk)2)
For 500k elements - xrange(500000) - this takes: 0.122527122498 (list comprehension) 0.145442008972 (map/filter)
list comprehensions generalize to other types (generator expressions (aka "lazy sequences"), set comprehensions, dict compresions)
Compare this syntax (using dict comprehensions in 2.7):
{k:v for k, v in something.iteritems() if v == 3}
to this syntax using map/filter: dict(filter(None,
filter(lambda x: x if x[1] == 3 else None,
something.iteritems()))
More frustrating to me is that there isn't an imap/ifilter builtin (I understand that it's in stdlib, I'm just lazy).FWIW, alongside the demotion of reduce to the stdlib Python 3 changed that, the builtin `map` and `filter` now return lazy "views" (as do e.g. `dict.items`, `dict.keys` and `dict.values`):
# python 2
>>> map(sys.stdout.write, ['foo', 'bar', 'baz'])
foobarbaz[None, None, None]
# python 3
>>> map(sys.stdout.write, ['foo', 'bar', 'baz'])
<map object at 0x10071cfd0> {k:v for k, v in something.iteritems() if v == 3}
vs dict(filter(lambda x: x[1] == 3, something.iteritems()))Writing a C extension to do computationally intensive work is fairly simple.
With its versatility and extensability I once threw together a proof of concept password cracking cluster in a week. Redis for queues/data, MySQL for data, hashing code in a C extension, flask for the web interface. Cross platform and lovely :-)
Threading is the only real let down for me.
Even more so with Cython, which does most of the hard work for you.
- C-extentions (many std and 3rd party libs that need speed are written in C)
- numpy http://numpy.scipy.org/
- numba https://github.com/numba/numba
- cython http://www.cython.org/
- pyrex http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/
- http://psyco.sourceforge.net/ -> PyPy (some day...)It is unfortunate that you have to know that kind of thing a priori.
"Oh, look, a nice, built-in threading module."
Yes, BUT...
EDIT: I just re-read the way you wrote the "threading" comment and realized you probably weren't talking about it in the same context. Amazing what a word or two can do to understanding.
len(a_string)
a_string.lower()
a_string.upper()
So here, there's a global, generic function len() that returns the length of the string while in the other examples, the string has methods such as .lower() .upper() etc. Why not have a_string.length()? I understand that generic functions are more efficient and even proper in OO. It just causes the feeling of inconsistency when you use the language a lot or when you are teaching a child how to program and have to explain the difference and why some things are methods while others are not.
Also, some python idioms (such as slicing) are not intuitive for many use cases. Here's an example of the traditional way in which to reverse a string in Python by slicing:
a_string[::-1]
In Ruby, you type a_string.reverse() and that is much more intuitive to a child when compared to the slicing in Python.
Having said all of that, I love Python and use it daily and encourage others to do so as well. I
?
result = [
(value, process(value), process(key, value))
for key in generator
for value in generate(key)
if some_condition(key)
if other_condition(value)
]
or did you mean something completely different? Maybe the problem of not being able to use statements in comprehensions (or lambdas)?Seriously, threads are evil.
Threads in Python still have all the pitfalls and complications they have in other languages, it's just that they cannot use multiple cores or hardware threads. So you get all the evil bits (race conditions, deadlocks, having to protect access to shared state, etc), but you miss out on all the performance benefits threads can have on parallel hardware.
In Python, threads are only useful as an abstraction of things that have to run concurrently, even though they never do.
> have to run concurrently, even though they never do.
is way off base.
Likewise, if you're doing CPU-heavy crunching (likely implemented in C or Cython), you can release the GIL and let the interpreter run some other bytecode at the same time.
This is just false. Threads still offer performance benefits for non-CPU bound operations. For example, if you are trying to download data from 100 different sources, you could do it in a single thread, but python would spend most of its time idling while other prosseses do work, or you could have 100 threads, so python can do work while it is waiting on other stuff to get done.
Fundamentally, I believe Python's drawback is that it appears to draw heavily from the ABC history it has, and wasn't designed for power and flexibility.
I'd rather use Perl, Ruby or Lisp.