Python: Myths about Indentation
secnetix.de
secnetix.de
The adverse visceral reaction to white space (which I think is akin to the reaction to parens in lisp) appears to me to be people being unhappy with being "forced" to do something. These (for me) happen to be the same people who don't understand why the code they wrote in their personal (and ever fluid) style is no good to be added to our code base.
The last paragraph is a straw man imho -- people have no business having a programming job if they can't use their team's code formatting rules. (If they work in a team of one, they still need to specify and follow coding/documentation standards.)
Which part of my post gave you the impression I was talking about anything other than direct personal experience? I made sure to be explicit about it.
I even say I don't know if they would be good or bad, but gave a reason why I like python disallowing them.
edit:
I take exception to the idea that my personal opinion needs to have a "reference" which would boil down to the personal opinion of another random person on the internet who supports my position authoring a blog post that looks "sciencey". Opinions on language features does not produce scientific studies.
Oh, private musings instead of an argument? Less interesting. :-)
I said I like that my coworkers need local bindings to code they pass. You asked for a reference.
Well here it is! Some guy on the internet saying why python's lack of multiline anonymous functions is so good you should not only change coding styles but adapt his language over your language because his is obviously proven superior in every way, scientifically.[1]
[1]http://news.ycombinator.com/item?id=3057283
Yes, ruby blocks are useful. Yes, lambdas let lisps do cool things. Python makes you name your blocks. It's a horror show.
And to your straw man issue. Yes, we actually just terminate everyone on the spot who tries to commit code that doesn't follow pep8 to the letter. Even when it doesn't make sense for a line that isn't nicely split at 80 chars.
And by terminate, I mean execute.
-------
It's like any commentary on a language must be part of some epic language-war.
I read it as a possibly interesting new claim about programming languages.
If you just wrote without making/arguing a position, sorry for commenting. And reading.
Edit: That anonymous funs might not be good is totally new to me. I've never seen anything about it, but was willing to read. I hope you at least had fun flaming people... (z0r -- I didn't see a discussion in the article.)
Do you just expect to be spoon fed information? Put some thought into your next comment before you demand something of another person when you aren't willing to put any effort in yourself.
No sure I get what you mean. I pasted a lot of code in PHP and Python, and in both case you have to select the pasted block and adjust left or right according to the change of depth level.
> people being unhappy with being "forced" to [indent]
Well, then why the Hell would these people be happy to be forced to write { and } around their blocks?
For me, there is one little annoyance with indent as syntax in Python, it is that it is harder for my editor (Vim) to let me jump from def to the end of the function, and the reverse. (Or there is a conf, a plugin I missed? I know about indent folding, but I just want % to behave like if I had curlies.)
Yes, you readjust the PHP for aesthetics. Not for correctness of execution.
In vim, if I recall(emacs for a while now), =G auto formats bracketed code without issue. Pasting code, deleting enclosing block, adding an enclosing block, or highlighting a mismatched bracket (when formatting goes horribly wrong) can be handled automatically.
Here we differ. A code uncorrectly indented is wrong for me, and must be corrected right now. I don't care at all if it runs or not.
if user_set_delete:
logger.info("User set flag to delete file")
delete_file()
vs if($user_set_delete===True){
log("User set flag to delete file");
delete_file();
}
"Correct" and "looks good" can have very different consequences. Acting like proper indentation for looks is on the same level as proper indentation for execution is ignoring the real dangers of improperly indented python.I know copying that php block will not change the execution, no matter how it is formatted vs copying python where I need to guarantee I've got it right. Hence "some overhead".
if user_set.delete:
logger.info("User set flag to delete file")
delete_file()
Becomes: if ($user_set_delete==True){
log("user set flag to delete file")
}
delete_file();http://www.vim.org/scripts/script.php?script_id=386
But really the plugin you should get and master is EasyMotion. Like vim in general, using it is going to feel painful for the first day or so. Between that plugin and auto-folding, I've never needed to use another movement command again.
The only major downside of significant indentation is that it makes it impossible to define multi-line anonymous functions in a clean way.
I think part of my initial dislike for Python was manually typing nested indentation, even with one space the level for class->def->for->if can get annoying to type. I used to use EditPad Lite that didn't have auto-indent. Everything got better once I learned vim.
Regarding multi-line lambdas in Python, you can do it more or less but like you said it's not clean and can be dangerous.
a = [1]
blah = lambda x: eval(compile('''
print x
a.append(x)
''', '', 'exec'))I'm not convinced that multi-statement anonymous functions are a good idea.
def go : this.some_other_method : here_is_my_block
?
This is maybe a bit far from the topic, but I would like to know why there is such a need for anonymous functions in Python.
I understand very well the need for stateless functions (functions that do not change anything else than their return value, have no side effect, except maybe logging), and I understood it was a piece of functional programming.
In Javascript, with silent overwriting of functions and without modules, it is obviously very bad to pollute the only namespace with function names that can clash with each other, but in Python, you are or should be always inside a relatively small scope, and there you can def _my_temp_func() without any problems, no?
Moreover, as it is lame to use _temp as function name, it forces you to formulate a meaningful function name, which is good pratice, especially for your lectorate, right?
Or am I missing something?
For example, in scheme:
> (define (foo x) (lambda (y) (+ x y)))
> (define bar (foo 10))
> (bar 5)
15
This sort of thing is of course immensely useful. >>> def foo(x):
... def add(y):
... return x + y
... return add
...
>>> bar = foo(10)
>>> bar(5)
15bar = foo(10)
This is perfectly correct Python code too if you prefer. The only real thing with Python's lambdas is that, because Python makes a distinction between statements and expressions, you can't easily do things like use temporary variables inside a lambda, but cases like that are rare and are typically more readable with a separate standalone function definition anyway.
x = [{1: 'b', 2: 'a'}, {1:'a', 2:'b'}]
You want to sort it on the value at the "2" key. You could write a dummy function: def extract_2(dct):
return dct[2]
x.sort(key=extract_2)
Or, you could just use a quick lambda: x.sort(key=lambda dct: dct[2])
The latter is more terse, and to a functional programmer, more clear (YMMV, of course). Lambda is just syntactic sugar in python; it's not necessary, and a lot of places where a functional programmer uses it in python make more sense as list comprehensions, but I think it's a nice thing to have.EDIT:
I think the biggest reason I prefer the lambda approach to the named-function approach is that I don't like seeing one-off functions defined first and then seeing them used later. If python could do this:
x.sort(key=extract_2)
def extract_2(dct):
return dct[2]
or even somehow support ruby-style blocks, it wouldn't be so ugly to my eye. Python lets functions call functions that are defined below them at the module level, but not at the function level, so the above isn't valid in the scope of a function.Building new control structures in-language, without having to hack the interpreter, and without the result looking like somebody has puked all over your screen. First-class functions are under-used in Python because they're syntactically and semantically heavy. With full anonymous functions, the `with` statement would not have been needed, it could have been handled through a higher-order function, akin to Haskell's `bracket`[0]
> In Javascript, with silent overwriting of functions and without modules, it is obviously very bad to pollute the only namespace with function names that can clash with each other
Namespace pollution is completely irrelevant (it's very easy to handle it in Javascript via the namespace pattern).
> Moreover, as it is lame to use _temp as function name, it forces you to formulate a meaningful function name, which is good pratice, especially for your lectorate, right?
If it is, why can I put name-less blocks of code in `for`, `if`, `while` and `with` bodies?
Not all code blocks need a name. Those that do will be given one.
[0] http://hackage.haskell.org/packages/archive/base/latest/doc/...
That's really besides my point. And last time I checked, Haskell's `if` and `case` expressions did not take functions, they took anonymous expressions.
> So with the bracket function, for example, it's still considered better practice to pass named functions to it.
To pass a named function as third argument to bracket?
"So the conclusion is: Python forces you to use indentation that you would have used anyway, unless you wanted to obfuscate the structure of the program."
You were going to use consistent indentation anyway, right?
Consider that the competing scripting language at launch was Perl.
Now consider the benefit to a large team of well formatted easy to read code; Python's whitespace and indentation rules are there to enhance maintanability and readability, and they generally work.
As the article shows, you CAN make python look worse, but you shouldn't.
Incidentally, languages like Google Go take this one farther and embed deterministic formatting as a part of the language toolkit. This is brilliant. I would like to see such a thing incorporated into Python as well.. (Where's my backlist of to-do items?)
some_object.
do_something(..).
do_something_else(..).
Doesn't work. It needs to be written: some_object.\
do_something().\
do_something_else()
or some_object\
.do_something()\
.do_something_else()
It's ok to write \ a couple of time, but I feel like it makes chain-able functional methods really ugly to use because of that. (someobject
.do_something()
.do_something()) some_object.do_something().do_something()\
.do_something().last_thing()
Basically, go to the end of the line and wrap with an indent. That's the way that I see most function calls with a long list of arguments in Python. In Perl, you might see something like: GetOptions(
'h|help' => \my $help
);
Whereas in Python you would see: parser.add_option('-h','--help',meta="HELP",target="HELP",
help="Print help message",action="store_true",default=False)In other words, forcing everyone to use tab-characters for spacing would be a burden on the majority for the benefit of a tiny minority.
Well, unless you're just reading code.
A lot of python is convention based, and you can break all the rules you want and still have valid code, but no one else will want to use or maintain it.
if x:
----some_long_func_name(...,
---- blah...
Note that ---- is indentation and the spaces need to be spaces. If you use tabs for all of this, it will come out misaligned.You could say -- then why not use tabs for the indent part, and spaces for the alignment part? Because virtually no editors support doing this automatically (and doing this manually is not practical). The few editors that do support automating this, don't make it discoverable (let alone a default).
What you end up with is a horrible mixture of tabs and spaces, or if you use tabs only, then code that does not align right.
Using tabs simply does not work in practice. Spaces may have the (supposed) disadvantage of a fixed indentation size, but they really are the only way to make indented code work across different editors. And this is not an optional thing, therefore tabs should be banned, completely.
if x:
----some_long_func_name(...,
blah...)
----fn2(x)
else:
----fn3()
However, I do agree that using tabs in python quickly becomes an enormous headache."Using tabs simply does not work in practice."
Outside of the python community, "tabs for indentation, space for alignment" is quite common. Many people use it in practice, to say that it simply doesn't work is ignoring reality.
And then, when others edit the code -- the use some wrong mixture of tabs and spaces.
What I said is that it won't work in practice in a multi-editor environment. If everyone has a uniformly configured vim, it may work -- but no work environment I've seen ever had that.
EDIT: Can you name a single example project source I can look at that correctly uses tabs for indentation only?
I've never seen it work, it always deteriorates into a mess in my experience. Also, do you ever work with people who use Eclipse, Visual Studio, Notepad++, and various non-vim editors?
For example.
if foo:
[tab]bar #valid
if foo:
bar #valid
if foo:
if baz:
bar #valid
if foo:
if baz:
[tab]bar #invalid Your four spaces disappeared but it wasn't a DEDENT.
if foo:
if baz:
[tab]bar #This can be valid, I guess. Doesn't harm anyone.
Once you don't allow people to interchange tabs and spaces it's perfectly safe to set tab width to any amount.def x():<enter>
<tab>"Oh no I'm on the reply button!"<Shift+Tab><space><space><space><space>print "Much Better"
It's true there are tools, especially on the web, that eliminate whitespace and if you rely on those tools using python is going to be a problem. But generally it's pretty obvious whether that's going to be an issue. Tab problems tend to sneak up when you least expect it.
In older tabbed python code, I've seen numerous patches fail to run, because the patch had the wrong indentation scheme. Worse, were patches I've found that ran, but with subtle bugs, because the change in indentation modified the logic without runtime errors.
Also, because tab is a pointless character and an outdated hack.
The problem is, a "tab" does not advance by 8 spaces; it advances to the next column divisible by 8! Shockingly, most editor writers don't seem to realize this; this appears to be especially true in the Windows world (perhaps because they never used typewriters or ASCII terminals?)
The resultant code is only readable in an editor with the same settings; for a language like Python, the resultant code will, in fact, be semantically incorrect.
Some editors that are not broken (eg. emacs, vim), allow you to specify a desired indentation level, and then the editor constructs the correct combination of tabs and spaces (ideally just spaces, for Python) to indent the code to the correct depth.
This comes from someone who has to regularly recover code mangled by dozens of Windows "programmers" who edit code on so-called "Programmers" editors with alternate tab settings...
In fact, you can mix 8 tabs with spaces in CPython (and do wacky stuff like
def f():
<tab><8 spaces>x=3
<4 spaces><tab><4 spaces>y=3
<8 spaces><tab>return x+y
My understanding of the article is that you shouldn't do it, because it's liable to cause confusion -- it's not safe.
You can mix spaces and tabs all you want as long as you are consistent. This is fine.
if 0:
<tab><sp><tab><sp>1
<tab><sp><tab><sp>2
This is not, for any number of spaces. if 0:
<tab>1
<sp><sp><sp><sp><sp><sp><sp><sp>2def foo(): <tab>print 'a' <sp><sp><sp><sp><sp><sp><sp><sp>print 'b'
compiles and runs properly in python 2.6.
I guess you probably just get used to it, I'm just coming from the 2 space Ruby standard.
sub func { shift->func1(shift)->func2(@_) }
sub things_with_attr { grep { $_[0]->is_attr_true($_) } $_[0]->things }
Only the function names have been obscured.