Live data from Hacker News

Python and the Principle of Least Astonishment

lucumr.pocoo.org

41–50 of 53 posts

Re: Python and the Principle of Least Astonishment

#41
post #16

This is a nice article, as a non-Python programmer who's dabbled a bit in order to read some random Python code, it gave me some appreciation for the "Pythonic"-way. I have to say though, that the thing that astonished me the most about Python is the Java-like "closures". I sort of thought it would be like Ruby, which I thought was closer to Scheme and JavaScript, and then I realized that Ruby isn't quite like them e…

> then I realized that Ruby isn't quite like them either. I am curious. How does Ruby's closure differ from Scheme's?

To be honest, I'm not really sure. I think it's a superficial difference. When I put this into a Ruby 1.9.2 REPL

    def foo (n)
        lambda {|i| n += i } end
I expected to be able to do this:

    acc = foo(0)
    acc(1)
But instead have to do

    acc.call(1)
Maybe there's another way...

Edit: oops you pointed out this example in another comment, sorry! Is there no way for the returned lambda to "feel" like a regular function?

Re: Python and the Principle of Least Astonishment

#42
post #21
post #12

Earlier quoted context omitted.

> I have only a trivial amount of experience experience with Scheme, could you explain how its closures are different from Python's? In pre Python 3, the closed over value isn't mutable unless it's a reference to a mutable object. def counter(num): def foo(): num += 1 return num return foo c = counter(5) c() This won't work because you can't mutate the closed variable `start`. This would work in languages with proper…

Python has had proper closures since version 2.2, which is quite old at this point. You can verify this using the following code: def func(): x = [] def func2(): x.append(1) return x return func2 closed = func(); closed(); assert closed() == [1,1] closed = func(); closed(); assert closed() == [1,1]

That's what I was referring to as "Java closures".

Re: Python and the Principle of Least Astonishment

#44

Overall it's a good article, but I don't fully understand his complaint about decorators, and I think he may not understand them. @foo and @foo() intentionally mean very different things. I don't see how you could "add a parameter to a previously parameter-less decorator" without drastically changing the meaning of the whole thing. They may be tricky to wrap your head around, but there's nothing too surprising about…

If I have the following code: @auth_required def some_view(): pass And later I decide to change that decorator to be parameterized: @auth_required("basic") def some_view(): pass I have now created a massive amount of pain for myself if I decide to define auth_required() such that it defaults its argument to "basic", unless I force people to call it as @auth_required(), because of the differing callable signatures. So…

Apparently there's something pretty big here I'm not getting. If you add a parameter to auth_required, of course you're causing a huge amount of pain because you're totally changing what auth_required means. It's going from being directly applied to some_view to generating a function that is applied to some_view. If every decorator was automatically called, every decorator would have to return a function. Among other consequences, you wouldn't be able to use property as a decorator.

Even if it's inconvenient, python's behavior here is still not surprising. It's perfectly consistent, and more general than automatically adding parentheses.

Re: Python and the Principle of Least Astonishment

#45
post #16

Earlier quoted context omitted.

> then I realized that Ruby isn't quite like them either. I am curious. How does Ruby's closure differ from Scheme's?

To be honest, I'm not really sure. I think it's a superficial difference. When I put this into a Ruby 1.9.2 REPL def foo (n) lambda {|i| n += i } end I expected to be able to do this: acc = foo(0) acc(1) But instead have to do acc.call(1) Maybe there's another way... Edit: oops you pointed out this example in another comment, sorry! Is there no way for the returned lambda to "feel" like a regular function?

[deleted]

Re: Python and the Principle of Least Astonishment

#47

Differentiating properties and methods from internal methods is as simple as using another char, like dots and colons. So box.length would not be the same as box:length, that way you could assign all names you want without fear of collision. Poor choices in language design will always haunt you til the end of times. That being said, python is my favorite language right now and the only complain I have is about unders…

how is ':' less onerous than underscores i.e. '.__'?

Sounds like you just have irrational hatred of underscore (or possibly love of colon?)

btw having two or more access operators '.', ':' and distinguishing between "internal/language" and "user" methods is a poor language design choice over having a standard but unenforced __ means "special" and "internal/language" and "user" all being coequal in semantics, syntax, rights and responsibilities.

Re: Python and the Principle of Least Astonishment

#49
post #40

So... Python has len() because it would be too hard to standardize on .len(). Any by the way, len() calls .__len__(), on which we have standardized. Got it.

The better explanation is that there are a series of fallback actions for all of the top level functions that delegate to the .__whatever__ methods. For example, if you write a class that quacks like a collection that has a length (it implements .__len__) Python can use that to get a value for bool() even if you didn't implement .__nonzero__.

When people inquire about the len() function it is mainly because it is (or at least looks like) a global function rather than an object method. In short, people wonder why you have to call len(x) instead of x.len(). This is a valid concern seeing that Python's standard library is, by and large, object oriented (c.f. string methods like "upper" or list methods like "append" or file methods like "read").

Neither the article nor your comment has a good explanation of why that is so. The article only harps on about why it's good that it's not called length() or getLength() or size() or getSize(), implying that len() is somehow the god-given name for this feature. And the fallback mechanism you refer to could just as well be implemented if len() was a method of the root class (object) rather than a global method.

I like Python a lot but this is one that I never quite understood and I believe there isn't really a good reason for it, I think it was just a historical accident. Might as well admit it rather than trying to come up with half baked justifications after the fact.

Re: Python and the Principle of Least Astonishment

#50

Differentiating properties and methods from internal methods is as simple as using another char, like dots and colons. So box.length would not be the same as box:length, that way you could assign all names you want without fear of collision. Poor choices in language design will always haunt you til the end of times. That being said, python is my favorite language right now and the only complain I have is about unders…

how is ':' less onerous than underscores i.e. '.__'? Sounds like you just have irrational hatred of underscore (or possibly love of colon?) btw having two or more access operators '.', ':' and distinguishing between "internal/language" and "user" methods is a poor language design choice over having a standard but unenforced __ means "special" and "internal/language" and "user" all being coequal in semantics, syntax,…

"how is ':' less onerous than underscores i.e. '.__'?"

There is no difference at all, just one char vs five if we count ending double-underscores too, and more visual noise. They both accomplish the same.

Being both the same, what difference do you think there is between, in one hand .init() and .__init__(), and on the other hand .init() and :init()

Just syntactic sugar, I rather pick my colon ;-)

Post reply on HN