
Why Python Is Not My Favorite Language (2016) - rbanffy
https://zenhack.net/2016/12/25/why-python-is-not-my-favorite-language.html
======
falcolas
So, this shortens down to "I don't want people touching my private class
members", "I want multi-line lambdas", and "I want types".

WRT private class members, I can understand why this might be frustrating for
library writers. But it's just so damned useful to be able to reach into a
library and get functionality the library's writer didn't think I'd need, that
I'll personally never consider this to be bad.

Multi-line lambdas would certainly be nice, but most of it can be handled with
a scope-level function. It adds, on average, one line of boilerplate. As for
syntax, no need to use Ruby's syntax, just incorporate parenthesis (a well
established continuation construct within Python):

    
    
        with(open("foo.txt"), lambda f: (
            print(f)
            print("I am a teapot")
        ))
    

The sinatra example doesn't really make the writer's argument about decorators
to me; since the Flask example exposes a bit more functionality for only two
lines of code - I now have a distinct app object, and can create more, or
explicitly access attributes of the app itself. I can even add authentication
to that function with just two lines:

    
    
        auth = flask.ext.httpauth.HTTPBasicAuth()
    
        @app.route("/")
        @auth.login_required
        def handle_route():
            return "I am a tea pot"
    

No, Python isn't Ruby; it doesn't support the same degree of metaprogramming.
That's good, IMO. Metaprogramming is the source of exponentially more
technical debt (and bugs) than is reasonable, frankly.

As for the lack of a type system, yeah, that is definitely Python's biggest
weakness for large programs. A lot of it is resolved with the type hints in
Python3, but a lot of it is just Python itself. Love it or hate it, that's how
Python is, was, and will be.

As for the example error - there is a typo. Of course it's going to throw a
traceback. The error message is even more explicit than I expected, frankly.
It pointed out the missing function name, which would make the typo rather
easy to find.

~~~
pyrale
> But it's just so damned useful to be able to reach into a library and get
> functionality the library's writer didn't think I'd need, that I'll
> personally never consider this to be bad.

I was ready to accept this as a matter of opinion, but then with this :

> Metaprogramming is the source of exponentially more technical debt (and
> bugs) than is reasonable, frankly.

Once you've jumped the shark and play with introspection and internals, are
you really going to make the point that metaprogramming (or almost anything
else, for that matter) should be banned ?

~~~
falcolas
> are you really going to make the point that metaprogramming should be banned
> ?

As a fan of the concept of the "Catfish" developer (even though I'm a
"corporate drone"), absolutely.

Or, if not banned, kept to an absolute minimum. It's like the preprocessor in
C. You can do anything and everything in it... but should you?

You can be exceedingly clever with metaprogramming, but clever code is
exceedingly hard to read and maintain. Give me boring, explicit code any day
of the week.

~~~
tome
> You can be exceedingly clever with metaprogramming, but clever code is
> exceedingly hard to read and maintain. Give me boring, explicit code any day
> of the week.

Boring, explicit code which depends directly on the unspecified, unsupported,
implementation details of the libraries you're using ... ?

~~~
KirinDave
It's weird how people neg on some abstractions for Python but then brag about
the absolutely intense degree of abstraction something like Tensorflow brings
to the table.

It's almost like the qualification is that the reader can't even imagine
writing the abstraction and therefore they can ignore it.

~~~
ThrustVectoring
There's a huge difference between abstraction you have to write and debug
yourself, and abstraction that you can use and debug through documentation and
StackOverflow.

Third-party resources are where it's at for taking advantage of things that
require skills you don't have. I haven't written enough frameworks and
abstract libraries to expertly debug, test, and write my own.

~~~
KirinDave
> There's a huge difference between abstraction you have to write and debug
> yourself, and abstraction that you can use and debug through documentation
> and StackOverflow.

Indeed. You're more likely to be able to use and debug it if you wrote it
yourself rather than relying on the weirdom of the crowd from SO.

> I haven't written enough frameworks and abstract libraries to expertly
> debug, test, and write my own.

I suspect you'd do just fine if you tried, assuming you don't try and displace
django.

------
hacknat
Decorators should be higher on this list. I work with Python daily and I
absolutely loathe people's custom decorators that obfuscate functionality
away, like STL in C++, except no type system of course.

Also, preach on duck typing it's a python myth that is absurd. If it quacks
like a duck means I had to figure out what it was.

This language is slowly destroying my soul, one painful unit test at a time.

~~~
Waterluvian
I love decorators so much (when used well).

For example, I used a decorator the other day that I called `@logperf` that I
can pin to any function, which will `logger.info` the approximate time it
takes to run the whole function. It doesn't mutate what the function does, it
just adds a side effect.

~~~
yxhuvud
It doesn't take away the argument that neither decorators and with would be
necessary if lambdas was a language feature.

~~~
Daishiman
The point isn't about the functionality of a lambda. The point is that the
syntax for a decorator denotes semantic meaning that you don't get when
composing a lambda on your own.

------
emodendroket
> Documentation. There’s a common mis-conception that type systems make code
> verbose. Firstly, type inference is pretty doable and addresses the problems
> that give rise to these beliefs. Java and C++ aren’t verbose because they
> have type systems, they’re verbose (partially) because they have bad type
> systems. Second, in my experience, actually clearly documenting an interface
> in a language that doesn’t have types ends up being much more verbose than
> it would otherwise be. You need docstrings regardless, but if the type of
> the thing says it’s an integer, you don’t have to write “x is an integer” in
> the docstring too. And now you have a concise, agreed upon notation for that
> sort of fact.

Preach it. Also, you don't have to worry that a method whose signature is
`public int DoWork(string input)` actually expects char[] as its input and
returns a long, the way you do when the types are only documented through
comments.

~~~
sametmax
Except Python is not only used for what you do. People use it for batch
testing, SIG embeded language, sciencitic analysis, sys admin, 3D tool
scripting, glue code, product pipelines, etc.

Having a required type system for this huge part of the community would be a
great let down.

Those criticism come from people comming from a strong dev background. They
completly ignore the rest of the world.

