
Python is not a great programming language - robertakarobin
https://gist.github.com/RobertAKARobin/a1cba47d62c009a378121398cc5477ea
======
tomp
> a big part of my frustration comes from having a JavaScript background

Yeah, I thought so when I was reading the list of "problems". In fact, many of
those are _features_ (e.g. lists & tuples being different, list
comprehensions, lazy evaluation, distinction between maps/dicts and objects,
...) but the author doesn't actually understand them. I hate to sound so
negative, I guess I just didn't realize how bad a programmer knowing
(only/mainly) JavaScript causes you to be.

~~~
mlyle
There's some annoyances that have accumulated in python, like the inheritance
patterns and too many underscored things. Decorators are kinda weird, too.

I think Python is pretty awesome, but to pretend that it's super clean is a
bit of a stretch.

(a,) for a single-elem tuple-- avoiding colliding with (a) as a paren'd
expression-- is wonky, too.

~~~
eesmith
The tuple notation is one of those places where the obvious way is a
distraction. A non-empty tuple doesn't need the ()s - it's the "," which makes
the tuple! The following two are equivalent:

    
    
      x = 1,
      x = (1,)
    

Of course, then there's no way to express the empty tuple using commas, which
is where () comes in.

Using (a,) is the belts-and-suspenders way of saying "this is a 1 element
tuple".

~~~
mlyle
Which is just further elaboration on the syntax being excessively special
cased.

~~~
eesmith
Oh, agreed. Just wanted to point out that I think the transition from "()" to
"a," is the underlying problem, not from "(a)" to "(a,)"

------
akubera
Part of the problem here is clearly this person is mapping JavaScript idioms
to python; in particular "Needing to put dict property names `{'in':
'quotes'}": these aren't object _properties_ , these are keys in a map, and
they can be and often are variables themselves, (also, can be any hashable
type, not just strings) and I don't see how that can detract from the
'greatness' of python.

Also "foo['bar'] returns a KeyError", then saying sometimes this is resolved
by "getattr(foo, 'bar', None)" \-- the split between attributes and items are
one of the best things about python.

I don't know why list comprehensions are "magic syntax", and you can't
complain about having to type list(map(...)) and then complain that you don't
like list comprehensions!

"You have to cast your data back to a list/tuple after using enumerate() and
map()." \-- I don't know what that means

"Different syntaxes for lists and tuples." \-- Again, what? They're different
types!

How is r'a\nd' less "goofy" than String.raw`a\nd` in JS or any other language
with prefixed strings?

Python is clearly not for everyone nor for every problem, but in my opinion it
is a great programming language.

~~~
duckerude
> "Different syntaxes for lists and tuples." \-- Again, what? They're
> different types!

Lists and tuples have different syntax in a weird and surprising way. List
syntax is straightforward, just square brackets and commas. Tuple syntax
pretends to be list syntax with parentheses but it's actually only about
commas except when it isn't.

Typically you write `(a, b)`, but the parentheses are only for precedence, and
can be left out if it's unambiguous: `a, b`. You can write a 1-list as `[a]`,
but a 1-tuple is `(a,)` because `(a)` is just `a`. An empty tuple on the other
hand is `()`, without any commas, and with parentheses doing something other
than precedence. It's very ugly.

I can't think of a better way to fit it into the rest of the syntax but I
still count it as a flaw.

~~~
mixmastamyk
It’s the one of those common situations where there’s no perfect solution, so
one from a number of suboptimal ones needs to be chosen. This rubs random
folks in random ways, and is endemic to language design. Just try to design
one without no such compromise.

In this case the problem stems from the overloading of parentheses. There
aren’t any more paired delimiters in ascii available unfortunately. Perhaps
t[] could have been chosen, but as you see it isn’t exactly elegant either.

