Hacker News new | past | comments | ask | show | jobs | submit login

I would never accuse Python of "language clarity and friendliness". Far from it. For someone who came up through C, Java, Perl, and Ruby, but who's wrangled with Python, Javascript, Go, and even Haskell in recent years, I still find Python mysterious, self-contradictory, and filled with implicit rules and assumptions that are entirely un-intuitive to me far more than other languages. And yet, people seem to like it. Certainly this article does. It's an interesting effect.



I find that with Python that's almost always caused by not quite understanding the underlying rules. Once understood, they're very consistent.

For example: does Python pass function arguments by value or by reference? Neither! It passes them by object reference - not by variable reference like C/C++.

Check out:

  >>> def foo(a):
  ...     a = 2
  ...
  >>> value = 1
  >>> value
  1
  >>> foo(value)
  >>> value
  1
and:

  >>> def mutate(dct):
  ...     dct['foo'] = 'bar'
  ...
  >>> value = {}
  >>> value
  {}
  >>> mutate(value)
  >>> value
  {'foo': 'bar'}
This apparent contradiction confuses a lot of people. The first example would imply that Python's pass-by-value, but the second looks a lot like pass-by-reference. If you don't know the actual answer, it looks magical and inconsistent, and I've heard all sorts of explanations like "mutable arguments are passed by reference while immutable objects are passed by value".

In reality, the object itself - not the variable name referring to the object - is passed to a function arguments. In the first example we're passing in the object `int(1)`, not the variable `value`, and creating a new variable `a` to refer to it. When we then run `a = 2`, we're creating a new object `int(2)` and altering `a` to point to the new object instead of the old one. Nothing happens to the old `int(1)` object. It's still there, and the top-level `value` variable still points to it. `a` is just a symlink: it doesn't have a value of its own. Neither does `value` or any other Python variable name. That's why the second example works: we're passing in the actual dictionary object and then mutating it. We're not passing in the variable `value`; we're passing in the object that `value` refers to.

The point of this long-windedness is that Python's rules tend to be very, very simple and consistent. Its behavior can be unexpected if you don't truly understand the details or if you try to infer parallels to other languages by observing it and hoping you're right.


haha, your explanation is exactly the mutable vs immutable thing.

dicts are mutable, so, it doesn't create a new object to make an assignment

numbers are immutable, so, it does.


Except that's 100% wrong here. There's not even a facility for marking an object as immutable in Python, so that wouldn't support user-defined classes at all.

Python is always pass-by-object-reference, and assignment is always a pointer operation. It's not special-cased like you're describing.


Indeed, this may or may not be clearer with the help of id() - observe:

  >>> v=2
  >>> id(v)
  4148001784
  >>> id(2)
  4148001784
  >>> def m(a):
  ...   a=3
  ...
  >>> m(v)
  >>> id(3)
  4148001800
  >>> id(v)
  4148001784
  >>> v
  2


  >>> l=[]
  >>> id(l)
  4140973392
  >>> def m2(lst):
  ...   lst.append(1)
  ...
  >>> m2(l)
  >>> l
  [1]
  >>> id(l)
  4140973392


In my experience and implied by the rising popularity of python, you would be among the minority. Personally, I find python to be the most clear of any language I've worked with, most resembling natural language in the way I typically speak. Do you have some examples of how you find it self-contradictory?

Here's an example of its expressiveness a colleague and mine I discussing the other day: Python: [os.remove(i.local_path) for i in old_q if i not in self.queue] Java: old_q.stream().filter(i -> !self.queue.contains(i)).map(i -> new Path(i.local_path)).forEach(Files::delete);

I've programmed in both languages but joked I could only understand the Java line by using the Python line as documentation!


I've always found the fork bomb page on Wikipedia to be a good example of different languages. https://en.wikipedia.org/wiki/Fork_bomb

The Python is example is both short and obvious, whereas the other examples tend to be either cryptic or complicated.


I'd slightly change the wikipedia article to use "while True:" instead of "while 1:". Also, the C# is also quite readable (though very verbose).


Well you've picked the perfect example where Python's list comprehensions shine, 1 map, 1 filter, 1 'each'.

I think your Java example only looks gross because it's using ugly APIs, and isn't indented well, but otherwise, apart from contrived examples, pipelining is superior.

I find Python's lack of pipeline capability, whilst every other modern language supports it, very frustrating. JavaScript, Scala, Swift, Rust, Ruby, Elixir, C#, F#, Java, Kotlin <-- all support pipelines.