That's why having the current OPTIONAL type system is so good. Because you can
use it and benefit from it, and many do, but you don't screw half of your
community.

Python strength is versatility. It's not the best at anything. But it's very
good at most things.

That's why if you know it, you can solve most problems decently. That's what
makes it fantastic.

I'm still waiting for people doing data anaylsis in Go, sysadmin in JS, 3D
scripting in Haskell, batch testing in erlang or geography in lisp.

Because Python... well it can. And AI. And Web. And embeded systems. And IoT.
And... And...

~~~
KirinDave
> batch testing, SIG embeded language, sciencitic analysis, sys admin, 3D tool
> scripting, glue code, product pipelines, etc.

> Having a required type system for this huge part of the community would be a
> great let down.

... Why? These days types generate much more code than they cost in modern FP
languages. E.g., [https://github.com/haskell-servant/example-servant-
minimal/b...](https://github.com/haskell-servant/example-servant-
minimal/blob/master/src/App.hs)

> That's why if you know it, you can solve most problems decently. That's what
> makes it fantastic.

Most of the things you're talking about are available in other languages. And
quite frankly, if pure versatility and available open sourced code is your
argument? Then why aren't you using Javascript and nodejs?

Name a general domain that I can't find at least one or more well-maintained
projects supported on NPM. I dare you.

> And embeded systems.

Who.. who is doing IoT and Esys in Python outside of toymakers?

~~~
cwyers
You may be able to find a well-maintained data science/machine learning
project on npm, but I doubt you'll find as many as are needed in a typical
data science workflow.

~~~
KirinDave
Maybe, not sure how that is relevant. You definitely CAN do it, it's just not
as common. Same could be said of F#.

~~~
cwyers
> Name a general domain that I can't find at least one or more well-maintained
> projects supported on NPM. I dare you.

 _one is named_

> Maybe, not sure how that is relevant.

Man, it was your dare!

~~~
KirinDave
You didn't do what I said at all. Everything you need is there, it's just not
as popular.

Please tell me what pieces you need and I'll do my best to make good on my
claim. At a minimum, tensorflow, nltk and spark bindings exist. And in fact,
the popular notebook software packages are reaching out to other runtimes
already.

~~~
cwyers
That's far from where the bulk of time is spent for most data science
workflows. You need a pandas/dplyr and you need a ggplot2/[long list of Python
plotting libraries here]. You mentioned F#, which has Deedle for data frames.
What's JavaScript/NPM's story on this?

~~~
KirinDave
[https://github.com/data-forge/data-forge-js](https://github.com/data-
forge/data-forge-js)