------
stevebmark
These all just seem related to syntax, not actual program structure or
capabilities. The only program structure thing I see is list comprehensions,
which is one of Python's great strengths. Ironically they say Ruby is more
pleasant to write, when Ruby has the deepest structural flaws of any dynamic
programming language.

~~~
manch23
I’ve always been super happy using Ruby. Never had any experiences that would
cause me to label it in the way that you have. However, I am interested to
know what those flaws are. Could you elaborate?

~~~
kiaulen
Disclaimer: I do ruby for my day job, and python for fun Disclaimer 2: I don't
hate ruby, despite all these criticisms. If I had to pick a worst dynamic
language it would be PHP.

Things I don't like about ruby: \- two string types, symbol and string, with
string being the mutable by default one.

This means given a random thing back from an api, you don't know whether to do
thing[:id], thing["id"], or thing.id

This has been acknowledged as a pain point by Matz, which is why in ruby 3
strings will be immutable by default.

\- Simultaneously too many names for things and not enough.

Is it .length or .size or .capacity (probably not capacity)? is_a?, kind_of?,
or instance_of?? Why can I do .select or .keep_if but not .filter?

\- Too many function types.

Do you want a method, a block, a proc, or a lambda? There are subtle
differences between each, so choose wisely. I'll note that python suffers from
this too (method, function, lambda, comprehension).

\- Too much emphasis on magic

Novice rubyists get frequently bitten by all the advanced (and very hard to
google) ruby concepts. How do you know what arr.map(&:id) is without already
knowing that it's calling symbol.to_proc? How about $1 $? $! (if you know what
all these do, you're a better rubyist than I am).

Since the author of the referenced post criticizes django for being too
magical, try rails. In addition to the names of files mattering a ton, there's
the routes DSL, the migrations DSL (which is not well specified in the
guides), and ActiveSupport, which you only realize you're using when it's
gone.

\- Horrible error messages

undefined method :[] for nil:NilClass (when you try to get something out of a
hash and it's nil) cannot convert Symbol into Integer (this is on an array
being returned where a hash is expected) Also, if you get a stack trace, it's
completely inscrutable. Python's stack traces (at least ipython's) show you
both the line number and the line in question (often with context).

------
kendallpark
Something not mentioned in list that I had never considered until a
conversation last year: meaningful whitespace is biased towards sighted
programmers. According to a blind acquaintance, it is much easier to keep
track of opening and closing parentheses and braces. Meaningful whitespace
forces blind programmers to always check the number of tabs at the beginning
of each line to make sure scope hasn't changed.

~~~
im3w1l
Couldn't whatever tool they use just apply a transformation from indentation
change to brace?

------
meow_mix
This is so surface level it's not even worth addressing

------
aeyes
> The syntax for classical inheritance. Half of each Django app is
> super().__init__( _args,_ *kwargs)

Care to show proof? I don't see this a lot.

The code of the official Django site for example has 8 occurences:
[https://github.com/django/djangoproject.com/search?q=super+_...](https://github.com/django/djangoproject.com/search?q=super+__init__&unscoped_q=super+__init__)

------
jchw
> you have to do foo.get('bar')... or in some cases getattr(foo, 'bar', None)

This is a mistake based on thinking in Javascript: in JS the index operator is
effectively the same as the dot operator, for example foo[“bar”] == foo.bar.
In Python those are different. [] corresponds to get in dicts, and .
corresponds to getattr in objects.

My personal biggest gripe with Python is that there isn’t a better story for
typing as I’m spoiled by TypeScript and static languages that have a better
development experience and prevent certain classes of mistakes.

~~~
snypox
How about post 3.5 type hints?

~~~
jchw
Every time I mention typing in Python, people point out the existence of type
hints and type checkers. However, I’ve yet to be able to meaningfully use one
in practice. I have used MyPy but it didn’t offer nearly as much benefit as
say, TypeScript, and brought problems of its own. There’s been some work done
but for example, Django middleware is problematic here. A lot of Pythonic code
would be extremely challenging to actually cover with robust typechecking.

Not to mention, a lot of software treats type hints very differently. PyCharm
is able to do powerful inference that MyPy can’t do.

------
JNRowe
I /feel/ like this gist is largely on the money, and also uninteresting at the
same time. All languages suck, and often suck in different ways for different
users. We just have to pick the one that is working best at the time, and that
we're best at working with.

I will say that I do like the act of writing a pro and con list for a language
or tool though(which is why I read the linked doc), and I often push co-
workers to do so when they're suggesting things. Sometimes it shakes a bad
choice out just with the simple act of jotting down a 5-point list.

Unless, of course, there actually is an ideal language out there. In which
case I retract my point ;)