Meanwhile, Python has borked, 1-line, lambdas that compose awkwardly with map/filter (if you do a map over the result of a filter, they'll be written in reverse order), and refuses to implement useful methods on lists, that would allow pipelining. It's like it can't decide to pick the OO solution to the problem (and add the methods to lists) or to go the FP route (and fix its lambdas), so has done neither.

So we're stuck hoping our problem at hand fits neatly into a list comprehension, which still won't be composable when we come back to it and realise we want to add another operation.

I like Python very much, but this is one of it's weakest areas in my opinion, so I'm surprised you bring it up as a strength.


Indent with four spaces to show code nicely on HN


I'm afraid I disagree. I programmed in a variety of languages in grad school (physics): C, C++, Fortran 77, Tcl, Perl, Matlab, Maple, Mathematica, IDL, Emacs LISP, etc. Not to mention the stuff I started on in high school.

When I switched my analysis to Python, I became so much more productive. And other science researchers I have known have echoed this sentiment. Even writing a C module to speed up my Python was pretty straightforward, if tedious.

Python had the fewest surprises. And debugging other people's Python is exponentially less annoying than debugging other people's Fortran or C. It's still my go-to language to get stuff done without fuss.


Yep, this was my point. I'm curious what makes Python seem so obviously clear to other people, but not to me. Maybe it's the OO approach. I had done a lot of Java, Smalltalk, and Ruby before I ever tried to approach Python. Maybe it's because Python's OO support feels (to me) bolted on compared to those other languages which are obviously OO from the bottom up, and I'm unwittingly trying to apply mental models I developed in those other languages to how I think about Python.


But Python is OO from the bottom up. Unlike Java, everything in Python is an object.

Perhaps your experience with objects in other languages has given you a different mental model for what an object is. I find Python objects to be more straightforward than in other languages, especially because classes are objects, too.


If it really is OO, then the global `len()` function and like explicitly declaring "self" in method declarations makes it _feel_ bolted on (to me). Why is `len()` special? I immediately question what other basic operations aren't methods, but global functions.

And as for method declaration, if you aren't satisfied with implicit self, I much prefer Go's choice of having you declare the self reference for methods before the method name, instead of in the argument spec list (which then doesn't match the calling list). Python's way makes it feel like the compiler writer couldn't be bothered to hide the OO implementation on the declaration side, but embraced it on the calling side.

Meh, I know these have been hashed over a thousand times here. Just some of the things that rub me the wrong way when I've tried to deal with Python.


If it really is OO

Python is a multi-paradigm language in the sense that it does not explicitly force the programmer to write all code in a particular way. So, for example, Python does not forbid the existence of standalone functions, or the execution of functions without looking them up through a class of which they happen to be a member.

But given that it is inescapably true that every function call in Python is translated to a call of a method of an object, it's hard to argue that it isn't "really" OO.


You can find explanations for both those questions in the design FAQ.

The explicit self came from Modula-3. https://docs.python.org/2/faq/design.html#why-must-self-be-u...

The explanation for len is a little lacking. https://docs.python.org/2/faq/design.html#why-does-python-us...

Guido once explained further in an email to the mailing list. I've forgotten some of it, but the gist is that he didn't want anyone to accidentally create or override a .len() method to do something other than tell the number of elements in the container.

And... almost everything is a method. Even ``len(obj)`` is just sugar for ``obj.__len__()``.


almost everything is a method

Not "almost". Just "everything is a method", in terms of function calls. Even user-defined standalone functions. Consider:

    def my_func(arg):
        return arg + 5
The following are equivalent, and show how things work:

    my_func(3)
and

    my_func.__call__(3)
and

    types.FunctionType.__call__(my_func, 3)


I'm not certain, but I think the C API can avoid that. I've learned to say "almost" because every time I say Python can't X, someone shows me it can.


There are a few functions in the C API to let you call things, depending on the type of thing and set of arguments you feel like providing, and they rely on the Python API to handle the calling for you. I imagine if you really wanted to, and knew enough about the structure and expectations of the Python object you were working with, you could "manually" call without going through one of those C API functions, but I don't know that I'd recommend trying it...


len is a method. it is just a shortcut for object.__len__()

so is str and other shortcuts


I'm inclined to agree that python isn't the "clearest and friendliest". I've been using it a while, and I still find myself looking up how to do X, when it should be obvious. I like python, but I'm amazed at how people love it.

I have to maintain a codebase of php/perl/java/python. "Pythonistic" programming seems to encourage finding the shortest/fastest way to code things at the expense of clarity.

Plus dependencies can get headachy. This might just be the code I have to work with, but while better than perl, in my case its harder to maintain than java or php, (the global scope thing in python seems to get me).


I usually find it fairly easy to figure out what Python code does... as long as no errors occur. The fact that basically no Python code documents what errors it can throw/generate is really annoying.


every language has warts and gotchas. I judge a language by how nice its _idiomatic_ patterns are, rather than by how bad its warts are (unless there are an overwhelming number of warts that can't be avoided even in idiomatic code).




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: