All this hate is justified, but not the conclusion.
The libraries available in Python, for everything touching data / scripting are just so powerful and well thought, that for many small projects, using Python is a no-brainer. The best thing is that those libraries are actually incredibly fast, while Python is supposedly slow.
A number of these points seem like reasonable opinions to have. But two which had me questioning the breadth of the author's experience were "Most programming languages pass function parameters by value." and "In every other language, arrays are called 'arrays'. In Python, they are called 'lists'."
To the author:
1. Java, JavaScript and C# all have types which are passed by reference. (They also have types which are passed by value.)
2. Python lists are not arrays, if by arrays you mean a C- or FORTRAN-style block of non-resizable memory which is indexed by position. Python lists are much more like the Java List or C# IList interface.
Python (and Java) do not pass anything by reference. They pass by pointer-value. (If they passed by reference, you could change to which value a caller's variable was bound, like you can in C++).
> (If they passed by reference, you could change to which value a caller's variable was bound, like you can in C++)
I might be misunderstanding you, but I don't think you can do this in C++. References can't be changed to point to a different object after initialization. If you have code like:
The assignment above isn't "changing the value to which a caller's variable is bound". Instead, it's running the '=' operator on the Foo object to copy the state from new_foo to ref_param. To demonstrate this, you could run the following to see that the addresses are the same:
Foo original_object;
Foo& object_ref = original_object;
MyFunc(object_ref);
// object_ref still points to the same address
assert(&original_object == &object_ref);
Under the hood, C++ reference params are basically syntactic sugar for passing by pointer-value, so this behavior isn't surprising.
The proper analogy for a Python object is `std::shared_ptr<Foo>`: in Python, all "objects" are actually reference-counted pointers to objects (thus making them shareable). And then yes, in C++, you can change to which object the caller's "object" (pointer-to-object) points to, if you pass a reference to that. This is not possible in Python: all you can do is pass that shared-pointer value around, not the value of the object itself, nor a reference to the shared pointer.
Ah, got it. Your basic point is that C++ params can use two layers of indirection (pointer-to-ref, ref-to-pointer, pointer-to-pointer, etc.), while doing so in Python is clunky. Makes sense.
Those languages use references rather than pointers (that is, their referring-things do not support arithmetic), so any name for what they do that involves "pointer" is a poor one.
The distinction between passing the value of a reference being used by the caller and passing a reference to the caller's stack is so rarely important or useful that there's no consensus terminology for it. The distinction that's actually relevant and useful is whether, when we write f(a), f receives the (thing we would call the) value of a or a reference to the value of a (and in particular whether f can change the value of a). In Python, f can change the value of a (that is to say, the thing Python programmers understand to be the value of a); therefore Python is pass by reference as the term is usually understood (and certainly any term for its call style that uses "value" is more misleading than calling it "pass by reference").
"The distinction between passing the value of a reference being used by the caller and passing a reference to the caller's stack is so rarely important or useful that there's no consensus terminology for it."
And the reason for that is essentially all modern languages make copies of something when passing argument parameters. The only question is what they are passing and exactly how hard the language layer works to make it look as if you are or are not passing something by reference.
I have to reach to something as obscure as Forth to find a language that does not work that way, and truly does not copy parameters into a function. (I haven't dug into Factor enough to know if under the hood it still copies parameters.) It's a very unusual choice to not copy something for a function call.
(This is one of the ways our CPUs have been optimized for the languages we run; they make this intuitively expensive operation otherwise cheaper than it would be on a naively designed CPU.)
> And the reason for that is essentially all modern languages make copies of something when passing argument parameters.
Then of course someone turns on whole program optimization for C++, half the code gets inlined into one terrifying large function, nothing is copied anywhere, and the code runs 2x faster.
Then some poor SOB has to debug the assembly code for all of this and has nightmares for years after.
"Pass by value" and "pass by reference" are both wrong with respect to Python. They denote semantics that Python does not have, and coders believe and do wrong things if they believe Python follows one or the other. Hence a 3rd term is needed. I don't care what you call it, because it's just a name, but it is a distinct idea, and I'm not the only one who thinks this: https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sh...
> In Python, f can change the value of a
but it can't. `f` can't change the object which `a` references:
def f(x): x = 5
is a function with no visible effect. When called as `f(a)`, `a` retains whatever value it had previously.
People who believe Python is "call-by-reference" don't understand this and write incorrect or overcomplicated code as a consequence. The distinction is important.
There is no consensus that "pass by reference" denotes that, whatever a wikipedia editor says. Normal working programmers do not understand "pass by reference" to mean specifically "pass a reference to the caller's stack" but only the more general concept of "pass a reference to the value of a". If you want to talk specifically about what kind of reference gets passed, you need some new terms for that, and both the new terms you come up with will be subtypes of what most of the world will continue to understand as a general category of "pass by reference". No-one outside of these arguments on HN gets confused about whether you can write a swap function in Python; people understand "pass by reference" to mean something more general and entirely true of Python (and Java and so on). You can tell by how often we see those languages described as "pass by reference".
> but it can't. `f` can't change the object which `a` references:
Yes it can. It can't make a to refer to a different object (as your f tries to), but it can change the object a refers to just fine.
No, Python doesn't even do that, because Python variables aren't names for storage locations to begin with. They're namespace bindings. Passing a variable to a Python function means transferring a namespace binding from the caller's namespace to the function's local namespace.
In C#, all variables ( reference or value types ) are passed by value. Yes, even reference types are passed by value. If you want to pass variables by reference, you need to use special modifiers ( out or ref ).
Right, but this is the same way Python works. Jumping back to the original argument in the post, the author claimed that Python was "going out of it's way to be different" by passing objects by reference value. C# provides a counterxample to that claim.
You are correct that changes to the object will be reflected in the calling code. But that's the nature of the reference, not whether it is called by value or called by reference. Whether you pass a reference by value or by reference, the changes to the object will be reflected in the calling code. And as I noted, it's all passed by value in C# unless you use modifiers ( ref or out ).
The difference between "pass by value" and "pass by reference" for reference variables is how the variables themselves are handled. For example, if you pass a reference "by reference" to a function and set it to null, then the reference variable in the calling code is also set to null and will cause null ref exception if you try to access the object. Whereas if you pass a reference "by value", if you set the variable to null, the calling code variable isn't affected.
Pass by value and pass by reference is not about objects or types really, it's about variables.
What Java, JavaScript and C# call "arrays" are each very much like what Python calls "lists'. In particular the indexing after push() and pop() type operations and the bigO of indexing.
More abstractly, Historically, in computing "list" connotes pointers and "array" connotes sequential memory. The connotations imply engineering tradeoffs. [1] "List" may make more sense for a beginning programmer. It is an arbitrary context switch for programmers writing in multiple languages. Considering that one of the driving use cases of Python has been systems programming, "list" is misleading regarding performance characteristics. [2]
[2]: googling "python arrays" returns a lot of results explaining the difference between Python's Arrays and Python's Lists. "List" is "foo => spam" and "bar => eggs" Pythonism gone too far.
Not true for Java or C#. In both of those languages, arrays are fixed-size and don't have anything like a push or pop operation. Both have an automatically resizing container backed by an array that is referred to as a list. Python's use of "list" matches the use in Java and C# perfectly.
Requiring a type definition, Python's Array type more closely matches Java and C# Lists than Python's List type.
In Python, Arrays are sequence types and behave very much like lists[1] In other words, even in Python, arrays have similar semantics to Javascript Arrays not Java Arrays.
What Java, JavaScript and C# call "arrays"
are each very much like what Python calls
"lists'.
In Java and C#, 'arrays' are fixed size at creation time.
Both Java and C# provide 'lists' which are variable sized*
Python 'lists' are variable-sized.
Seems like a consistent naming scheme to me?
*there might be an underlying array that gets reallocated - but it's encapsulated within the list object; the reference to the list object is unchanged when this happens.
> Most programming languages pass function parameters by value
The biggest irony here is that if you wrote your code in C instead, you would actually pass more arguments by reference than in equivalent python, because you're going to use pointers for everything but primitive integer values.
No, the biggest irony is that C as a language does not even have a syntax for passing by reference. Everything, including pointers, is passed by value. References were introduced in C++.
Java passes by value. The value being passed happens to be a copy of a pointer (properly called a "reference" in the spec, which confuses people). A copy of a pointer points to the same place as the original pointer. For normal use cases this works very well.
However, if you reassigned your copy of a pointer in the body of a function, the original pointer would still point to the same place it did before the function was called.
That's not the same thing as actual pass-by-reference in languages like C++.
The counter-argument is that in practice, most programmers use the indentation of the first non-whitespace character on each line to estimate lexical scopes, which is why removing all indenting drives most programmers wild.
I've seen several bugs in C++ and Java where colleagues have indented the code for the correct control flow, but misplaced the curly braces, resulting in incorrect flow. Granted, the two bugs that come to mind first are dangling-else problems that were fixed by inserting the optional braces.
I think that you and I both prefer auto-formatters to force indenting to match flow control. However, there's an argument to be made for actually enforcing it at the language level rather than at the tooling level.
Interesting points. I disagree with Reason 2 though. Python has smoother installation than Java and GCC.
One of the reasons Python is popular is because it's easy to do a lot of things. Some python choices don't make sense technically, but they were made to make python as easy as possible. Performance was never the first criteria of Python (or ruby). My personal peeve is with mandatory indentation. But again, that's the idiom the language has adopted.
>Python has smoother installation than Java and GCC
How so? With java you can get your distro version from the package manager or just download a tarball and setup your PATH. Maybe install maven. GCC and build deps is as simple as a xxx install build-essential/build-base and you have a C compiler for C89/C99/C11.
I think the huge volume of 3rd party libraries that Javascript and Python have show that prioritizing developer experience over technical perfection leads to a more useful language. I hate Python's quirks but it's the first language I reach for if I'm just fetching data from an api or doing some web scraping. It's so fast and expressive that it makes up for the negative parts of the language.
Many of the reasons authors states are quite silly. Personally I don’t like the huge perf hits everywhere you look. For example, a bool in Python takes whopping 24 bytes! Multi threading is a giant mess due to GIL that apparently no one can get rid of. Things like true static variables are missing. Import behavior differences for programs and modules is baffling. Lambda is intentionally kept under powered (ex. no group by). Need to create new environments to avoid conflicting package versions can quickly become annoying (could have been improved by side by side versions).
Still, I don’t hate it at all. It’s good and beautiful for lot of other stuff.
Now this is a good list of things to hate about Python. I would add the fracturing of build methods for large applications is another frustrating thing about working with python. Although projects like Pipenv and Poetry seem to be bringing python up to modern standards in that arena.
I have yet to use poetry although I’ve heard great things and I’ve read a blog post that kind of sold me into it. How would you say it ranks compared to pipenv? I’ve had frustrating issues with the latter in the past month.
I can’t help but think that the lack of Kenneth Ritz in the commit list since summer has something to do with this. I don’t recall the exact scenario, but I think there was some kind of falling out between him and the community.
Even if you despise python to the core, it's hard to shrug off the massive number of well documented, mature libraries. Usually there are 2 or more choices with at least one of them being fairly mature and maintained. Not to mention the wealth of examples showing how you can do something.
So despite all the things I dislike about python, it's still my go to for a proof of concept or getting something done quickly.
the self argument for methods/method calls is stressful (having to define self for methods and having to call a method via self); also the fact that you need to check if dictionary key exists, else you get an exception when trying to get the keys value.
Also ':' at the end of each line.
I always forget at least one of these.
You didn't have any of these goodies in good old perl (sob, sob)
(wow, this one got flagged pretty quickly, wonder why)
you need to check if dictionary key exists, else you get an exception when trying to get the keys value.
What should be the correct behavior when accessing a key that doesn't exists? If you want a specific default value when a key doesn't exist you can use the get() method.
Also ':' at the end of each line.
What do you mean? You don't need ':' at the end of each line, only at the start of a block (basically anywhere you'd use a '{' in other languages).
Idk, maybe because your arguments are bit shallow?
I like 'self'. There's a lot to complain about magical 'this' of every other language - especially in Javascript. Specifying self makes it really clear if it's a member method or a standalone function.
Python really likes the idea of 'seek forgiveness, not permission', aka. abusing try/except. But it's kind of nice:
# seek permission
if 'key' in map:
fn(map['key'])
else:
whatever()
# seek forgiveness
try:
fn(map['key'])
except KeyError:
whatever()
Just pray that 'fn()' doesn't also throw a KeyError.
It's been a while since I touched Python (thankfully), but there's also:
- The hideous __method__ and _private conventions
- A friend of mine was complaining about the implicit string concatenation: ["foo" "bar", "baz"] whoops forgot a comma and now there's a very hard to find bug
- pyc and pyo files littering the filesystem after running (yes, only a minor nuisance)
- import anywhere, the ugly __name__ = "main" hack
- GIL, reference counting GC, abysmal performance in general
- Dynamic typing, which is probably my most major complaint but I realize opinions differ
C and many other languages had that before Python, and it’s very useful to be able to split a single string over many lines in your source code. Python very wisely also implemented this feature.
> - pyc and pyo files littering the filesystem after running
"also the fact that you need to check if dictionary key exists, else you get an exception when trying to get the keys value."
dictionary.get(...) is specifically for avoiding the exception, you should just get None if the key doesn't exist. Unless I'm misunderstanding you and your complaint lies elsewhere.
Don't get me wrong, there are plenty of things that are stupid/annoying with python, this comment however reads more like "python is bad because I can't write perl in it". I think that's why people are flagging it.
I didn't downvote you, but you present complaints without support or putting forward alternatives other than everyone should ditch Python in favor of Perl. 2 of your 3 complaints come across as shallow syntactic bikeshedding presented without support.
It would be interesting to read your take on the engineering problems involved, the design tradeoffs involved, and why you think Perl's decisions are more appropriate than Python's decisions, even for Python's main use cases.
Python rubbed me the wrong way when I first encountered it, then I really loved it for a few years. Now, I feel it's pretty good at what it does, but I find other languages more interesting.
Maybe you have some good reasoning behind your gripes, but you don't provide any evidence that you've thought much about them or put any effort into understanding the design decisions.
For your first complaint, I do agree that the explicit self parameter vs just making it a keyword is hard to justify in retrospect, but is it really raising your blood pressure, causing you to lose sleep, etc? Your use of hyperbole doesn't help your case.
I presume that making all method calls explicit is to keep scoping sane in the case where a subclass defines a method that happens to accidentally conflict with the name of a function called in some superclass's method. An alternative would be to use static name resolution to resolve the ambiguity, but then you'd have to be statically invoking metaclasses to figure out which methods were in scope, or you'd have to get rid of metaclasses and generally get rid of a bunch of the effort Python goes through to be very late-binding and generally dynamic in behavior.
For your second complaint, if you don't want the dict to throw, use the get method instead of the brackets operator.
For your third complaint, I'm not sure exactly what you're complaining about. Clearly not every line literally ends with a colon, and you're again making poor use of hyperbole. Guido has commented in at least one interview that most of the colon's aren't necessary to disambiguate the grammar, but they really do help beginners in particular read the code.
Showing more effort toward educating and/or convincing people would help avoid the downvotes.
> It would be interesting to read your take on the engineering problems involved, the design tradeoffs
self - the reason is that I forget to type it and causes me to go back and fix the method signature or method call (there are usually lots of them in the code, so lots of opportunities to forget self - hence lots of instances where it can be omitted so that it must be fixed, at least for me; the remark is a gripe about ergonomics of syntax)
The alternative: do a different keyword for method definitions other than def; (well, still you need something to differentiate between method invocation and global function call in an untyped language)
: at the end of function definition or a block - same thing.
KeyError if dict entry not found; most other languages I know would return None, so that's counter intuitive to me (I know about get, but [ ] is the default syntax for accessing dictionaries in most languages)
I know it will cause an undefined method call to bomb, which is a good thing in my book; but I don't like it for data structure access - I also don't like exception handling in particular and prefer explicit checking of results (again matter of taste and habit + frequent cause to go back and fix it)
I think python is making several tradeoffs in favor of making the resulting script more robust (like going for exception handling) but it makes the experience of writing said script a bit more tricky.
You don't have to define 'self' in Perl code, but you should. You don't have to check if a key in a dict exists, but you should. Not doing these things, and the crappy code that results, are part of why people make fun of Perl.
$ cat > foo.pl <<EOF
use strict; use warnings;
{ package Foo;
sub new { my $class = shift; bless {}, $class }
sub method {
my $self = shift;
$self->{"hash"} = { "thng1" => shift };
my $key = $self->{"hash"}->{"thing1"};
$self->increment($key);
}
sub increment { $_[1] + 1 }
}
my $count = Foo->new()->method(33);
print "count: $count\n";
EOF
$ perl foo.pl && echo "Program succeeded"
Use of uninitialized value $_[1] in addition (+) at foo.pl line 12.
count: 1
Program succeeded
The debugger is telling us about an uninitialized variable in Foo::increment(), but the bug is really a missing dict key in Foo::method() due to a typo. We never checked if it existed, Perl didn't care either way, and the program didn't even fail. In code that's more than a page long, good luck spotting that...
> Here I am hating on the language I love, but I remember when I would have to review this question on StackOverflow every time I wanted to create a new virtual environment:
I too have tried it many times mainly because of all the great ML libs for python but each time I dreaded using it for Reason #3 (Syntax).
Using indents for blocks just seemed unintuitive and error prone to me. But I dismissed it because all the programming languages I've worked with have had curly braces so maybe
the reason for my discomfort was that it was unfamiliar.
I'm guessing it refers to moving something out of a loop, which in Python often requires only an indentation change. In most other languages, it requires moving the code to the other side of a brace, or perhaps adding a brace. If braces were considered whitespace, which is not entirely unreasonable when talking about Python vs. other languages, it would be much harder to come up with examples. Drawing a blank right now. Anyone else want to try?
Most languages that use braces also encourage you to indent the things enclosed by the braces to make it easier to read. However, people sometimes make mistakes. For instance I've seen this quite a number of times
if (a)
somestatement;
someotherstatement;
athirdstatement;
Which is of course horribly misleading unless you're careful because the second statement isn't actually conditional on a. Python forces the indent to match the semantics.
Does no one have auto indentation in their editors? Every so often I just select the block of code I'm editing and auto indent it, which matches indentation with semantics without me having to think about it. Annoyingly impossible to do this with python since indentation is semantics. It also results in copy pasting of code being 100 times harder in python than other languages.
That doesn't help with the problem you're replying to – in fact it would contribute to it.
More generally, yes, many people use auto-indenting and it works in Python in most editors. It's possible that you're using one where it doesn't work correctly but that doesn't mean that's true for everyone else.
It does help with the problem I replied to, which was code written in C. Auto indenting that code block would indent the `someotherstatement;` line correctly and I would notice that mistake. Anyway, single line if statement is a flaw in C's design.
What I said was that in Python's case, if I indent some code block wrong, I can't just fix the indentation using auto indenting in python, because the indentation is the semantics. There would be "nothing" to fix and the code would just run wrong. This problem comes up always when I copy paste python code. Of course I should notice that it's indented wrongly but that doesn't always happen. (And please don't advise me to never copy paste code. Life doesn't work that way.)
> Auto indenting that code block would indent the `someotherstatement;` line correctly and I would notice that mistake.
In my experience that last part is somewhat optimistic for the implication that someone would notice it immediately.
> if I indent some code block wrong, I can't just fix the indentation using auto indenting in python, because the indentation is the semantics. There would be "nothing" to fix and the code would just run wrong.
This is true in some cases — most of the time you'll get an indentation error instead of it running incorrectly — but also why many editors have an auto-indent option for pasted code and autoformatters covert things like code which is consistently indented to match your project's indentation size.
If this were code that I was writing myself it wouldn't be a problem because I always use braces even for single statements. And I do use an auto-indentor. But these issues come up when I'm reading other people's code, especially libraries supplied by IC vendors.
As for pasting, if you're using an IDE that ought to be taken care of for you. I use a vim and the sequence for indenting or de-indenting the text I just pasted is now second nature.
For the first 5 or so years of my career I used Algol syntax (and a bit of Lisp) exclusively. But having been trading off between C, C++, and Python since then I've come to prefer Python's syntax, though not its lack of static types.
It sounds great in theory, but it leads to problems in practice. If you copy and paste code from any language with C style syntax, it's trivial to fix the formatting based on where the braces are, and your editor can help. With python, testing a 10 line example program from a forum post can turn out to be quite difficult. Not to mention, if a tab character ends up anywhere in your program, you now have a syntax error that's completely invisible to the eye. Lastly, it makes it difficult to have clean syntax for multi-line statements.
At some point in my Uni I thought of writing a "curly braces to python" translator that will take code in "pseudo python" which allowed curly braces and would convert/indent it the correct way.
A lot of these gripes are unfair and don't really matter IMO. I think complaining about things as trivial as syntax is really just scratching the surface and doesn't serve your conclusion.
A dictionary is an interchangeable word with hashmap in most situations. The string 'quirks' actually look to be pretty helpful.
Gripes I do actually agree with: the schism in versions, mutation from pass by ref (although this isn't different from JS, Java or any other OO lang). In my opinion you can add OOP in general to the list of gripes against python.
Reason 2 is getting out of hand for Python, Ruby, JavaScript, Haskell and OCaml.
These so called package manager can't manage packages at all. I'd like to call it package downloader. They just download everything from language interpreter to library to a single directory and call it job done. No easy way to remove the package. The interpreter and library's version is forever fixed. Theoretically, it can upgrade the version but the user mostly don't.
> They just download everything from language interpreter to library to a single directory and call it job done.
This is scoped to the project in npm. In Ruby, you can specify the path (common to "vendor" your gems). You can use version managers (like rvm or nvm) to scope your libs to versions or even named projects (thinking of rvm's "gemsets")
> The interpreter and library's version is forever fixed. Theoretically, it can upgrade the version but the user mostly don't.
I can't speak for all ecosystems, but on Ruby projects I've been a part of, keeping gems updated is pretty common. Github even includes monitoring for vulnerabilities to encourage this.
Having been part of ecosystems that don't have package management (for example, ColdFusion) even the worst package managers are a major improvement. (Though I do think the isolation of python's virtualenvs is a better approach than most systems)
This article illustrates amateur hour in many
ways.
The whole can't manage my environment and don't
understand how to manage the python thing at all
has my sympathy. Python is messy at that level.
With the organizational 'control' sysadmin deprecated
by devops and containers/automation becoming the
new norm these types of complaints have to be
dealt with by people who have no idea of what
they are doing in generalized context.
2. Python libraries' documentation leave a lot to be desired (compared to good javadocs). Just because your language is dynamically typed doesn't mean you don't need to describe what the expected shape of a parameter should be.
3.
Static method? @staticmethod
Static variable? declare it outside of your methods but inside your class
Private method/var? name it: __name (but it's not really private just hard to find)
I'm sure other Java programmers have had a culture shock coming to python as well. I appreciate how concise the language is, the great community and module ecosystem, and how productive it is. But, it feels to me like there's features missing (I've only been seriously programming python for 5 months though, before I was basically just scripting and writing java code in python).
Also the most fun part of learning python has been list comprehensions and itertools, they've really been a revelation. Like when I first learned Ruby procs, blocks, closures and lambdas.
> Static variable? declare it outside of your methods but inside your class
Sounds like you're still thinking in Java :). A static method is rarely what you want (and if you do you can put it on the class like for variables); functions are first-class so you can just put them in your module.
As a former certified Java programmer, moving to Python was a breath of fresh air. More concise syntax, no more braces all over the place, much less boilerplate (think getter, setter, public static void main(String[] args)) and not everything has to be an object. First-class functions, yay ! (I haven’t used Java seriously since 1.6 so I don’t know how much the language has evolved since then). The only reason I’ve had to go back to Java is specific libraries, and speed.
Python’s philosophy of convention rather than constraint is a nice chance from Java’s corseted mindset.
But I’ve never had to use Python in a large development team, so I don’t know how well it holds up with a large group of devs.
As an aside, if you have fun with comprehensions, itertools and lambdas, I urge you to take a look at functional programming!
Java has changed a great deal since 1.6, and includes something like first-class functions. But Java's main benefit over python is the JVM itself, which is extremely well specified and generally a rock-solid piece of kit. Python doesn't have a specification, or alternative implementations (AFAIK) and I think that hurts it.
Python is not specified but CPython serves as a reference implementation. Python has _numerous_ alternative implementations. Jython in Java, IronPython in C#, PyPy in Python (kinda...), and even more lesser-known implementations.
Coming from Java I loved Python's conciseness and convention, but as my codebase got larger I hit a maintainability wall where it was impossible to refactor reliably enough to keep the code clean and framework upgrades were always a massive task. Then I discovered Scala which combines the conciseness of Python (and allows you to write in a very "plain English" style if you want to, with the optional no-brackets call syntax) with the safety (and runtime and library availability) of Java.
The solution is to stop thinking about private and static variables :)
I've been developing Python for upwards of 10 years, and not one single time I have ever had a legitimate use for double underscore, and I've only seen one legitimate use in the wild (involving auto-generated code and C interfacing).
> Private method/var? name it: __name (but it's not really private just hard to find)
This is why I _love_ python. Stop trying to protect me from your library. I've been doing some C# work and nothing bothers me more than having to fork and re-build an entire library just because the author didn't think I should be allowed to touch some variable that should've just been `public` or `protected`.
I don't need a library author to hard-block me from things. Put up the appropriate warning signs, then get out of my way.
> 2. Python libraries' documentation leave a lot to be desired (compared to good javadocs). Just because your language is dynamically typed doesn't mean you don't need to describe what the expected shape of a parameter should be.
I used to hate python because of of the lack of static typing. I realized the reason I hated it because of that, was due to other people's bad code. One of the nice things with compiled languages that are statically typed is that there's that nice check up front for types. I actually think you need to be a really good developer to properly use dynamically typed languages.
A better reason to hate Python: the internal model is way overcomplicated for what it's meant to be: a beginner-friendly scripting language. "Everything-is-an-object", duck typing, decorators, bizarre scoping rules, etc., all make it difficult for experienced programmers to understand, let along beginners. I've always thought there's a much simpler language struggling to get out of Python, and I wish it would and would become popular so I wouldn't have to recommend Python to beginners any more, and be on the hook for explaining e.g. why default values are mutable, or why nested generator comprehensions behave differently than nested list conprehensions. (Think Erlang levels of simplicity.)
The standard library is also haphazard and inconsistent (much like JavaScript's). Take lists: some operations are methods, some are functions, some mutate the list, some make a copy, some are global, some are in a module. There's no rhyme or reason that I can tell. Modern C++ has, in my opinion, a much more well-thought-out standard library. There are very few methods/functions which exist due to historical accident (iostreams aside), and the distinctions between methods/free functions and mutators/copiers is fairly uniform.
The C++ standard library is (mostly) an exemplary work, but that is despite the language's complexity, with pointers and lvalue, rvalue and forwarding references, just to get started...
"So I never intended Python to be the primary language for programmers, although it has become the primary language for many Python users. It was intended to be a second language for people who were already experienced programmers, as some of the early design choices reflect. On the other hand, intuitively I probably stuck to many of ABC's design principles. Because although I had my criticisms of ABC, I borrowed many of its valuable elements, which eventually made Python a great language for people who aren't ace programmers or who are just learning. We now have a large community of people using Python as an educational language, teaching Python in schools. These people aren't and may never be professional programmers, but they still find some programming skills useful." -GvR
Sort of. Maybe. When python was gaining momentum in university settings though, the one main argument I read from a professor more than a decade ago, is that the python implementation was near identical to their pseudocode. So they stopped using whatever language they were using in their textbooks, and started using python, since it made it easier to convey the knowledge they were trying to convey (and probably greatly decreased the amount of editing and typos).
Basically, don't discount a text book where every algorithm is executable code, without first having to translate it into some other language.
I think that's how it got so popular. It gets taught in school as a first language because it's "simple, beautiful", etc. and people stick with their first language as people do.
It's not a beginner language, but it is beginner-friendly.
It's actually a huge problem if you end up trying to hire people to build enterprise software in Python, because everyone and their mother has "5 years of Python experience", but "scripting on your own" is miles apart from "building well designed systems".
"Language for people who are not programmers but who need to instruct computers to do things" is the definition I work with. Scratch, BASIC, etc., aren't really suited to that role. No-one who's just trying to munge some files wants to be told "OK first learn this language that isn't useful for you, and then you can learn a language that is". Hence why practically, Python tends to fill the "beginner" role, which is unfortunate due to its complexity.
It's true Basic was designed to be a beginner's language; but in practice, what exactly is easier in Basic? (Even disregarding Dijkstra's assertions that Basic "mutilates the mind", because I suspect he would have held the same opinion of Python!)
The "immediacy" is there in both languages (well, in Python and modern Basic without line numbers): you can just type code without any rituals or mumbo jumbo and it will do more or less what you tell it to.
It was heavily influenced by ABC, a language meant to be used for teaching:
So, I decided to design a language of my own which would borrow everything I liked from ABC while at the same time fixing all its problems (as I perceived them).
In practice, snippets of Python look very similar to pseudocode. A lot of the idiosyncrasies I see people bring up I consider "intermediate" or "advanced" Python. For the first 5 years I used Python I never wrote a class or a module (I was familiar enough to know when I saw one), let alone cared what a meta-class was.
I think most beginners learn languages to solve some particular thing. Python has a lot of great libraries and tutorials for learning. It doesn’t matter if Python has a lot of idiosyncrasies—new Pythonistas are copy/pasting and mad-libbing their way to a basic understanding of coding.
Learning to program from first principles is hard, but I reckon Racket, a lisp, with it’s wealth of teaching materials and well thought out design is about as good a way to start as any. It’s small and consistent enough to actually learn from the ground up rather than by pulling on one thread.
OK, fair point. I misremembered the list interface.
`set` is a better example: `set.union`, for example, returns a new set, while `set.add` updates in place. (To be fair, this is with Python 2.7, maybe Python 3+ is more uniform?)
What would you expect it to do differently in those cases? That seems like the API is following how most people would think about those operations as you'd expect in a language which isn't trying to be immutable.
I thought it was interesting that Ruby, by convention, used an exclamation mark to convey that a function would mutate. I haven't used Ruby all that much to see how well that works at scale.
I think a simpler language would also have more potential for speedup. The effort PyPy has invested is herculean! Grumpy, Cython, Unladen Swallow, etc.
Python has fallen to my second or third most commonly used language now and speed is definitely a reason.
"I wouldn't have to recommend Python to beginners any more, and be on the hook for explaining e.g. why default values are mutable, or why nested generator comprehensions behave differently than nested list comprehensions."
Maybe those scoping gotchas are not beginner topics?
> I've always thought there's a much simpler language struggling to get out of Python
I agree, although I suspect we have different ideas as to which parts of Python would be included in that simpler language. For example, I quite like the consistency of "everything-is-an-object".
FWIW some of the Python core developers have started to express a similar sentiment. Especially in the last few versions, where Python has got much more complex with the addition of type annotations and multiple `async/await` features. I wonder whether the retirement of the BDFL will slow down Python's rate of growth, or accelerate it?
> nested generator comprehensions behave differently than nested list conprehensions
I haven't come across this - could you give an example?
>>> [[x*y for y in xrange(1,5)] for x in xrange(1,5)]
[[1, 2, 3, 4], [2, 4, 6, 8], [3, 6, 9, 12], [4, 8, 12, 16]]
>>> list([x*y for y in xrange(1,5)] for x in xrange(1,5))
[[1, 2, 3, 4], [2, 4, 6, 8], [3, 6, 9, 12], [4, 8, 12, 16]]
>>> [list(l) for l in ((x*y for y in xrange(1,5)) for x in xrange(1,5))]
[[1, 2, 3, 4], [2, 4, 6, 8], [3, 6, 9, 12], [4, 8, 12, 16]]
>>> [list(l) for l in [(x*y for y in xrange(1,5)) for x in xrange(1,5)]]
[[4, 8, 12, 16], [4, 8, 12, 16], [4, 8, 12, 16], [4, 8, 12, 16]]
The problem is that the `x` in the inner comprehension gets somehow linked to the variable `x` in the outer comprehension, rather that the value of `x`. (This matches Python's similarly bizarre closure semantics.) So, the results are "as expected" when either (a) the inner comprehension is evaluated eagerly (first and second example), or (b) the outer comprehension is evaluated lazily (second and third examples). But if both the inner and outer comprehensions are evaluated lazily, the inner comprehensions just see whatever the "last" value of `x` was, which I think to many people is surprising. (No less so than mutable default arguments, at least.)
You're right, this is the same problem that Python has with closures, for example:
>>> l = []
>>> for i in range(10):
... l.append(lambda: i)
...
>>> for j in range(10):
... print(l[j](), end=' ')
...
9 9 9 9 9 9 9 9 9 9
The issue isn't really closing over the variable instead of the value, but the fact that Python re-uses the same variable for each iteration of the loop. C# used to behave the same way, but its designers considered this bad enough that they made a breaking change to the language to fix it [1].
Unfortunately, C#'s solution (creating a new variable for each execution of the loop body) isn't really an option for Python. It would conflict with the rest of the language, which only uses variables scoped to whole functions.
Note that Go makes the same mistake as in earlier versions of C# [2].
> I've always thought there's a much simpler language struggling to get out of Python [...]
I tend to assume that about all languages now, and when I need to use a new language I try to just learn that simpler language initially. I skim the documentation for the rest, but don't learn it until I actually need it (either because the functionality is necessary, or it can make the code better).
It can take a surprisingly long time to need more than the simple language.
* Versions and installation: Consider doing all dev work in a virtual env. It installs the correct python interpreter on your PATH, and installs pips in that isolated env.
> This worked great until I started on a second project that needed Python 3.6. Two concurrent projects with two different versions of Python -- no, that wasn't confusing.
Note that Java also comes in various version of the compiler and jvm, and its common to have several versions on the same system. They are backward compatible but forward compatibility issues exist: your main server app runs on java7 but your batch process is running java9. you might not want to make java9 the default on the system without formally upgrading the server app.
* Syntax/spaces : you get used to it. Hey some people write code in perl :)
* Includes: In principle, this works somewhat similar in Java: You use imports and the imported package hierarchy can nest quite deep so you need to look.
> The import function also allows users to rename the imported code. They basically define a custom namespace..
This can be a good thing, it helps you prevent name clashes. C++ also lets you do this, its called Namespace aliases.
> In every other language, arrays are called 'arrays'. In Python, they are called 'lists'.
Thats because it is a list. Nodes are dynamically allocated and appended to the list. You can expect similar algorithmic complexity for the operations.
I agree with the OPs points but disagree with the degree to which the author has used them as a means to "hate" a language. I generally avoid Python too but saying you hate the language because of a bunch of contrivances mostly wrought from inexperience and holding too strongly to C like languages is poor form.
Versions:
Ok? Versioning and fragmentation is a hard problem. Backwards compatibility is a hard problem. Moving fast and never breaking anything is a nearly impossible problem. Python sucks at it, lots of things suck at it. There are many examples of other languages (.NET Core, for example), platforms, frameworks, etc that all suffer from this. It can be a reason to avoid those things but probably not one to base a loathing on.
Also, lots of people still use Perl. Lots of people still love Perl. I don't know why.
Installation:
This screams "windows only" user. Path'ing, local dependencies, and package management is relatively common and you should force yourself to be comfortable with those concepts. Just saying "I should be able to just run one thing and be done forever", albeit ideal, is naive and never going to happen.
Syntax:
Yep. Spacing blows and you're always going to have stupid issues with it. I hate Python's spacing. Someone ought to create a custom interpreter that allows for using braces.
Includes:
Most of these complaints sound like they're coming from someone largely silo'd in the C/C++ world of wanting to know everything. "With C, you can just look in /usr/include/*.h" - the author is admitting he's unhappy because Python isn't C.
Quirks:
General complaints about other languages... and using those complaints to somehow sour Python? The quirks he does list for Python aren't even strange - they're pretty useful.
Local Names:
OP probably could have just included this in quirks rather than having another point. I'm pretty sure there's actually a means of avoiding this type of import issue by some silly Python pathing shenanigans.
> author is admitting he's unhappy because Python isn't C
That's not the point at all. I mean, did you read it?
How do you mischaracterize the specific point about the casual opportunity for foreign module metaprogamming? Module initialization is bad in a pernicious way. While you can't do operator overloading, you can clobber namespaces (which was referenced). Paired with a community repository, this is exactly the same case of what's so dangerous about npm.
My point wasn't to devalue his arguments - in fact, I find them valid arguments. My point was he's using inexperience and/or familiarity with C/C++ as a reason to "hate" it.
He complains about Python developers grep'ing directories when he admits to looking through just as arbitrary of a directory. His complaints about random code execution during import is valid but also seen as a feature _allowing_ metaprogramming. It's just as bad as C/C++ allowing clobbering over memory.
These are features that come with trade-offs. The author is focusing _only_ on the trade-offs and how they don't exist in his favored language as a reason to hate the language. That's certainly fine for his subjective opinion but not as appropriate in a blog post where he is clearly trying to persuade others.
> It's just as bad as C/C++ allowing clobbering over memory.
That's a good parallel.
> My point was he's using inexperience and/or familiarity with C/C++ as a reason to "hate" it.
I have less familiarity with C/C++ than Python and I hate it because it's not about language perspective. It's bad language design, for such a high level language. Inclusion wrapped with execution is unsafe and has an easy fix for most language interpreters. Don't allow execution during a declaration. If you want to do metaprogramming, there are other ways that don't break the paradigm (rewriting files before inclusion, chaining programs, etc).
> Also, lots of people still use Perl. Lots of people still love Perl. I don't know why.
Perl still holds a special place in my heart. It's so forgiving that it's perfect for banging out something that gets the job done in a couple minutes but while I most often use it for something quick and dirty you can still put in some time and planning to write something complex and maintainable too.
I'm not sure there are major projects being written in perl today, but it's the language I still use for automation of day to day admin stuff and for pretty much all of my log/mail parsing needs.
> Syntax: Yep. Spacing blows and you're always going to have stupid issues with it. I hate Python's spacing. Someone ought to create a custom interpreter that allows for using braces.
Nope, for reasons I've explained up thread. In the meantime I suggest this code:
Your reasons amount to "use an editor that shows whitespace". That's valid and would be my suggestion to people too but it doesn't address my comment. Using an editor for a "necessary" feature only helps you when you have it. If you, on an off-chance, don't have it then you're going to have a bad time.
Comparing whitespace issues to brace-matching issues is a strawman. Braces are visible in 99% of editors.
I use nano or micro at times, which don’t, problem doesn’t happen there either.
The benefits are enjoyed every single read of the code while the drawback is a potential issue (being very generous when I say) once every six months. Honestly happens about once every five years to me.
553 comments
[ 3.0 ms ] story [ 241 ms ] threadThe libraries available in Python, for everything touching data / scripting are just so powerful and well thought, that for many small projects, using Python is a no-brainer. The best thing is that those libraries are actually incredibly fast, while Python is supposedly slow.
To the author:
1. Java, JavaScript and C# all have types which are passed by reference. (They also have types which are passed by value.)
2. Python lists are not arrays, if by arrays you mean a C- or FORTRAN-style block of non-resizable memory which is indexed by position. Python lists are much more like the Java List or C# IList interface.
I might be misunderstanding you, but I don't think you can do this in C++. References can't be changed to point to a different object after initialization. If you have code like:
The assignment above isn't "changing the value to which a caller's variable is bound". Instead, it's running the '=' operator on the Foo object to copy the state from new_foo to ref_param. To demonstrate this, you could run the following to see that the addresses are the same: Under the hood, C++ reference params are basically syntactic sugar for passing by pointer-value, so this behavior isn't surprising.This is a well-known distinction that goes by several names, see e.g. https://en.m.wikipedia.org/wiki/Evaluation_strategy#Call_by_...
The distinction between passing the value of a reference being used by the caller and passing a reference to the caller's stack is so rarely important or useful that there's no consensus terminology for it. The distinction that's actually relevant and useful is whether, when we write f(a), f receives the (thing we would call the) value of a or a reference to the value of a (and in particular whether f can change the value of a). In Python, f can change the value of a (that is to say, the thing Python programmers understand to be the value of a); therefore Python is pass by reference as the term is usually understood (and certainly any term for its call style that uses "value" is more misleading than calling it "pass by reference").
And the reason for that is essentially all modern languages make copies of something when passing argument parameters. The only question is what they are passing and exactly how hard the language layer works to make it look as if you are or are not passing something by reference.
I have to reach to something as obscure as Forth to find a language that does not work that way, and truly does not copy parameters into a function. (I haven't dug into Factor enough to know if under the hood it still copies parameters.) It's a very unusual choice to not copy something for a function call.
(This is one of the ways our CPUs have been optimized for the languages we run; they make this intuitively expensive operation otherwise cheaper than it would be on a naively designed CPU.)
Then of course someone turns on whole program optimization for C++, half the code gets inlined into one terrifying large function, nothing is copied anywhere, and the code runs 2x faster.
Then some poor SOB has to debug the assembly code for all of this and has nightmares for years after.
Not like I'd know or anything. :)`
> In Python, f can change the value of a
but it can't. `f` can't change the object which `a` references:
is a function with no visible effect. When called as `f(a)`, `a` retains whatever value it had previously.People who believe Python is "call-by-reference" don't understand this and write incorrect or overcomplicated code as a consequence. The distinction is important.
There is no consensus that "pass by reference" denotes that, whatever a wikipedia editor says. Normal working programmers do not understand "pass by reference" to mean specifically "pass a reference to the caller's stack" but only the more general concept of "pass a reference to the value of a". If you want to talk specifically about what kind of reference gets passed, you need some new terms for that, and both the new terms you come up with will be subtypes of what most of the world will continue to understand as a general category of "pass by reference". No-one outside of these arguments on HN gets confused about whether you can write a swap function in Python; people understand "pass by reference" to mean something more general and entirely true of Python (and Java and so on). You can tell by how often we see those languages described as "pass by reference".
> but it can't. `f` can't change the object which `a` references:
Yes it can. It can't make a to refer to a different object (as your f tries to), but it can change the object a refers to just fine.
No, Python doesn't even do that, because Python variables aren't names for storage locations to begin with. They're namespace bindings. Passing a variable to a Python function means transferring a namespace binding from the caller's namespace to the function's local namespace.
In C:
Means "set aside one int's worth of storage and fill it with the bit pattern for the number 17".In Python:
means "create an int object with the value 17 and bind it in the current namespace dictionary to the name i".In addition python also does have arrays if you want/need them: https://docs.python.org/3.7/library/array.html
this will not change the array which was passed in and is a purely local change in C#.
static void Change(ref int[] myArray) on the other side would change the array which was passed in.
The difference between "pass by value" and "pass by reference" for reference variables is how the variables themselves are handled. For example, if you pass a reference "by reference" to a function and set it to null, then the reference variable in the calling code is also set to null and will cause null ref exception if you try to access the object. Whereas if you pass a reference "by value", if you set the variable to null, the calling code variable isn't affected.
Pass by value and pass by reference is not about objects or types really, it's about variables.
More abstractly, Historically, in computing "list" connotes pointers and "array" connotes sequential memory. The connotations imply engineering tradeoffs. [1] "List" may make more sense for a beginning programmer. It is an arbitrary context switch for programmers writing in multiple languages. Considering that one of the driving use cases of Python has been systems programming, "list" is misleading regarding performance characteristics. [2]
[1]: For example as in Scala https://docs.scala-lang.org/overviews/collections/performanc...
[2]: googling "python arrays" returns a lot of results explaining the difference between Python's Arrays and Python's Lists. "List" is "foo => spam" and "bar => eggs" Pythonism gone too far.
In Python, Arrays are sequence types and behave very much like lists[1] In other words, even in Python, arrays have similar semantics to Javascript Arrays not Java Arrays.
[1] https://docs.python.org/3.4/library/array.html
Both Java and C# provide 'lists' which are variable sized*
Python 'lists' are variable-sized.
Seems like a consistent naming scheme to me?
*there might be an underlying array that gets reallocated - but it's encapsulated within the list object; the reference to the list object is unchanged when this happens.
But Python predates C# and Java.
The biggest irony here is that if you wrote your code in C instead, you would actually pass more arguments by reference than in equivalent python, because you're going to use pointers for everything but primitive integer values.
However, if you reassigned your copy of a pointer in the body of a function, the original pointer would still point to the same place it did before the function was called.
That's not the same thing as actual pass-by-reference in languages like C++.
In this case the emperor has no visible scope delimiters.
I've seen several bugs in C++ and Java where colleagues have indented the code for the correct control flow, but misplaced the curly braces, resulting in incorrect flow. Granted, the two bugs that come to mind first are dangling-else problems that were fixed by inserting the optional braces.
I think that you and I both prefer auto-formatters to force indenting to match flow control. However, there's an argument to be made for actually enforcing it at the language level rather than at the tooling level.
One of the reasons Python is popular is because it's easy to do a lot of things. Some python choices don't make sense technically, but they were made to make python as easy as possible. Performance was never the first criteria of Python (or ruby). My personal peeve is with mandatory indentation. But again, that's the idiom the language has adopted.
How so? With java you can get your distro version from the package manager or just download a tarball and setup your PATH. Maybe install maven. GCC and build deps is as simple as a xxx install build-essential/build-base and you have a C compiler for C89/C99/C11.
Still, I don’t hate it at all. It’s good and beautiful for lot of other stuff.
I can’t help but think that the lack of Kenneth Ritz in the commit list since summer has something to do with this. I don’t recall the exact scenario, but I think there was some kind of falling out between him and the community.
So despite all the things I dislike about python, it's still my go to for a proof of concept or getting something done quickly.
I always forget at least one of these. You didn't have any of these goodies in good old perl (sob, sob)
(wow, this one got flagged pretty quickly, wonder why)
What should be the correct behavior when accessing a key that doesn't exists? If you want a specific default value when a key doesn't exist you can use the get() method.
Also ':' at the end of each line.
What do you mean? You don't need ':' at the end of each line, only at the start of a block (basically anywhere you'd use a '{' in other languages).
Undefined behaviour, i.e. the JIT generates code to 3D print a clay golem on your desktop that sets your village ablaze.
I like 'self'. There's a lot to complain about magical 'this' of every other language - especially in Javascript. Specifying self makes it really clear if it's a member method or a standalone function.
Python really likes the idea of 'seek forgiveness, not permission', aka. abusing try/except. But it's kind of nice:
Just pray that 'fn()' doesn't also throw a KeyError.- The hideous __method__ and _private conventions
- A friend of mine was complaining about the implicit string concatenation: ["foo" "bar", "baz"] whoops forgot a comma and now there's a very hard to find bug
- pyc and pyo files littering the filesystem after running (yes, only a minor nuisance)
- import anywhere, the ugly __name__ = "main" hack
- GIL, reference counting GC, abysmal performance in general
- Dynamic typing, which is probably my most major complaint but I realize opinions differ
C and many other languages had that before Python, and it’s very useful to be able to split a single string over many lines in your source code. Python very wisely also implemented this feature.
> - pyc and pyo files littering the filesystem after running
Fixed in Python 3.
dictionary.get(...) is specifically for avoiding the exception, you should just get None if the key doesn't exist. Unless I'm misunderstanding you and your complaint lies elsewhere.
Don't get me wrong, there are plenty of things that are stupid/annoying with python, this comment however reads more like "python is bad because I can't write perl in it". I think that's why people are flagging it.
I didn't downvote you, but you present complaints without support or putting forward alternatives other than everyone should ditch Python in favor of Perl. 2 of your 3 complaints come across as shallow syntactic bikeshedding presented without support.
It would be interesting to read your take on the engineering problems involved, the design tradeoffs involved, and why you think Perl's decisions are more appropriate than Python's decisions, even for Python's main use cases.
Python rubbed me the wrong way when I first encountered it, then I really loved it for a few years. Now, I feel it's pretty good at what it does, but I find other languages more interesting.
Maybe you have some good reasoning behind your gripes, but you don't provide any evidence that you've thought much about them or put any effort into understanding the design decisions.
For your first complaint, I do agree that the explicit self parameter vs just making it a keyword is hard to justify in retrospect, but is it really raising your blood pressure, causing you to lose sleep, etc? Your use of hyperbole doesn't help your case.
I presume that making all method calls explicit is to keep scoping sane in the case where a subclass defines a method that happens to accidentally conflict with the name of a function called in some superclass's method. An alternative would be to use static name resolution to resolve the ambiguity, but then you'd have to be statically invoking metaclasses to figure out which methods were in scope, or you'd have to get rid of metaclasses and generally get rid of a bunch of the effort Python goes through to be very late-binding and generally dynamic in behavior.
For your second complaint, if you don't want the dict to throw, use the get method instead of the brackets operator.
For your third complaint, I'm not sure exactly what you're complaining about. Clearly not every line literally ends with a colon, and you're again making poor use of hyperbole. Guido has commented in at least one interview that most of the colon's aren't necessary to disambiguate the grammar, but they really do help beginners in particular read the code.
Showing more effort toward educating and/or convincing people would help avoid the downvotes.
self - the reason is that I forget to type it and causes me to go back and fix the method signature or method call (there are usually lots of them in the code, so lots of opportunities to forget self - hence lots of instances where it can be omitted so that it must be fixed, at least for me; the remark is a gripe about ergonomics of syntax)
The alternative: do a different keyword for method definitions other than def; (well, still you need something to differentiate between method invocation and global function call in an untyped language)
: at the end of function definition or a block - same thing.
KeyError if dict entry not found; most other languages I know would return None, so that's counter intuitive to me (I know about get, but [ ] is the default syntax for accessing dictionaries in most languages) I know it will cause an undefined method call to bomb, which is a good thing in my book; but I don't like it for data structure access - I also don't like exception handling in particular and prefer explicit checking of results (again matter of taste and habit + frequent cause to go back and fix it)
I think python is making several tradeoffs in favor of making the resulting script more robust (like going for exception handling) but it makes the experience of writing said script a bit more tricky.
> https://stackoverflow.com/questions/41573587/what-is-the-dif... What a mess.
Came here to post something similar, but a commenter on the blog post itself got to it first. Virtual environments never made sense to me.
i just made that up
Using indents for blocks just seemed unintuitive and error prone to me. But I dismissed it because all the programming languages I've worked with have had curly braces so maybe the reason for my discomfort was that it was unfamiliar.
Braces are most definitely not whitespace, even if they serve the same function as whitespace in Python.
More generally, yes, many people use auto-indenting and it works in Python in most editors. It's possible that you're using one where it doesn't work correctly but that doesn't mean that's true for everyone else.
What I said was that in Python's case, if I indent some code block wrong, I can't just fix the indentation using auto indenting in python, because the indentation is the semantics. There would be "nothing" to fix and the code would just run wrong. This problem comes up always when I copy paste python code. Of course I should notice that it's indented wrongly but that doesn't always happen. (And please don't advise me to never copy paste code. Life doesn't work that way.)
In my experience that last part is somewhat optimistic for the implication that someone would notice it immediately.
> if I indent some code block wrong, I can't just fix the indentation using auto indenting in python, because the indentation is the semantics. There would be "nothing" to fix and the code would just run wrong.
This is true in some cases — most of the time you'll get an indentation error instead of it running incorrectly — but also why many editors have an auto-indent option for pasted code and autoformatters covert things like code which is consistently indented to match your project's indentation size.
As for pasting, if you're using an IDE that ought to be taken care of for you. I use a vim and the sequence for indenting or de-indenting the text I just pasted is now second nature.
For the first 5 or so years of my career I used Algol syntax (and a bit of Lisp) exclusively. But having been trading off between C, C++, and Python since then I've come to prefer Python's syntax, though not its lack of static types.
It sounds great in theory, but it leads to problems in practice. If you copy and paste code from any language with C style syntax, it's trivial to fix the formatting based on where the braces are, and your editor can help. With python, testing a 10 line example program from a forum post can turn out to be quite difficult. Not to mention, if a tab character ends up anywhere in your program, you now have a syntax error that's completely invisible to the eye. Lastly, it makes it difficult to have clean syntax for multi-line statements.
Lists and arrays are fundamentally different beasts, though. Shouldn’t most coders understand that?
A dictionary is an interchangeable word with hashmap in most situations. The string 'quirks' actually look to be pretty helpful.
Gripes I do actually agree with: the schism in versions, mutation from pass by ref (although this isn't different from JS, Java or any other OO lang). In my opinion you can add OOP in general to the list of gripes against python.
These so called package manager can't manage packages at all. I'd like to call it package downloader. They just download everything from language interpreter to library to a single directory and call it job done. No easy way to remove the package. The interpreter and library's version is forever fixed. Theoretically, it can upgrade the version but the user mostly don't.
I really hate their ecosystem.
This is scoped to the project in npm. In Ruby, you can specify the path (common to "vendor" your gems). You can use version managers (like rvm or nvm) to scope your libs to versions or even named projects (thinking of rvm's "gemsets")
> The interpreter and library's version is forever fixed. Theoretically, it can upgrade the version but the user mostly don't.
I can't speak for all ecosystems, but on Ruby projects I've been a part of, keeping gems updated is pretty common. Github even includes monitoring for vulnerabilities to encourage this.
Having been part of ecosystems that don't have package management (for example, ColdFusion) even the worst package managers are a major improvement. (Though I do think the isolation of python's virtualenvs is a better approach than most systems)
"pip uninstall" has been around as long as pip has.
Theoretically, it can upgrade the version but the user mostly don't.
In practice, people deploy to a virtualenv that gets recreated on each deployment, and tend to treat a virtualenv as an ephemeral thing.
The whole can't manage my environment and don't understand how to manage the python thing at all has my sympathy. Python is messy at that level.
With the organizational 'control' sysadmin deprecated by devops and containers/automation becoming the new norm these types of complaints have to be dealt with by people who have no idea of what they are doing in generalized context.
1. Importing behavior is ridiculous: https://chrisyeh96.github.io/2017/08/08/definitive-guide-pyt...
2. Python libraries' documentation leave a lot to be desired (compared to good javadocs). Just because your language is dynamically typed doesn't mean you don't need to describe what the expected shape of a parameter should be.
3.
I'm sure other Java programmers have had a culture shock coming to python as well. I appreciate how concise the language is, the great community and module ecosystem, and how productive it is. But, it feels to me like there's features missing (I've only been seriously programming python for 5 months though, before I was basically just scripting and writing java code in python).Also the most fun part of learning python has been list comprehensions and itertools, they've really been a revelation. Like when I first learned Ruby procs, blocks, closures and lambdas.
Especially #2 -imo a dynamically typed language/library warrants having better documentation, not worse.
> Static variable? declare it outside of your methods but inside your class
Sounds like you're still thinking in Java :). A static method is rarely what you want (and if you do you can put it on the class like for variables); functions are first-class so you can just put them in your module.
Python’s philosophy of convention rather than constraint is a nice chance from Java’s corseted mindset. But I’ve never had to use Python in a large development team, so I don’t know how well it holds up with a large group of devs.
As an aside, if you have fun with comprehensions, itertools and lambdas, I urge you to take a look at functional programming!
I've been developing Python for upwards of 10 years, and not one single time I have ever had a legitimate use for double underscore, and I've only seen one legitimate use in the wild (involving auto-generated code and C interfacing).
You also very, very rarely need static methods.
This is why I _love_ python. Stop trying to protect me from your library. I've been doing some C# work and nothing bothers me more than having to fork and re-build an entire library just because the author didn't think I should be allowed to touch some variable that should've just been `public` or `protected`.
I don't need a library author to hard-block me from things. Put up the appropriate warning signs, then get out of my way.
Could you give examples?
The standard library is also haphazard and inconsistent (much like JavaScript's). Take lists: some operations are methods, some are functions, some mutate the list, some make a copy, some are global, some are in a module. There's no rhyme or reason that I can tell. Modern C++ has, in my opinion, a much more well-thought-out standard library. There are very few methods/functions which exist due to historical accident (iostreams aside), and the distinctions between methods/free functions and mutators/copiers is fairly uniform.
https://www.artima.com/intv/pyscale.html
"So I never intended Python to be the primary language for programmers, although it has become the primary language for many Python users. It was intended to be a second language for people who were already experienced programmers, as some of the early design choices reflect. On the other hand, intuitively I probably stuck to many of ABC's design principles. Because although I had my criticisms of ABC, I borrowed many of its valuable elements, which eventually made Python a great language for people who aren't ace programmers or who are just learning. We now have a large community of people using Python as an educational language, teaching Python in schools. These people aren't and may never be professional programmers, but they still find some programming skills useful." -GvR
"It was intended to be a second language for people who were already experienced programmers, as some of the early design choices reflect. "
Basically, don't discount a text book where every algorithm is executable code, without first having to translate it into some other language.
It's actually a huge problem if you end up trying to hire people to build enterprise software in Python, because everyone and their mother has "5 years of Python experience", but "scripting on your own" is miles apart from "building well designed systems".
Also that rule is tautological.
The "immediacy" is there in both languages (well, in Python and modern Basic without line numbers): you can just type code without any rituals or mumbo jumbo and it will do more or less what you tell it to.
So, I decided to design a language of my own which would borrow everything I liked from ABC while at the same time fixing all its problems (as I perceived them).
http://python-history.blogspot.com/2009/01/personal-history-...
Learning to program from first principles is hard, but I reckon Racket, a lisp, with it’s wealth of teaching materials and well thought out design is about as good a way to start as any. It’s small and consistent enough to actually learn from the ground up rather than by pulling on one thread.
Let's take the list type, the public methods are: 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'
Obviously as the names imply `copy` returns a new list, and `count` returns an integer. Neither mutate the list.
For the rest of it: methods mutate the list. There are some builtin functions that do similar things, but those always a new object (sorted, reversed)
`set` is a better example: `set.union`, for example, returns a new set, while `set.add` updates in place. (To be fair, this is with Python 2.7, maybe Python 3+ is more uniform?)
Python has fallen to my second or third most commonly used language now and speed is definitely a reason.
Maybe those scoping gotchas are not beginner topics?
They are the second one of your beginners trips over them. Especially the first one is fairly easy to encounter and really vexing.
That said, Python in my experience is an excellent beginner language, and I'd still recommend or teach it.
I agree, although I suspect we have different ideas as to which parts of Python would be included in that simpler language. For example, I quite like the consistency of "everything-is-an-object".
FWIW some of the Python core developers have started to express a similar sentiment. Especially in the last few versions, where Python has got much more complex with the addition of type annotations and multiple `async/await` features. I wonder whether the retirement of the BDFL will slow down Python's rate of growth, or accelerate it?
> nested generator comprehensions behave differently than nested list conprehensions
I haven't come across this - could you give an example?
Unfortunately, C#'s solution (creating a new variable for each execution of the loop body) isn't really an option for Python. It would conflict with the rest of the language, which only uses variables scoped to whole functions.
Note that Go makes the same mistake as in earlier versions of C# [2].
[1] https://ericlippert.com/2009/11/12/closing-over-the-loop-var...
[2] https://play.golang.org/p/Pt6BN2Mj-WL
I tend to assume that about all languages now, and when I need to use a new language I try to just learn that simpler language initially. I skim the documentation for the rest, but don't learn it until I actually need it (either because the functionality is necessary, or it can make the code better).
It can take a surprisingly long time to need more than the simple language.
I'd also welcome a simplified Python, from scratch as it were, but that is a giant undertaking and bigger than the 2 to 3 chasm.
$ python2.7 -m virtualenv .venv or $ python3.6 -m venv .venv
$ source .venv/bin/activate
// dev + test here
$ deactiveate
> This worked great until I started on a second project that needed Python 3.6. Two concurrent projects with two different versions of Python -- no, that wasn't confusing.
Note that Java also comes in various version of the compiler and jvm, and its common to have several versions on the same system. They are backward compatible but forward compatibility issues exist: your main server app runs on java7 but your batch process is running java9. you might not want to make java9 the default on the system without formally upgrading the server app.
* Syntax/spaces : you get used to it. Hey some people write code in perl :)
* Includes: In principle, this works somewhat similar in Java: You use imports and the imported package hierarchy can nest quite deep so you need to look.
> The import function also allows users to rename the imported code. They basically define a custom namespace..
This can be a good thing, it helps you prevent name clashes. C++ also lets you do this, its called Namespace aliases.
namespace fbz = foo::bar::baz; std::cout << fbz::qux << '\n';
* Nomenclature:
> In every other language, arrays are called 'arrays'. In Python, they are called 'lists'.
Thats because it is a list. Nodes are dynamically allocated and appended to the list. You can expect similar algorithmic complexity for the operations.
Python also has arrays if you want the better efficiency: https://docs.python.org/3/library/array.html
* Pass By Object Reference: same as java
Versions: Ok? Versioning and fragmentation is a hard problem. Backwards compatibility is a hard problem. Moving fast and never breaking anything is a nearly impossible problem. Python sucks at it, lots of things suck at it. There are many examples of other languages (.NET Core, for example), platforms, frameworks, etc that all suffer from this. It can be a reason to avoid those things but probably not one to base a loathing on.
Also, lots of people still use Perl. Lots of people still love Perl. I don't know why.
Installation: This screams "windows only" user. Path'ing, local dependencies, and package management is relatively common and you should force yourself to be comfortable with those concepts. Just saying "I should be able to just run one thing and be done forever", albeit ideal, is naive and never going to happen.
Syntax: Yep. Spacing blows and you're always going to have stupid issues with it. I hate Python's spacing. Someone ought to create a custom interpreter that allows for using braces.
Includes: Most of these complaints sound like they're coming from someone largely silo'd in the C/C++ world of wanting to know everything. "With C, you can just look in /usr/include/*.h" - the author is admitting he's unhappy because Python isn't C.
Quirks: General complaints about other languages... and using those complaints to somehow sour Python? The quirks he does list for Python aren't even strange - they're pretty useful.
Local Names: OP probably could have just included this in quirks rather than having another point. I'm pretty sure there's actually a means of avoiding this type of import issue by some silly Python pathing shenanigans.
That's not the point at all. I mean, did you read it?
How do you mischaracterize the specific point about the casual opportunity for foreign module metaprogamming? Module initialization is bad in a pernicious way. While you can't do operator overloading, you can clobber namespaces (which was referenced). Paired with a community repository, this is exactly the same case of what's so dangerous about npm.
He complains about Python developers grep'ing directories when he admits to looking through just as arbitrary of a directory. His complaints about random code execution during import is valid but also seen as a feature _allowing_ metaprogramming. It's just as bad as C/C++ allowing clobbering over memory.
These are features that come with trade-offs. The author is focusing _only_ on the trade-offs and how they don't exist in his favored language as a reason to hate the language. That's certainly fine for his subjective opinion but not as appropriate in a blog post where he is clearly trying to persuade others.
That's a good parallel.
> My point was he's using inexperience and/or familiarity with C/C++ as a reason to "hate" it.
I have less familiarity with C/C++ than Python and I hate it because it's not about language perspective. It's bad language design, for such a high level language. Inclusion wrapped with execution is unsafe and has an easy fix for most language interpreters. Don't allow execution during a declaration. If you want to do metaprogramming, there are other ways that don't break the paradigm (rewriting files before inclusion, chaining programs, etc).
Perl still holds a special place in my heart. It's so forgiving that it's perfect for banging out something that gets the job done in a couple minutes but while I most often use it for something quick and dirty you can still put in some time and planning to write something complex and maintainable too.
I'm not sure there are major projects being written in perl today, but it's the language I still use for automation of day to day admin stuff and for pretty much all of my log/mail parsing needs.
Nope, for reasons I've explained up thread. In the meantime I suggest this code:
Comparing whitespace issues to brace-matching issues is a strawman. Braces are visible in 99% of editors.
The benefits are enjoyed every single read of the code while the drawback is a potential issue (being very generous when I say) once every six months. Honestly happens about once every five years to me.