~~~
juped
There is, and it starts with L. (Lua, of course.)

------
sidlls
There are plenty of things about Python that are not good, and only two are
touched on in this list (the cumbersome double-underscore syntax and "magic
methods" in general, although the author clearly has experienced python mainly
through Django, which has other problems). The rest read like someone who
doesn't really know the language that well and hasn't a lot of experience in
general.

------
santiagobasulto
TLDR: if you’re a new programmer, don’t take this seriously.

This person clearly doesn’t understand programming languages so well. Sorry to
make this response an “ad hominem” one, I should revoke the claims. But it’s
impossible if they don’t understand basic concepts of programming languages.

For example, complaining that a dictionary key must be place in quotes. It’s
not that “the key needs quotes”, but that you’re using “a string” as a key. In
fact in Python, you can use any immutable object as a key (tuples, for
example). They clearly come from Javascript, Python works differently (and
arguably better), and they’re complaining that it doesn’t work the way they’d
like it to work.

Then complaining about list comprehensions as a “weirdo” thing. Clearly not
understanding the functional paradigm and the beauty of expressions (see
Smalltalk for the ultimate example of beauty and expressiveness).

~~~
mplanchard
> any immutable object as a key

Any hashable object, technically. Especially with custom objects, the two
(immutability and hashability) don’t necessarily have to overlap, although
it’s often a bad idea to have hashable, mutable objects

~~~
abhishekjha
Mutable objects can't be hashed right?

~~~
detaro
As the comment you responded to says: An object can be mutable and hashable.
E.g. any custom class by default has mutable and hashable instances.

~~~
abhishekjha
How can you get both mutable and hashable instances at the same time?

~~~
detaro
the only requirement on a hashable instance is that it implements a proper
__hash__ function. The default implementation in CPython returns the address
of the memory the object is stored at, so all instances are considered unique,
values play no role at all.

------
skinkestek
I think I agree.

Here are a couple of more, from someone who has worked for years in a number
of languages:

\- few limitations means you can easily make a serious mess, which means you
are more dependent on good programmers to avoid the mess.

\- lack of typing information isn't "free solo" hard but it certainly makes
life a lot less pleasant.

~~~
JNRowe
> lack of typing information isn't "free solo" hard but it certainly makes
> life a lot less pleasant.

One of the projects I work on has switched to full type hinting along with
heavy mypy¹ usage, and it has become an absolute pleasure to work with. Along
with hypothesis², I can't recommend mypy enough. It must be said that
retrofitting either to an existing project is a _lot_ of work though.