[https://www.npmjs.com/package/dataframe-
js](https://www.npmjs.com/package/dataframe-js)

------
LyndsySimon
I think the author has good points, but from where I sit they seem fairly
subjective.

I've written Python for several years, and as a result the way I reason about
problems have been heavily influenced by it.

I'm fluent in Ruby as well, but it takes mental effort for me to conform to
its "flow", for lack of a better term. My time in Ruby has made me a better
Python programmer, too - I understand more fully the idea of a DSL and how it
should work. Ruby is great for writing DSLs and writing concise code that's
guided by the language of the domain in which you're working.

I'm competent in Clojure, which if nothing else has given me a distinct
distaste for impure functions - if a Python function takes an array, it should
_not_ modify that array in place unless it's very clear from the name that's
it's going to do so, and a function should not both modify its input and
return it.

At the end of the day, Python is still the language that I reach for whenever
I'm writing pretty much any personal project. It's clean, easy to read, and
the overall feel of the language guides me to write maintainable code.

~~~
crimsonalucard
Don't get how any competent programmer can recognize a glaring flaw... type
checking. A program without type checking will inevitably lead to less
maintainable code.

~~~
scriptkiddy
Python does type check - at runtime. That said, I understand where you're
coming from. Maybe it's because I'm very familiar with Python, but lack of
static typing has not been a problem for me in a long time. If I really need
to be sure that something is the correct type, i can try to catch `TypeError`
and handle it. Not the most terse of methods, but I rarely find that I'm all
that concerned with types in my Python work.

~~~
KirinDave
I think you fundamentally misunderstand what type checking is.

~~~
scriptkiddy
I don't think so. I think you're referring to static type checking. I'm
referring to dynamic/runtime type checking.

For instance, `1 + "hello"` will throw a `TypeError` at runtime because the
`+` operator is not supported for use with `str` and `int` types. In a
statically typed language, the code would not run or compile unless a `+`
operator has been defined that takes a `str` type as it's first argument and
an `int` type as it's second argument.

I guess you could say that a `TypeError` at runtime isn't really "type
checking" in a sense. I guess it would be more like "type enforcement".

~~~
KirinDave
> I don't think so. I think you're referring to static type checking. I'm
> referring to dynamic/runtime type checking.

No, you're referring to _type-related errors at runtime_. That's not checking.
Nothing is checked. Code breaks and may recover, but it has no idea what the
types of the arguments to the offending expression were, only that it didn't
work with that bit of code.

This is not type checking.

> I guess you could say that a `TypeError` at runtime isn't really "type
> checking" in a sense. I guess it would be more like "type enforcement".

All it does is say, "This code cannot execute with this implicit prior state."
It has nothing to do with types except in the most tangential way.

~~~
bendbro
1 + 'hello' resulting in a seg fault would be a "type related error"

1 + 'hello' resulting in an Exception would be a product of dynamic type
checking.

A language would not be able to explicitly throw a type related error unless
it had some information regarding the type of the data.

Most resources show little confusion on this, I think you are wrong.
[https://stackoverflow.com/questions/1347691/static-vs-
dynami...](https://stackoverflow.com/questions/1347691/static-vs-dynamic-type-
checking-in-c)

~~~
KirinDave
> 1 + 'hello' resulting in an Exception would be a product of dynamic type
> checking.

Incorrect. It _might_ be the product of a run time type check. It is not
_inherently_ so. You ca't even be sure you actually had a type error when you
get a TypeError.

But even then, this is using "type check" in the most vacuous and equivocal
way possible. It's not the concept most people are referring to.

> A language would not be able to explicitly throw a type related error unless
> it had some information regarding the type of the data.

You (and this resource to some extent) are confusing strong vs weak typing
with static vs dynamic typing. A + method might have a type check embedded
(esp python since + is magical), but it's actually quite rare of that a.foo(b)
is anything other than an assertion that the object A's vtable-analogue has an
entry.

This is sort of dynamic typing, in the sense that I can think of formalisms
that model this (named extensible row types come to mind), but this is a
profoundly useless defintion of "type."

I'm not sure why we'd tolerate redefining type checking to a worthless concept
and then using that divergent definition to imply that it's unhelpful. It's
2017, functional languages are fully baked. Powerful static type systems exist
even for the Javascript environment and they are taking over that ecosystem
rapidly.

~~~
blain_the_train
I find it amusing that the first look Google gives me on strong vs weak typing
says there like static vs dynamic typing. Then wiki says there is no strong
definition for either term. Maybe it would be easier to drop those terms and
go one level deeper?

~~~
KirinDave
There isn't too much confusion, but people bend over backwards to avoid having
their type system called "Weak."

------
sametmax
I don't know, that sound like details to me, or even taste.

There are many more important problems with Python: packaging, no way to know
which exception you are gonna get, no nice async web framework and so on.

But this? You have those in any languages.

~~~
thearn4
For packaging pains, do you mean the fact that there is no single agreed on
packaging/provisioning method (easy_install, pip, conda, apt, etc.), or that
these solutions are terrible?

~~~
sametmax
\- too many config files

\- no clean way to freeze / update

\- no good way to distribute a standalone executable

\- binary packaging is still a mess

\- big libs like GUI framewok are still terrible depend on

\- compilation solutions like the awesome nuitka requires a lot of manual work

\- too many things to know

For a beginer it's hell. I haven't been a beginer in 10 years, but I train
people in Python for a living, and I know it's a pain point.

~~~
thenomad
I AM a Python beginner, and I was amazed recently to discover that there was
no way for me to assemble an executable for an OS I'm not on that didn't
involve buying a computer running said OS.

I'm on Win10, and pyinstaller made it easy to create an .exe for Windows, but
I could find no way on Earth to assemble an executable for Mac.

As it happened I just asked a tech-savvy colleague on a Mac to use pyinstaller
on their machine, and it worked, but still - I'm really impressed with Python
generally, but this seems like a surprising and considerable oversight.

~~~
coldtea
> _I AM a Python beginner, and I was amazed recently to discover that there
> was no way for me to assemble an executable for an OS I 'm not on that
> didn't involve buying a computer running said OS._

Are you a beginner in general maybe? Because that's either arcane, damn
difficult to setup or nigh impossible in tons of other languages too -- even
ones that actually do produce executables to work (which Python by default
does not).

(And of course you can always just create the executable on a vm -- no need to
buy a computer running the other OS).

~~~
thenomad
20+ years of working in the tech industry and adjacent, so not so much, no.

But my work has, until this year, rarely involved compiling software for other
people to use. So that may be the noobness you're detecting.

~~~
coldtea
Basically, why I asked, is because you seemed to imply that you expected that
cross compiling would be easy.

Whereas, from my experience, it's usually a pain in the ass to set up.

------
jorgemf
Things I miss when I am programming in python:

\- nothing similar to a build system that manages the external libraries

\- no type checking, it means: no autocomplete in the IDE, runtime errors due
to a value not expected

I don't mind to use python for small things, but for medium-big project I
don't know how people can handle it.

~~~
SomeStupidPoint
I always find Python gets unwieldy between 1k and 10k lines.

Maybe I suck at Python.

~~~
Daishiman
As a general practice if a Python file goes beyond 2K lines of code I split it
and modularize it.

It's a great general rule for any programming language.

------
jamesfe
I like to use Python for quick, fast, flexible things.

I like to use Java and Go for things that require more structure and type
checking.

I like to use Scala for functional programming.

I suppose we all have our preferences, but I'm glad the author uses the word
"favorite" because there are great cases to be made for every technology out
there.

~~~
hacknat
I'm with you. The problem comes when you get a job with a bunch of kook-aid
drinkers who use it for everything.

~~~
acomjean
This.

Python is great for what it does. I use it a fair amount. Its fast enough for
me.

I admire the hutzpah to use it everywhere, but...But when people try to number
crunch, and add c extensions and hey arduino could use an interpreted
language! why don't we have it optimize itself (pypy) and so on.

When I learned it I thought I would reach programming bliss, as its reputation
is so strong. But after I learned it I thought "its nice, but am I missing
something?"

~~~
cpburns2009
I think a statically typed language similar to Python might be the holy grail
of languages. Maybe Nim, Scala, or even Kotlin could fulfill that role (I have
no experience in any of these). Although, I am skeptical if JVM based
languages could work well for that given their long start up times.

~~~
dom96
I'd say Nim fits that description best. It's a great compiled, efficient,
statically typed Python alternative.

------
Sohcahtoa82
I disagree with criticism's of Python's lack of encapsulation. I prefer the
"responsible adults" method of dealing with things that should be private.
Prefixing with an underscore to tell users "Don't mess with this", and
prefixing with double-underscores to tell users "DO NOT mess with this!" is a
perfect compromise.

If someone hands you a gun and tells you "Don't shoot yourself in the foot",
and you proceed to aim down and pull the trigger, it is not the fault of the
person that handed you the gun that you shot yourself in the foot.

Lack of encapsulation is a feature. It allows greater flexibility. But
flexibility requires responsibility.

I've never understood the desire for multi-line lambdas. At that point, what
makes a lambda different from a function? Just seems like a $1,000 term for a
$5 concept.

And decorators are wonderful things. The ability to alter functions in a
single line is beautiful.

TypeErrors as described by the author are just sloppy programming. If a
function is expecting a string, why are you passing it something that's not a
string?

~~~
kiriakasis
I saw the examples differently; first the author notes that it is easy to mask
a private attribute and that docs would not help, I believe this was its main
point.

secondly and less importantly he notes that (with some weird naming practice
though) _Foo__bar might again look like something you could define. here i'm
stretching my python knowledge so i could be wrong (for example i don't know
if hinheritance affects the extended name...)

Anyhow i think the first one was the main point: "accidental and hard to debug
modification of private field"

------
luord
All three criticisms are entirely based on opinion:

• One either likes, doesn't care about or doesn't like encapsulation. I fall
in the second camp so... meh.

• One either likes, doesn't care about or doesn't like functional programming.
I fall in the second camp so... meh.

• One either likes, doesn't care about or doesn't like static typing. I fall
in the second camp (although I admit I do have a preference for dynamic
typing) so... meh.

On that third point: Whenever anyone gets into the "but muh type checks at
compile time!" I just think, always, that one should write unit tests for
everything anyway, no matter the language. The only difference between static
and dynamic languages in that regard becomes, then, that the type in static
languages is checked twice: in the test and at compile time. So I was waiting
for this person to mention tests _anywhere_...

> Often in dynamic languages, even with a decent test suite, extensive changes
> can be really difficult.

And he did... with a non-point. Extensive changes _are_ (not merely "can be")
really difficult, _in any language_ , no matter the type system.

Frankly, most of this article could be summed up as "I, really, really want
Python to be Java", and the half-assed attempt at critiquing java at the end
deters that notion very little. I, for one, like Python specially because it
is very much _not_ Java and I'm glad it'll stay that way.

> But I’ll be looking elsewhere if I want to write robust software.

Why do people who write this kind of articles always include sophomoric
comments like this? Python has been around for longer than Java (or Go, which
seems to be the other language he wants python to be, but I like Go so that's
fine) and a lot of very robust stuff has been written with it.

~~~
GuB-42
Yes, we should all write unit tests, have 100% coverage, all that stuff. I
practice it never happens.

If you are lucky enough to have unit tests, they are full of holes and/or
outdated. I've seen it happen on critical software. The kind where a crash can
literally kill people, and where appropriate testing is mandated by law.

Relying on unit tests for anything is madness. They are nice to have, but they
are just another layer in a defense in depth strategy, not something you can
count on. Static checks (including typing) is another layer, just like code
reviews, functional tests, etc...

And yeah, you can write very robust code in any language, but Python doesn't
help much.

~~~
luord
> they are [...] outdated

How can an unit test be outdated? Honest question because, from what I've
seen, if whatever it was testing doesn't work anymore (or if something that
wasn't supposed to work starts working) then the test fails, fulfilling its
purpose.

In other words: I don't see how I would write a test that wasn't self-
updating, by alerting that it needs to be changed.

> Relying on unit tests for anything is madness. They are nice to have, but
> they are just another layer in a defense in depth strategy, not something
> you can count on. Static checks (including typing) is another layer, just
> like code reviews, functional tests, etc...

Not sure what you tried to say here. "Relying on unit tests is madness" I can
understand (if not agree with), but then you went out to compare it with other
practices. Is relying in those practices madness too? If so, what's the point
of anything?

> And yeah, you can write very robust code in any language, but Python doesn't
> help much.

Is there a language that does? I could turn this around and say that you can
write truly horrible code in any language, and [insert whatever language you
like here] doesn't prevent it much.

------
thehardsphere
It's refreshing to see criticism of Python that is not merely a rehash of the
Python 2 vs. 3 whining.

------
agentultra

        class Foo:
            def __init__(self, a: int):
                self.a = a
        
        class Bar(Foo):
            def __init__(self, a: int):
                self.a = a
    

The idea of "encapsulation," seems to be a synonym for _information hiding_ ,
a principle touted by Betrand Meyer[0]. Except that languages which try to
provide facilities for this principle have bolted it on in the wrong way and
we as an industry have come to accept some other feature
("public/final/private" keyword languages) to mean we have, "encapsulation,"
in our language.

Information hiding is orthogonal to class member accessors and even object-
oriented programming as we understand it today. In Haskell it's much easier to
take advantage of information hiding without requiring such extra verbiage in
our declarations.

This is just a long way to say that this isn't Python's fault. That Python
doesn't have keywords or anything to control access to class members is a
feature and not a flaw. I haven't read any introductory Python material that
tries to misrepresent or hide this fact.

[0]
[https://sophia.javeriana.edu.co/~cbustaca/docencia/POO-2016-...](https://sophia.javeriana.edu.co/~cbustaca/docencia/POO-2016-01/documentos/Object%20Oriented%20Software%20Construction-
Meyer.pdf)

 _update_ : That is, Python has _ways_ to achieve the effect but it's not a
part of the language grammar.

~~~
Ace17
> Except that languages which try to provide facilities for this principle
> have bolted it on in the wrong way and we as an industry have come to accept
> some other feature ("public/final/private" keyword languages) to mean we
> have, "encapsulation," in our language.

Actually, there's an interesting talk from Uncle Bob saying exactly that:
[https://www.youtube.com/watch?v=TMuno5RZNeE&t=2325](https://www.youtube.com/watch?v=TMuno5RZNeE&t=2325)

The idea is that public/private/protected is an attempt to restore some of the
"perfect" encapsulation we had with C; and that we lost this encapsulation
with OO langages.

------
wheaties
About the only two things I'm going to agree with here is the single
underscore and lambdas. The single underscore import behavior is, by and
large, a wart. You effectively can do "the same thing" two different ways and
get two different outcomes. It is non-intuitive and error prone. That needs to
get fixed.

------
notthemessiah
It seems to me that the author should be using Julia.

There's not much comparison with encapsulation because Julia isn't exactly
object-oriented in the sense that objects have methods, instead methods have
types that are deployed with multiple-dispatch.

Julia provides first-class support for anonymous functions:

    
    
        function (x)
            x^2 + 2x - 1
        end
    

Julia also provides full macros for metaprogramming.

And you also get a type-annotation system and type inference. Newer releases
also add support for return types.

~~~
jhasse
Too bad Julia is focusing on numerical computing and not trying to be a
general purpose language like Python.

------
hedora
I agree with the points made about python, but most of the C++ complaints were
addressed in C++11. These days, you get extremely powerful type checking with
sane compiler errors.

One cool example is the JSON for modern C++. You can write stuff like this:

json j = ... // read json

my_struct s = j;

and the compiler will check (recursively) that there is a complete my_struct
deserializer. At runtime, it presumably avoids virtual method invocations as a
bonus.

I get that some people want a GC, but python feels lower level than C++ to me
these days.

~~~
ceronman
> python feels lower level than C++ to me these days.

Yes I know that C++11 has a lot of cool things, but I find hard to believe
it's higher level than Python. Even for simple things, C++ code tends to be
several times bigger than the Python equivalent. Yes C++ might be faster, but
when you need agility, to build a prototype or a script really quickly Python
is many times better than C++ (in agility)

------
cyphar
> Go’s interface types provide a nice solution for this.

Despite all of my various grievances with Go (on various levels), I strongly
believe that interfaces were one of the things they did quite well.

I'm still making my mind up on whether Rust traits are an improvement in this
department (though to be fair they are solving a harder problem because Rust
has generics), but the fact that you can describe and use an interface without
having to update every implementation is incredibly powerful.

------
LyndsySimon
I've been tinkering with Nim lately, and while I'm far from competent with it,
it seems to address most or all of the issues brought up by the author. In
particular, it has a very flexible, strong type system.

------
dbcurtis
Python duck typing can be as strong as you want it to be. You expect an int?
Call int() on your parameter. Problem solved. If it's an int, great, you are
GTG. Was it a string containing digits? Great, it converts. Was it a user
defined class that defines __int__()? Still GTG. Something that can't
__int__()? It raises value error. Of course this only happens at run time. But
I would argue that if you depend on compile-time type checking you are not
testing everything you should be. unittest, hypothesis, and coverage are your
friends.

Learn to write constructors well, and typing takes care of itself.

Explicit, make-it-this-gosh-darn-it typing is for performance acceleration:
numpy, cython. Otherwise you should be using constructors that raise the
appropriate ValueError exceptions. Embrace the duck. Otherwise, if you want to
write Java, just go use Java to start with.

------
alsadi
I would respons with an article titled why python is favorite language and why
black is not my favorite color.

Multi statement lambdas are forbidden for a reason. Decorators are not after
thoughts to fix lambdas.

If you like nodejs and consider it beautiful, thats your favorite color.

------
deathanatos
There are two reasons why I disagree that `with` shouldn't be a function. The
first is evaluation order; the proposed function form has will leak resources
if the latter argument's expression raises, that is, in:

    
    
      with(obtain_resource(), make_function_to_process_resource())
    

if `make_function_to_process_resource` raises, we leak the resource. You can,
of course, pass a function as the first argument to fix this…

    
    
      with(lambda: obtain_resource(), make_function_to_process_resource())
    

and we're good again, but I think this is why the syntax is nice. It's sugar.

The other problem would be lexical scoping:

    
    
      with(lambda: open('foo'), |fobj|:
        data = fobj.read()  # data is bound to the wrong scope!
      )
    

Assuming we're ignoring Python 2, we can work around that,

    
    
      data = None
      with(lambda: open('foo'), |fobj|:
        nonlocal data
        data = fobj.read()  # data is bound to the wrong scope!
      )
    

The sugar gets sweeter.

(I suppose we could also copy how Ruby does scoping/binding, since we're
copying syntax. I also think this is why a let keyword is nice.)

I don't think the above arguments invalidate his general argument for a better
multi-line lambda syntax, just the argument for with-as-a-function. (Though he
was using that to support multi-line lambdas as a potential use-case, and I
don't think that's really the case.) I'm just not as bothered by having to def
a function, in practice…

------
jcrben
I feel the same way. I'd rather use Typescript as a basic backend language but
the nodejs community is less mature, especially if you, want an advanced SQL
ORM.

There's a pull request for mypy protocols which moves further toward
structural subtyping:
[https://github.com/python/mypy/pull/3132](https://github.com/python/mypy/pull/3132)

------
jack9
The first couple points ("encapsulation", specifically no private vars) and
interface conflicts are not issues worth complaining about.

More importantly, we have 0 evidence that using private/protected in software
is better than not. People like to use them because, as the author notes later
in the Type System comments:

"Refactoring. If I’ve got a type system and I’m faced with needing to clean
things up, I can put stuff behind an interface and follow the type errors to
find everything that needs updating. Often in dynamic languages, even with a
decent test suite, extensive changes can be really difficult."

You shouldn't have a workflow of fix failures blindly and private variables
contribute to that style of over-reliance on language features instead of
actual attention to detail.

That being said, the world isn't perfect and it's common to take this approach
if you have a lot of strong language safety features and are under time
pressure (who isn't). In my experience, this situation means things are being
rushed and/or the developer is burned out to give up such fine grained control
over the code.

~~~
js8
> More importantly, we have 0 evidence that using private/protected in
> software is better than not.

I would go further. I think encapsulation (data hiding) is a concept that is
slowly being obsoleted by functional programming style, having "naked"
(structurally visible to all programs) immutable data types. One of the
promises of encapsulation is breaking global state into smaller pieces, but
the approach where you avoid state entirely is superior to that.

~~~
yen223
Immutable data types are a great idea, but man are Python's implementation of
them so bad. Tuples are okay for really simple structures, but the lack of
attribute access makes them a little clunky to use.

Fortunately, Python has namedtuples right? Holy shit is it such a pain in the
ass to use. The way to declare namedtuples are some of the ugliest code I've
seen anywhere, not just in Python.

    
    
      Point = namedtuple('Point', ['x', 'y'])
    

Taking a list of strings to define attributes? Having to write the class name
twice? Oh and as an extra fuck you to good coding style, a namedtuple instance
has underscored methods that are intentionally meant for public use -
[https://docs.python.org/3/library/collections.html#collectio...](https://docs.python.org/3/library/collections.html#collections.somenamedtuple._make)

Fortunately 3.5 introduced the NamedTuple class that sort of fixed everything
right? Of course I'm talking about `typing.NamedTuple` class, not
`collections.named_tuple` - clearly two different things and definitely not
confusing at all. Well, here's how you use that:

    
    
      Employee = typing.NamedTuple('Employee', [('name', str), ('id', int)])
    

Just shoot me now.

3.6 fixed that, but I'm too tired to care anymore. I feel so frustrated just
thinking about some of Python's recent daft design decisions. Hugely
disappointing considering Python used to be rightfully lauded for having good
style.

~~~
js8
Sure, Python is not a very functional language and will never be. I liked
Python a lot in the past, and still use it as quick tool, because I know it
well and its simplicity for practical usage is hard to beat, but the future of
SW development is something more like Haskell, in my view.

What I meant was that the author complains that Python does encapsulation
badly, because it doesn't have private/public. But that itself is, IMHO, an
obsolete view which ignores functional programming.

------
nikofeyn
i have only used python for small (but professional) projects. i really did
not enjoy working with the language. it just feels so sloppy and unprincipled.
when i worked with it, it felt i constantly met strange quirks (the underscore
business is a good example), much more than any other language i have learned.

i could probably think hard and remember the examples, as i realize i just
said "feels" here, but i think that that's actually important. as i am writing
code in f# or racket, i feel at ease. these languages are consistent and
relatively simple in their experiences. how you feel when using a tool matters
a lot in addition to technical talking points.

python feels like it ignores language design principles on purpose and not for
a lot of good reason. but unfortunately, it's used everywhere. it's especially
bad in scientific contexts where scientists don't know better and are lured in
because of its decent selection of available packages.

~~~
cirgue
> it's especially bad in scientific contexts where scientists don't know
> better and are lured in because of its decent selection of available
> packages.

Packages are _the_ motivation for using the language. No other language has
both the array of scientific and math-related libraries and a robust non-math-
related ecosystem. I know that there would be enormous demand for alternatives
but none exist so far.

~~~
nikofeyn
i agree that python has a surprising amount of useful packages, but unless you
are using those unique-to-python packages, then i honestly don't understand
why you would choose the language. i have seen it used in two cases where it
shouldn't have been used. one had no use of special python packages and to be
honest, the ecosystem was a mess. so i disagree with the robust ecosystem
part. the other case was transitioning academic work to a product. it's
obvious they used python in their academic work but to continue using it for a
product with user interface and speed requirements made no sense to me.

~~~
Daishiman
It's not in general a good idea to use a slow, interpreted language for a GUI
product with performance requirements.

However, UI development is complex in _any_ language, and Python's bindings
for toolkits are comparatively much better than most other languages in
similar domains.

------
jcalabro
A comment I find myself returning to whenever this conversation comes around
is from Eskill Steenberg: "In the beginning you always want Results. In the
end all you want is control" [0].

[0]:
[https://www.youtube.com/watch?v=443UNeGrFoM&t=21s](https://www.youtube.com/watch?v=443UNeGrFoM&t=21s)

------
viraptor
That types criticism is a little bit old now. 3.6 will happily accept type
annotations, and mypy can check them.

While I get what the decorator complaint was about, the example was just
weird... My first follow up question would be: so how do I route 2 paths to
that function?

    
    
        @route ('/')
        @route ('other')
        def ...
    

seems like a trivial answer. Now, how do I do that with that block-taking
function? Maybe I can pass in an array of paths, maybe not...

------
joaodlf
Python type hinting is a nice newish feature. It needs to go further, though.
PHP (gasp!!) for example is making the right strides when it comes to runtime
checking.

~~~
crimsonalucard
What is the point? Comments do the same thing, Why aren't they adding optional
type checking?

~~~
joshuamorton
What do you mean? MyPy supports comment based syntax for python <3

------
jondubois
Underscores tend to get abused in Python.

There's something depressing about code that is littered with double
underscores.

Django's ORM can get fugly! Lines like this are quite common:

tr_rowid_debtor__de_listed_date__lte=to_date

See [https://stackoverflow.com/questions/21319832/what-do-
double-...](https://stackoverflow.com/questions/21319832/what-do-double-
underscores-indicate)

That said, Python itself is a decent language.

------
flavio81
TL;DR: Author complains because he wants to program Java-style within Python,
and he can't. He doesn't mention _real_ outstanding things that Python needs
to solve like the threading/async features versus the Global Interpreter Lock
(GIL). No, just trivial stuff.

Detailed reply:

\- The author complains: "Most obviously, there’s no such thing as ‘private’.
The convention is to prefix things with an underscore, as a way of saying to
the user, “don’t touch.”"

Yes, underscore is just a convention. But the author ignores that a good
Python IDE, like PyCharm, will automatically and explicitly warn you whenever
you're accessing an 'underscored' member from outside.

\- "Python has lambdas, but they’re seriously hamstrung: They can only be a
single expression. No statements."

The author ignores that functions are pretty much first class in Python. Thus,
if you want a multi-line 'lambda', just define a function and pass it around.
Yes, they will not be anonymous, but functions can be defined at any inner
block, as well, so if you do it that way, you will have no name conflict
problems. Very easy to do.

\- The _with_ statement. "If you had multi-statement lambdas in the language,
you could do something like:"

I guess the author has not used Python enough, since the example he shows _can
be done in Python_. As I mentioned, you can pass a function as an argument
pretty easily.

\- "Decorators, on the other hand, are just a band-aid over the lack of multi-
statement lambdas."

The author hasn't got a clue of what is the usefulness of decorators.

\- Again, complaining about decorator use in Flask:

    
    
        from flask import Flask
        
        app = Flask(__name__)
        
        @app.route('/'):
        def index():
            return 'Hello, World!'
    

The author complains that he needs to define a name for index(), while this
isn't needed in Ruby (under Sinatra).

Well, the author should use the Flask framework a little bit more before
complaining, because the reason one needs to define a name for the index()
function is to be able to... refer to this function later in many occasions!!
For example, if i want to get the actual URL that is routed to index() i can
use:

    
    
        url_for('index')
    

In this way, you can separate the _routes_ from the view controllers, and
thus, you can change the route paths without having to break many parts of the
code.

\- The rest of the article, the author complains that Python "does not have a
Type System". However, the author conveniently ignores many things:

a. Python allows to call functions using keyword arguments, which strongly
helps preventing bugs since the order of parameters is not going to affect the
code _and_ you clearly know what input parameters are you assigning values
too.

b. You _can_ always insert assert() statements to have a form of type
checking. A good IDE, like PyCharm, will then warn you whenever you are
calling a function with wrong types, and in runtime the type error will be
catched as well.

c. If you want static typing at 100%, why are you using Python? Wrong choice.

d. There is a specific documentation style that, if you're using PyCharm or
another decent IDE, will show you the documentation for every parameter as you
are writing your function call. If, having this feature, the coder still makes
mistakes calling a function with wrong parameter types, then the coder is too
stupid to use Python.

e. Python is strongly typed, so a wrong input type to a function will cause a
runtime error pretty soon, anyways. And, in my opinion, the bugs caused by
type mismatches, in a strongly typed languaged, are trivial to solve. The bugs
that worry me are the difficult to solve ones, the ones caused by wrong
understanding of the problem, etc. Those bugs will never be prevented by a
type system.

~~~
coldtea
> _TL;DR: Author complains because he wants to program Java-style within
> Python, and he can 't. He doesn't mention real outstanding things that
> Python needs to solve like the threading/async features versus the Global
> Interpreter Lock (GIL)._

The author is not concerned with "hard stuff". He is concerned with language
(syntax, etc) criticism, which is a different thing.

And, no, he doesn't want to "program Java-style within Python" \-- that's an
easy way to dismiss any kind of language criticism.

~~~
flavio81
The author wants: True private and protected class members (restrained by the
compiler), static type checking, interfaces, and he wants to make good use of
abstract classes.

The author wants to do Java in Python. He should better use Java! Java has all
that, it is significantly faster than Python, it has even more libraries, it
is easy to learn, it has great IDEs and a lot of codebase and support. Java 8
even has a sort of anonymous function support.

My point is -- if you use Python then you need to take advantage of its
differences, instead of wanting to type code exactly as in other language.
Kind of makes me remind of the clever one-liner on "Real Programmers Don't use
Pascal":

    
    
       FORTRAN programmers can write FORTRAN code in any language.
    

Mind you, Java is my least favorite programming language, but if I wanted to
make use of all those features, i'd be choosing Java or C#, easily, no-contest
there.

~~~
coldtea
> _The author wants: True private and protected class members (restrained by
> the compiler), static type checking, interfaces, and he wants to make good
> use of abstract classes._

None of which (and not even all together) were first, exclusively, or
specially in Java.

And none of which are what make (or made, circa J2EE/EJB/XML craze) Java
painful.

> _My point is -- if you use Python then you need to take advantage of its
> differences, instead of wanting to type code exactly as in other language._

Sure, but those differences have to be different for good reason, not just
because "well, that's what Python offers".

Else one is justified to criticize them.

------
Dowwie
If you're going to complain about what Python lacks, at least verify that your
argument is valid as of Python 3.6

~~~
cwyers
If you're going to complain about an article not mentioning something recent,
at least verify that the article postdates the thing you want it to have
mentioned.

------
heinrich5991
I think there are truly private class members in Python, prefix the name with
`__`.

------
wdiamond
just go c and javascript. or javascript and c. some prefer lua and c, or c and
lua.

------
flaviuspopan
I've noticed a new phenomena when it comes to posts on HN that criticize or
complain about something I like. I find myself really drawn to it because I
know that the comments will have people respectfully disagreeing and proposing
alternatives that have a high chance of being useful to others. I used to
downvote those kinds of posts on reddit back in the day, but this community
makes disagreement progressive. You guys rock.

~~~
theandrewbailey
You hate the things you love.

------
douche
Python feels to me like a nifty little language that's been asked to do to
much and had too much cobbled onto it, bending it all out of whack.

I have some fond memories of hacking around with it, about 10 years ago -
although for some context, I was coming out of the wastelands of Visual Basic,
pre-modern C++, and AWT/Swing era Java. I'm not sure there's anything easier
for quick and dirty 2D game development than the PyGame SDL bindings were.

But I'd cringe at the thought of building anything serious with it today. It
feels too loose and sloppy, like trying to build a castle made of sand,
although I might have some Stockholm syndrome from years of more static, more
strongly typed languages. But I have to do enough grepping around through the
code base now to make sure tweaking something isn't going to break some
snippet of JS code on the front-end now; I really don't need to be worried
about that on the back-end too.

~~~
sametmax
You have type hints in Python now. With Pycharm as an IDE, you will feel right
at home.

~~~
BuckRogers
Those are ignored by the Python bytecode compiler though, and aren't used to
enhance performance or anything else. If you don't mind type annotations that
are for the IDE only you may as well just reach for TypeScript instead of
Python.

~~~
sametmax
> Those are ignored by the Python bytecode compiler though

That's the whole point.

> you may as well just reach for TypeScript instead of Python.

The only reason I do JS is because it's the only browser native language we
got. I'm never gona use this aweful tech anywhere else if I can.

~~~
BuckRogers
> That's the whole point.

It is? I don't think so. My whole point was that they're useless from a
performance perspective.

>The only reason I do JS is because it's the only browser native language we
got. I'm never gona use this aweful tech anywhere else if I can.

That's why TypeScript exists. It's better than older versions of ECMAScript
and Python, put together.

------
whatnotests
After starting with about 10 years of Perl, then 6 years of Ruby on Rails, and
finally entering the world of Django I see the following problems:

++Lack of Consistency in Project Structure:++

* The Django way of "several applications are in your application" creates many folders, each with either a models/ folder or models.py.

++Django ORM:++

* Writing a model which generates a migration, prefixes table names based on the "sub-application" name in my application is just awful. The default names for these tables is just another layer of cognitive load to bear.

* Implicit relationships between tables, vs allowing me to specify has_one/has_many/belongs_to/has_many_through/etc relationships.

* I miss FactoryGirl. Specifically I miss FactoryGirl's feature of allowing me to declare associations between my factories, then creating upstream objects in the proper order. Without this feature, I get a lot of repeated 'setup' code in my unit tests, which makes my tests brittle, and lowers their value.

++Django Testing:++

* Writing unit tests without the describe/context/it style feels like I just took a bus back to 1998. Combined with the lack of FactoryGirl, I see test suites that barely test the "happy path", let alone any "sad path" cases.

* Due to the brittle boilerplate setup code in unit tests, minor changes to the code tend to break tests badly, further slowing down the pace of development.

* The alternative to constantly fixing broken tests is to have fewer tests (or no tests) which is even worse IMO, since only your customers will test code code.

* The Rails way of writing a migration, making factories, making model specs, then controller specs (and finally features for end-user acceptance tests) seems to have gone completely off into an unknown future from the perspective of Django, who was left behind, frozen in time at the end of Bill Clinton's last term in office.

~~~
satyanash
> I miss FactoryGirl ... I get a lot of repeated 'setup' code in my unit
> tests,

Recently discovered these two projects, thought I'd mention them:

* Model Mommy ([https://github.com/vandersonmota/model_mommy](https://github.com/vandersonmota/model_mommy))

* Factory Boy ([https://github.com/FactoryBoy/factory_boy](https://github.com/FactoryBoy/factory_boy))

~~~
whatnotests
Oh wow. FactoryBoy doesn't appear to handle relationships but ModelMommy does.

Thanks for sharing!

------
crimsonalucard
>It’s pretty well known that Guido van Rossum isn’t big on functional
programming. Python tries pretty hard to make functional programming painful,
to a degree that seems rather extreme, given how simple the fixes for the pain
points would be.

>Python has lambdas, but they’re seriously hamstrung: They can only be a
single expression. No statements. Furthermore, Python has a bunch of built-in
language features that are totally obviated by adding a decent syntax for
multi-statement lambdas.

This guy doesn't understand functional programming and why the lambda exists
as a single expression.

Either way I agree it's better to have multiline lambdas but I disagree with
how he related it to Guido making functional programming painful... If lambdas
had multiline statements then the lambda would not be following the functional
paradigm. In functional programming EVERY function is a single expression,
including the main itself. In fact, in functional programming, your Entire
program is just a single expression.

~~~
zenhack
> In functional programming EVERY function is a single expression, including
> the main itself. In fact, in functional programming, your Entire program is
> just a single expression.

This is a technicality. There's basically always a mechanism for chaining two
expressions and junking the result of the first. In OCaml, the semicolon
operator does this. In Scheme (and most lisps) you can just put multiple
expressions in the body of the function, and the return value will be the last
one. And there's `begin` if you want a block that's not a function. In
Haskell, the purity makes this _actually_ not make sense in most contexts, but
you've got `(>>)` for monads, and the do-notation which sugars it into
something that looks basically like a statement.

Some version of that in python would be an acceptable alternative.

But there's also stuff like moving reduce to a separate package.

------
madshiva
I hate python because of the indentation. I know it's stupid, but I hate it.

~~~
sametmax
It's not stupid, it's taste.

I have ruby for having "end". It's just taste.

I like Python because it forces people to write well indented code. I worked
with too many pigs.

~~~
jacquesm
No, it goes beyond taste. The inability to reliably cut-and-paste example code
and the total mess you get when people have combined spaces and tabs which
could result in ambiguous code is a danger that could have been side-stepped
quite handily.

It's like driving on the autobahn without guardrails. You may only need them
once or twice in your life and yet, you're happy the guardrails are there.
Just in case.

Allowing both tabs _and_ spaces was simply a mistake but it is too late to
turn the clock back on that now.

[http://wiki.c2.com/?PythonWhiteSpaceDiscussion](http://wiki.c2.com/?PythonWhiteSpaceDiscussion)

In general, significant whitespace isn't fantastic but at least now we have
widespread version control which helps in case of accidental
deletion/insertion of a space or a tab or a newline.

~~~
pmontra
Yes, but having to do a git diff to verify if I refactored a function
correctly is telling about the shortcomings in the design of the language.
Indentation should be left to the editor: RETURN starts a line at the correct
indentation level (one more after a : ) and anything convenient to decrease
indentation (example: TAB TAB in emacs)

This is my major pain point with Python. No code blocks as arguments and
single line lambdas are the second one.

And those underscore looks so 80s, but that's when van Rossum started
designing Python. Same thing for the self argument in method definition: it's
the pointer to the struct holding the function pointers and instance variables
in object oriented C (without ++).

On the other side

    
    
       object = ClassName()
    

is more convenient than

    
    
       object = ClassName.new # Ruby
       object = new ClassName(); // Java

~~~
sametmax
Editors have nothing to do with this. You have dirty coders everywhere on the
net.

Having to edit a bit a code you copy paste from an badly formatted source
(which you should do anyway for safety) is a small price to pay to avoid all
the dirty coders in the world to write blocks with a showel.

~~~
pmontra
It's not from a badly formatted source: it's my well formatted working code. I
just did it a couple of minutes ago: I added an indentation level into a
function and went TAB, ARROW DOWN through 10 lines of codes in emacs. There
was a try except in that code and I had to pay attention to the line after the
end of the except block.

I know that I should select the region and do C-c > to move it one level to
the right, but it's not convenient. In any other language the editor indents
it with one keystroke (actually it's 3 keystrokes: C-M-\ because it's
emacs...) That's why TAB, ARROW DOWN is faster, when it doesn't add bugs.

~~~
sametmax
Why is everything so compliated in emacs ? Sublime text, pycharm and vscode
all handle that fine automatically.

------
wyldfire
> I die a little inside every time I see NameError or TypeError in a server
> log. Even Javascript can catch undefined variables before runtime (with use
> strict), but not Python.

It's not like this is an accident, it's by design. Python pretty much waits
until <<right now>> to bind things to names. It's part of the reason that it's
"slow" and relatively challenging to make "fast." But OTOH you can do really
interesting powerful stuff. "But I don't do that stuff"? Ok, maybe indeed
Python's not for you.

As a simple fix for this problem, vim users can use syntastic, atom users have
a/several python plugins to check syntax, I'm sure VSCode and emacs have
analogous plugins. Yes, their power is limited to only some kinds of checks,
so TypeErrors can slip through.

~~~
willvarfar
Since I started using the free community edition of pycharm, almost all of
these kinds of typos have been caught by the IDE! Really recommended.