1\. [http://www.mypy-lang.org/](http://www.mypy-lang.org/) 2\.
[https://github.com/HypothesisWorks/hypothesis](https://github.com/HypothesisWorks/hypothesis)

~~~
totalperspectiv
What I can't reconcile with myself is, if I'm going to add type hinting in
Python, why not use a language where my efforts of adding type hints result in
performance gains? I get why it's nice for a team, but it seems like a lot of
work for half the potential benefit of a typed language.

~~~
j88439h84
I think that sort of thing is coming, with e.g. `mypyc` that compiles typed
Python into C.

------
sys_64738
It doesn't matter about its quirks. The most important thing nowadays is that
it's reached critical mass. We're at the point where even non-developers can
now cobble together a bit of python code to do simple things to make their
jobs easier. This means it's here forever, IMO.

~~~
FpUser
I remember our salesmen back in 90's cobbling together some Visual Basic or
whatever it was called. So where it is now?

~~~
zekrioca
Has the same happened to Python yet? No? So bookmark this thread, and come
back in here when the very same happens to Python :)

~~~
FpUser
I just made a note. I do not really give a flying hoot to what happens to
Python. I only use it in places I do some consulting since they require it. I
do not use it for my own products.

------
verdverm
I love Rob Pikes idea that is dumb to have scope determined by invisible
characters

~~~
TheOtherHobbes
It makes scope visual and consistent. IMO it's less dumb than having scope
defined by pairs of curly braces which are impossible to read without
indentation and require extra effort to keep matched - unless you try to
automate the matching with a good IDE.

~~~
erik_seaberg
There are a lot of tools that shred whitespace but not curly braces.

------
RootKitBeerCat
I think the adage goes “Python is the best at being the second best at...
almost everything”

~~~
folkhack
I've never heard that one, and not going to take a stance... but I WILL make
the argument that if true, that's a HUGE strength!

------
beager
There’s plenty to criticize Python about relating to performance and developer
operations (pypi, virtualenvs, cumbersome version juggling), but most of these
language features mentioned have a valid need to exist, or are purely
stylistic, and therefore not admissible to the “not a great programming
language” debate imo.

------
juped
I wish more languages did something like the D feature where method syntax and
function syntax are really just syntax - i.e., str.len() is equivalent to
len(str), and you use the syntax that best improves readability at your call
site.

~~~
turbinerneiter
[https://en.m.wikipedia.org/wiki/Uniform_Function_Call_Syntax](https://en.m.wikipedia.org/wiki/Uniform_Function_Call_Syntax)

Called uniform function call syntax, an I idea I first saw in Nim and found
really cool, but then found really dumb. Can't remember the reasoning for both
opinions, which is a strong indicator of "probably doesn't matter".

For a while I was entertaining a thought regarding mutability and returning
copy's. Like foo.bar() would mutate, bar(foo) return a modified copy. Don't
know if that concept has a name or if it actually makes sense at all.

------
ACow_Adonis
It's certainly not a perfect language (no multi-line lambdas, double
underscore keywords everywhere, converting generators, significant white space
means problems working in other environments, typing, isn't called lisp), but
you know what...

I've been pleasantly surprised how shallow the "general python rabbit-hole"
is.

If i can express this in words, one of the ways I like to judge a language
when I program: i think of something a computer could theoretically do, then I
look for ways to express it in that language. How many independent jumps I
have to take down the conceptual rabbit-hole before I get to the solution is a
nice little arbitrary metric.

Does python do everything the way I'd do it? No. You get over yourself and
just accept that's the way it is in this language.

Once I've done that, so far most problems in python have been pretty shallow:
do this arbitrary thing and then this arbitrary thing and you're done.

Compared to some of my past languages where you have to go 4 or 5 levels deep
with N compulsory but conceptually irrelevant steps, it's pretty damn good.
Makes for a reasonable quick pathway to actually getting anything done...

Purely my subjective opinion.

------
rdiddly
As usual, the worst things about a thing are also the best things about it,
and the best things about it are the worst things about it.

------
imtringued
I personally hate virtualenv. I just install pip dependencies in a vendor
directory and point PYTHONPATH to it.

------
encoderer
That's just like, your opinion, man

------
nhumrich
There are two kinds of languages. Those that people complain about, and those
that nobody uses.

------
bjoli
I hate python, but it is still invaluable to me for its ability to let me get
shit done. Getting meaningful data from a 100mb CSV? No problems. Everything I
need can be found in the STDlib.

From my main perspective (scheme programmer): I am constantly amazed by the
bad code python programmers write in scheme. Take a nice recursive function,
sprinkle it with set! (which leads to boxing and general slowness in many
implementations) and if that wasn't slow enough for you, wrap it in a call/cc
to be able to return from arbitrary places in the function. As a bonus, forget
to discard the captured continuation to make your program use insane amounts
of memory.

Then proceed to complain on Reddit.

------
diminoten
You can write your dicts as keyword args to the dict() cast if you don't like
putting your keys in quotes, but it looks like shit.

There are two paths to take when one encounters something new; adapt and
learn, or reject and mock. This sounds like the latter.

------
polotics
This list is so deliciously sophomoric. The best one is, and I quote: """To
many other weirdo bits of magic syntax, like [list comprehensions]"""
Obviously without actually proposing how comprehensions could be made better
one has to hope the author would say he likes the equivalent Haskell better,
but there is a strong doubt that is not the case.

~~~
mattlondon
I personally find list comprehensions in python pretty horrible.

They seem to exist only to do lots of stuff in one single line of code. You
end up with totally impenetrable unreadable perl-esq garbage write-once-read-
never code that is too clever for its own good. And people say python is easy
to learn and good for beginners...!

A better approach would be something like Java Streams/.net Lync/RxX pattern
IMO. Explicit, clear, no magic, logical.

~~~
genidoi
dic = {k: v for k, v in dic.items() if k in other_dic and v == "bar"}

How could that be improved? That's 3-4 LOC minimum in any other language

My main grip is python's ternary operators, since the True value is evaluated
before the condition, if you are doing ternaries on things that might throw
exceptions the False value has to come first

value = 0 if key not in dic else dic[key] * 5

rather than (throws indexerror if key isn't in dic)

value = dic[key] * 5 if key in dic else 0

~~~
grzm

        dic = {k: v for k, v in dic.items() if k in other_dic and v == "bar"}
    

> _" How could that be improved? That's 3-4 LOC minimum in any other
> language"_

If I'm understanding the comprehension correctly,

    
    
        (into {} (filter (fn [[k v]] (and (get other-dic k) (= v "bar"))) dic))
    
    

Though, for readability, I'd likely write it as:

    
    
      (->> dic
           (filter (fn [[k v]]
                     (and (get other-dic k)
                          (= v "bar"))))
           (into {}))
    

Legibility is in the eye of the beholder.

~~~
genidoi
Each to their own, but both the snippets you posted crossed my threshold for
headache inducing parentheses tracking

~~~
grzm
Similarly, my parsing of special-case syntax in the Python.

~~~
genidoi
What special-case syntax? The only brazenly pythonic thing in there was the
(iterative) tuple unpacking, as in

(for) k, v in dic.items()

which is just

k, v = (<key>, <value>) for every key value pair in the dictionary

~~~
grzm
There are a number of precedence rules you need to keep track of to parse the
list comprehension. There are two different syntaxes that do the same thing.
Arguably you need to do the same if you don't already know how the threading
macro in the my second example works.

I posted due to your claim regarding all other languages necessitating
increased verbosity. I should have left it lie, as I didn't intend to promote
a language war, just to post a counter example. My apologies.

~~~
genidoi
No language war intended, just curious how something I see as a wheel could be
improved.

------
LaGrange
I actually dislike Python, but this list ain't it.

------
nojvek
I love python. I just really wish there was a typed equivalent of python like
Typescript. The python3 types are very weak compared to TS.

------
hootbootscoot
One realizes the true cost of Pythons forced indentation when using
MicroPython over a serial connection, for example It's a big PITAS, tbh. I see
Python as a Vala-like glue language for C (as in Cython or FFI) but this
singular weakness makes you pay attention to it in a context in which it' s
truly beyond ridiculous. One may as well lisp for such use-cases...

------
viraptor
If you complain in the same post: about top level functions like list and map,
and about list comprehension, you've lost me. To have lazy and eager types and
list processing, you either have to have functions or syntax for both. If you
complain about existence of both, you either need to propose a new paradigm,
or you're just grumpy about everything.

------
danbrooks
This reads like a list of complaints about Django. There's much, much more to
python than Django!

------
j88439h84
Sure Python has many problems, but these aren't a great selection.

> The syntax for classical inheritance. Half of each Django app is
> super().__init__( _args,_ *kwargs). At least you don't have to pass
> arguments to super anymore.

Yes, inheritance is hard to do well, but that's generally true. Much better to
prefer composition
([https://en.wikipedia.org/wiki/Composition_over_inheritance](https://en.wikipedia.org/wiki/Composition_over_inheritance))

> Too many magic __double-underscore__ methods and properties that you have to
> just memorize.

Better to just start using [http://attrs.org](http://attrs.org) or dataclasses
so you don't have to do all the manual work. Classes without boilerplate.

> Too many top-level built-in functions that (a) you have to just memorize,
> and (b) get really ugly. You end up with stuff like list(map(...)). I
> haven't used so many nested parentheses since my early days in PHP. Guido's
> explanation makes sense in theory, but is really annoying in practice.

I agree, there should be a pipeline operator |> like F# has. A similar
proposal has been made for JS. [https://github.com/tc39/proposal-pipeline-
operator](https://github.com/tc39/proposal-pipeline-operator)

> Too many other weirdo bits of magic syntax, like [list comprehensions].

List comprehensions are good, they just take 5 minutes of getting used to.

> Django specifically is so full of magic words, and its documentation is so
> convoluted, that I've basically given up on documentation altogether and
> just look at the Django source code now.

Pyramid has a much more principled design IMHO.
[https://trypyramid.com/](https://trypyramid.com/)

> Needing to put dict property names `{'in': 'quotes'}.

Or use `dict(foo=5)`. Python mappings can contain non-string keys, so `{5:
1.2, 6: 3.4}` maps ints to floats.

> You have to cast your data back to a list/tuple after using enumerate() and
> map().

Don't use map, use comprehensions.

> Different syntaxes for lists and tuples.

They are different objects, why would they have the same syntax?

> foo['bar'] returns a KeyError, so you have to do foo.get('bar')... or in
> some cases getattr(foo, 'bar', None), but not in others because getattr and
> .get are different things.

Python is a different language from Javascript.

> You can't just tack on flags to /regular_expressions/ig.

Yeah that's annoying.

> All the goofy string literals: f' ', u' ', r' ', etc.

That's a good thing, not a bad thing.

> Pipfile does not work that well.

Poetry works better. [https://poetry.eustace.io](https://poetry.eustace.io)

~~~
Animats
_Sure Python has many problems, but these aren 't a great selection._

Yes. I have my disagreements with Python, but those are not it. The article
author is mostly complaining about ways Python differs from Javascript.

\- Agree that the mechanism for talking about parent classes wasn't very good.
Multiple inheritance usually adds complication without adding much necessary
functionality. That's not just a Python problem. It's a leftover from viewing
objects through an "A is-a B" lens, one of the dead ends of early AI.

\- Python has too much gratuitous dynamism. Any thread can find and mess with
any code and data in another thread. The implementation has to support that,
which knocks out many valuable optimizations. The language model, and the
original implementation, use "everything is a dict", which implies "slow".

\- One consequence of the above is Python's terrible one thread at a time
thread system, with the "global interpreter lock". The "multiprocessing" hack
to get around that is ugly and uses too much memory, since each subprocess has
its very own Python system instance. To some extent, the "async" add on is yet
another hack to get around the limits of threading.

\- Another consequence is a tendency to call C code where Python performance
is terrible. The C code has to carefully obey the rules of the Python system.
Mostly it does.

\- Optional typing is a marginal idea, but unchecked marginal typing is just
weird. Language design seems to be converging on implicit static typing -
result variables are automatically typed whenever possible. Go, Rust, and now
C++ (with "auto") took that route.

\- And, of course, the botched Python 2 to 3 transition set Python back for a
decade.

On the other hand, Python exceptions work out well. A reasonably sane
exception hierarchy helps. Although the one for 2.x was better than the one
for 3.x; the 2.x one made a clear distinction between external problems
("Environment errors") and internal problems.

The "with" clause system plays well with exceptions, and nested exception
failures unwind correctly. It's far better than Go's "defer". C++ and Rust try
to handle this sort of thing with RAII, which never handles trouble in a
destructor well.

~~~
j88439h84
Regarding performance, I'm hoping alternate interpreters like pypy start
thriving. Theres no reason python needs to be as slow as CPython.

------
tgv
Django is ok if you have a web site with content and forms that map nicely
onto SQL tables. Any 1998 website, basically, with not too much of a load. Any
other setup, and it gets messy.

> Needing to put dict property names `{'in': 'quotes'}.

Now that's a part I like.

~~~
TylerE
The inclusion of that made me question the entire thing. It's just so...wrong.

Does the author not realize that you can use (almost) anything as a dict key?

    
    
        a = 123
        b = 'foo'
        d = {a:456, b:123, 2.3:'2.3'}
        print(d)
    
        >>> {123: 456, 'foo': 123, 2.3: '2.3'}

~~~
coldtea
> _Does the author not realize that you can use (almost) anything as a dict
> key?_

You can do that in JS too, it's just coerced to string, so you can do e.g.

    
    
      {foo: "bar"}
    

where foo is not a variable, but assumed to be the string foo. In Python you
can't, because you couldn't tell if it's foo the string, or a reference to an
existing (or non-existing) foo variable.

That said, in JS, not only you're limited to using (real or coerced) strings
as keys to Objects, but you also need to use the square bracket:

    
    
      {[foo]: "bar"}
    

syntax, so it can tell that you need it to use the value of the variable foo
as the key (else it will understand {"foo": "bar"}).

~~~
TylerE
That's different though. In python there is no coercion going on with the
keys.

------
HorkHunter
I mean, one could complaint about many things in Django, but the
documentation....??? Really?

------
hannibalhorn
Jack of all trades, master of none.

The article has a silly premise - I don't know anybody who would claim it's a
great programming language. For the moment, it's just the most practical in
certain areas (notably data science and machine learning.)

~~~
Stubb
Python's power comes from the fantastic array of libraries to which it
provides access. Much of my Python coding involves stringing together calls to
SciPy, Matplotlib, lxml, and the like. It's a super productive language so
long as you don't need multithreaded performance.

~~~
FpUser
Too bad you can not use it to write those libraries ;)

------
jolux
The biggest beefs I have with Python are inconsistent syntax and dynamic
typing. But I've basically decided that dynamic types are a waste of time for
me personally, so I'm not really the target audience anyways.

~~~
j88439h84
The new static type analyzers like Mypy and PyType give it more of a static
feel IMHO.

~~~
jolux
As I understand it those are not really complete though, are they? Do they
offer algebraic data types and exhaustive pattern matching?

~~~
j88439h84
I'm not sure. There is Union[Foo, Bar] which matches instances of Foo or Bar
[0] and there is @overload [1]. What would it need to be complete?

[0]
[https://docs.python.org/3/library/typing.html#typing.Union](https://docs.python.org/3/library/typing.html#typing.Union)

[1]
[https://mypy.readthedocs.io/en/latest/more_types.html#functi...](https://mypy.readthedocs.io/en/latest/more_types.html#function-
overloading)

------
phtrivier
Yes. Because python is a programming language.

------
djohnston
He has insulted list comprehensions and therefore my honor!

------
FpUser
I would agree about "magic" parts.

------
tanilama
So be it. It is useful nevertheless.

------
ben509
Most of these are pretty dumb, but there are some inadvertently good
points.[5]

> The philosophy of "one correct way to do things."

I miss those days. T_T

> A huge ecosystem of good third-party libraries.

Python also has a huge ecosystem of huge ecosystem managers, which is less
than ideal.

> Half of each Django app is super().__init__( * args, * * kwargs)

Haven't used Django, but if I were providing a suite of classes people were
extending, I'd provide lifecycle hooks.

Using super().__init__ is not a syntax problem as much as it's a semantics
problem, because it's so fragile.[1]

> Too many magic __double-underscore__ methods and properties that you have to
> just memorize.

Yup, dunders are the ugly consequence of duck-typing. Maybe Python could tuck
them away in some kind of traits system.

> Too many other weirdo bits of magic syntax, like [list comprehensions].

But the Python syntax _is_ weird:

    
    
        [elem for outer in iterable for inner in outer]
    

Which is essentially:

    
    
        for outer in iterable:
            for inner in outer:
                result.append(elem)
    

It's backwards. The PEP[2] doesn't explain why, but looking at the JS
syntax[3] it does seem less worse.

Other weirdo syntax:

    
    
        while some_condition():
            if check_a_thing(elem):
                break
        else:
            print("never broke from loop")
    

The meaning sort of makes sense if you think of `while` as an extension of
`if`.

But I would have expected this:

    
    
        for x in y:
            do_a_thing()
        else:
            handle_empty_case()
    

Or, really, a more descriptive keyword.

> You have to cast your data back to a list/tuple after using enumerate() and
> map().

I thought it was the coolest thing when generators got full support in py3k,
but it's _horribly_ broken.

This does not raise an exception:

    
    
        x = list(some_generator)
        y = list(some_generator)
    

y will be empty, which is a completely silent failure. The same is true for
iterators. And everyone's been bit by strings being iterable[4].

Sometimes it's useful to do this:

    
    
        i = iter(some_generator)
        x = next(i)
        for elem in i:
            ...
    

But Python should _not_ :

1\. make iterators be iterable.

2\. allow spent generators to be reused without exception.

3\. make strings be iterable.

(All of which could be bypassed with a method call when you really want that
behavior.)

> foo['bar'] returns a KeyError, so you have to do foo.get('bar')

LOL, only javascript devs could not have noticed the hours they've spent
tracking down the source of some mysterious undefined. Python _definitely_ got
this one right.

[1]: [https://fuhm.net/super-harmful/](https://fuhm.net/super-harmful/)

[2]:
[https://www.python.org/dev/peps/pep-0202/](https://www.python.org/dev/peps/pep-0202/)

[3]: [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Operators/Array_comprehensions)

[4]:
[https://mail.python.org/pipermail/python-3000/2006-April/000...](https://mail.python.org/pipermail/python-3000/2006-April/000759.html)

[5]: Wait, am I talking about the OP or this comment?

~~~
rsinger87
I wrote this package to provide model lifecycle hooks (similar to Rails
callbacks) because I didn't like the frequency that my codebase was overriding
__init__(): [https://github.com/rsinger86/django-
lifecycle](https://github.com/rsinger86/django-lifecycle)

Django's ecosystem (mainly DRF and django-filters) makes it very productive
for me, but I find Django core to be lacking for developer ergonomics in
certain areas.

~~~
ben509
That's exactly what I was imagining. You can tell it's a nicely designed base
class because the dunders are all tucked away so the only thing left is actual
logic.

------
elan3
yeah i agree, it is so weird to see that python is the only language that
haven't implemented in its own language atleast to my knowledge

~~~
progval
That's not what the article is about. And most interpreted languages (bash,
ruby, perl, JS, ...) are not implemented in themselves, because it makes no
sense (who interprets the interpreter?).

However, Pypy is a Python interpreter that is written in (a compiled subset
of) Python 2.

