Python Reviewed Having used a lot of languages a little bit and not finding satisfactory answers to these in some cases often asked questions, I thought I'd join this group to make a post on the virtues and otherwise of python. The Good: Syntactically significant new lines Syntactically significant indenting Different types of array like structures for different situations Mostly simple and clear structures Avoiding implicit structures like C++ references which add only negative value Avoiding overly complicated chaining expressions like "while(*d++=*s++);" Single syntax for block statements (well, sort of. I'm ignoring lines like "if a=b: c=d") Lack of a with statement which only obscures the code The Bad: Colons at the end of if/while/for blocks. Most of the arguments in favour of this decision boil down to PEP 20.2 "Explicit is better than implicit". Well, no. if/while/for blocks are already explicit. Adding the colon makes it doubly explicit and therefore redundant. There is no reason I can see why this colon can't be made optional except for possibly PEP20.13 "There should be one-- and preferably only one --obvious way to do it". I don't agree that point is sufficient to require colons. No end required for if/while/for blocks. This is particularly a problem when placing code into text without fixed width fonts. It also is a potential problem with tab expansion tricking the programmer. This could be done similarly to requiring declarations in Fortran, which if "implicit none" was added to the top of the program, declarations are required. So add a "Block Close Mandatory" (or similar) keyword to enforce this. In practice there is usually a blank line placed at the end of blocks to try to signal this to someone reading the code. Makes the code less readable and I would refer to PEP20.7 "Readability counts" This code block doesn't compile, even given that function "process" takes one string parameter: f=open(file) endwhile="" while (line=f.readline())!=None: process(line) endwhile I note that many solutions have been proposed to this. In C, it is the ability to write "while(line=fgets(f))" instead of "while((line=fgets(f))!=NULL)" which causes the confusion. No solutions have been accepted to the current method which is tacky: f=open(file) endwhile="" endif="" while True: line=f.readline if line = None: break endif process(line) endwhile Inadequacy of PEP249 - Python Database Specification. This only supports dynamic SQL but SQL and particularly select statements should be easier to work with in the normal cases where you don't need such statements. e.g: endselect="" idList = select from identities where surname = 'JONES': idVar = id forenameVar = forename surnameVar = surname dobVar = dob endselect endfor="" for id in idList: print id.forenameVar, id.dobVar endfor as opposed to what is presently required in the select case which is: curs = connection.cursor() curs.execute("select id, forename, surname, dob from identities where surname = 'JONES'") idList=curs.fetchall() endfor="" for id in idList: print id[1], id[3] endfor I think the improvement in readibility for the first option should be plain to all even in the extremely simple case I've shown. This is the sort of thing which should be possible in any language which works with a database but somehow the IT industry has lost it in the 1990s/2000s. Similarly an upgraded syntax for the insert/values statement which the SQL standard has mis-specified to make the value being inserted too far away from the column name. Should be more like: endinsert="" Insert into identities: id = 1 forename = 'John' surname = 'Smith' dob = '01-Jan-1970' endinsert One of the major problems with the status quo is the lack of named result columns. The other is that the programmer is required to convert the where clause into a string. The functionality of dynamic where/from clauses can still be provided without needing to rely on numbered result columns like so: endselect="" idList = select from identities where :where_clause: id = id forename = forename surname = surname dob = dob endselect Ideally, the bit after the equals sign would support all syntaxes allowed by the host database server which probably means it needs to be free text passed to the server. Where a string variable should be passed, the :variable syntax could be supported but this is not often required Variables never set to anything do not error until they are used, at least in implementations of Python 2 I have tried. e.g. UnlikelyCondition = False endif="" if UnlikelyCondition: print x endif The above code runs fine until UnlikelyCondition is set to True No do-while construct else keyword at the end of while loops is not obvious to those not familiar with it. Something more like whenFalse would be clearer Changing print from a statement to a function in Python 3 adds no positive value that I can see Upper delimiters being exclusive while lower delimiters are inclusive. This is very counter intuitive. e.g. range(1,4) returns [1,2,3]. Better to have the default base as one rather than zero IMO. Of course, the programmer should always be able to define the lower bound. This cannot be changed, of course. Lack of a single character in a method to refer to an attribute instead of a local variable, similar to C's "*" for dereferencing a pointer Inability to make simple chained assignments e.g. "a = b = 0" Conditional expression (<true-value> if <condition> else <false-value>) in Python is less intuitive than in C (<condition> ? <true-value> : <false-value>). Ref PEP308. Why BDFL chose the syntax he did is not at all clear. The Ugly: Persisting with the crapulence from C where a non zero integer is true and zero is false - only ever done because C lacked a boolean data type. This is a flagrant violation of PEP 20.2 "Explicit is better than implicit" and should be removed without providing backwards compatibility.
On Mon, Jan 9, 2017 at 10:25 PM, Simon Lovell <simon58500@bigpond.com> wrote:
Python Reviewed
Having used a lot of languages a little bit and not finding satisfactory answers to these in some cases often asked questions, I thought I'd join this group to make a post on the virtues and otherwise of python.
I think this thread belongs on python-list@python.org, where you'll find plenty of people happy to discuss why Python is and/or shouldn't be the way it is. A couple of responses to just a couple of your points.
The Good: Syntactically significant new lines Syntactically significant indenting
The Bad: No end required for if/while/for blocks. This is particularly a problem when placing code into text without fixed width fonts. It also is a potential problem with tab expansion tricking the programmer.
If indentation and line endings are significant, you shouldn't need end markers. They don't buy you anything. In any case, I've never missed them; in fact, Python code follows the "header and lines" concept that I've worked with in many, MANY data files for decades (think of the sectioned config file format, for example).
This code block doesn't compile, even given that function "process" takes one string parameter: f=open(file) endwhile="" while (line=f.readline())!=None: process(line) endwhile
I note that many solutions have been proposed to this. In C, it is the ability to write "while(line=fgets(f))" instead of "while((line=fgets(f))!=NULL)" which causes the confusion. No solutions have been accepted to the current method which is tacky: f=open(file) endwhile="" endif="" while True: line=f.readline if line = None: break endif process(line) endwhile
Here's a better way: for line in open(file): process(line) If you translate C code to Python, sure, it'll sometimes come out even uglier than the C original. But there's often a Pythonic way to write things.
Inadequacy of PEP249 - Python Database Specification. This only supports dynamic SQL but SQL and particularly select statements should be easier to work with in the normal cases where you don't need such statements. e.g: endselect="" idList = select from identities where surname = 'JONES': idVar = id forenameVar = forename surnameVar = surname dobVar = dob endselect
endfor="" for id in idList: print id.forenameVar, id.dobVar endfor
You're welcome to propose something like this. I suspect you could build an SQL engine that uses a class to create those bindings - something like: class people(identities): id, forename, surname, dob where="surname = 'JONES'" for person in people: print(person.forename, person.dob) Side point: "forename" and "surname" are inadvisable fields. http://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-nam...
One of the major problems with the status quo is the lack of named result columns. The other is that the programmer is required to convert the where clause into a string. The functionality of dynamic where/from clauses can still be provided without needing to rely on numbered result columns like so: endselect="" idList = select from identities where :where_clause: id = id forename = forename surname = surname dob = dob endselect
That's easy enough to do with a namedtuple.
Variables never set to anything do not error until they are used, at least in implementations of Python 2 I have tried. e.g. UnlikelyCondition = False endif="" if UnlikelyCondition: print x endif
The above code runs fine until UnlikelyCondition is set to True
That's because globals and builtins could be created dynamically. It's a consequence of not having variable declarations. You'll find a lot of editors/linters will flag this, though.
Changing print from a statement to a function in Python 3 adds no positive value that I can see
Adds heaps of positive value to a lot of people. You simply haven't met the situations where it's better. It's sufficiently better that I often use __future__ to pull it in even in 2.7-only projects.
Lack of a single character in a method to refer to an attribute instead of a local variable, similar to C's "*" for dereferencing a pointer
Ehh. "self." isn't that long. Python isn't AWK.
Inability to make simple chained assignments e.g. "a = b = 0"
Really? Works fine. You can chain assignment like that.
Conditional expression (<true-value> if <condition> else <false-value>) in Python is less intuitive than in C (<condition> ? <true-value> : <false-value>). Ref PEP308. Why BDFL chose the syntax he did is not at all clear.
I agree with you on this one - specifically, because the order of evaluation is "middle then outside", instead of left-to-right.
The Ugly: Persisting with the crapulence from C where a non zero integer is true and zero is false - only ever done because C lacked a boolean data type. This is a flagrant violation of PEP 20.2 "Explicit is better than implicit" and should be removed without providing backwards compatibility.
In Python, *everything* is either true or false. Anything that represents "something" is true, and anything that represents "nothing" is false. An empty list is false, but a list with items in it is true. This is incredibly helpful and most definitely not ugly; Python is not REXX. ChrisA
On 1/9/17 8:31 AM, Chris Angelico wrote:
On Mon, Jan 9, 2017 at 10:25 PM, Simon Lovell <simon58500@bigpond.com> wrote:
Python Reviewed
Having used a lot of languages a little bit and not finding satisfactory answers to these in some cases often asked questions, I thought I'd join this group to make a post on the virtues and otherwise of python. I think this thread belongs on python-list@python.org, where you'll find plenty of people happy to discuss why Python is and/or shouldn't be the way it is. I think this is the only reasonable response to this posting on this mailing list.
Simon: quoting from the Python-Ideas info page: "This list is to contain discussion of speculative language ideas for Python for possible inclusion into the language." Your comments, while interesting, don't make specific proposals for changes to Python. python-list@python.org is good for general discussion. If you do intend to make specific proposals, you'll have to put a lot more work into them. Proposals should be focused and specific; one thread with a dozen ideas makes discussion impossible. It helps to understand the language and its history. Many of your reactions to Python have been expressed many times before, so there are well-documented discussions and rationales for Python being the way it is. Doing some research beforehand can save you some work. Finally, backward compatibility is a serious consideration. Proposals containing new keywords, for example, are nearly impossible to get approved. Welcome to the community, --Ned.
On Mon, Jan 09, 2017 at 07:25:45PM +0800, Simon Lovell wrote:
The Good: Syntactically significant new lines Syntactically significant indenting Different types of array like structures for different situations Mostly simple and clear structures Avoiding implicit structures like C++ references which add only negative value Avoiding overly complicated chaining expressions like "while(*d++=*s++);" Single syntax for block statements (well, sort of. I'm ignoring lines like "if a=b: c=d") Lack of a with statement which only obscures the code
Python has a `with` statement. As a newcomer to this community, and apparently the language as well, do you understand how obnoxious and arrogant it comes across for you to declare what is and isn't "good" and "bad" about the language, particularly when you appear to know the language very well? Most of your comments aren't even a little bit objective, they're subjective judgements based on (I expect) what you are used to, and nothing more. Matters of taste, at best, and yet you're stating them as objective fact. So if you feel that my response is a tad blunt or even brusque, perhaps you can understand why.
The Bad: Colons at the end of if/while/for blocks. Most of the arguments in favour of this decision boil down to PEP 20.2 "Explicit is better than implicit".
This is the first time I've heard this ridiculous explanation for the use of colons! Where did you get it from? It sounds like the sort of nonsense that gets highly voted on StackOverflow. The reason for colons is a FAQ: https://docs.python.org/3/faq/design.html#why-are-colons-required-for-the-if...
No end required for if/while/for blocks.
Thank you Guido, for avoiding needing all those redundant and unnecessary "end" statements that other languages waste my time with.
This code block doesn't compile, even given that function "process" takes one string parameter: f=open(file) endwhile="" while (line=f.readline())!=None: process(line) endwhile
Assignment is not and should not be an expression, at very least not using the same = (pseudo-)operator which is used as an assignment statement. It may be acceptible with some other syntax that cannot be easily mistyped when you want == equality, but not = alone. But what is that obfuscated code meant to do? Operate on the file, one line at a time? This is simpler: with open(file) as f: for line in f: process(line) and it has the advantage of also automatically closing the file when the `with` block gets exited.
Inadequacy of PEP249 - Python Database Specification.
[...] I cannot comment on this.
Variables never set to anything do not error until they are used,
Fair enough, that is a minor annoyance occasionally.
No do-while construct
What do you mean by "do-while" and how do you expect it to differ from "while"?
else keyword at the end of while loops is not obvious to those not familiar with it. Something more like whenFalse would be clearer
Indeed. for...else and while...else are much more accurately described as for...then, while...then. The code in the "else" block is *unconditionally* executed following the for... or while... block, which makes the block a "then" rather than "else". To avoid that block, you have to jump out of the entire compound block, using "break", "return" or "raise". But we're stuck with the name now.
Changing print from a statement to a function in Python 3 adds no positive value that I can see
Consistency: print doesn't need to be a special cased statement. It does nothing special that a function can't do. So why make it a statement? As a function, it can be passed around as a first class value, used as a callback, monkey-patched or shadowed or mocked as needed. None of these things are possible with a statement. It can use the same ordinary syntax as other functions, instead of the bizarre and ugly special case syntax used as a statement: # Python 3 print(value, end='', file=sys.stderr) # Python 2 print >>sys.stderr, value, # note the magical trailing comma If Python was invented today, what arguments could anyone make for having print be a statement instead of a function? "It saves typing two parentheses." Anything else?
Upper delimiters being exclusive while lower delimiters are inclusive.
Dijkstra explained it well: http://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html Half-open ranges are much superior for avoiding off-by-one errors than closed ranges.
This is very counter intuitive. e.g. range(1,4) returns [1,2,3]. Better to have the default base as one rather than zero IMO. Of course, the programmer should always be able to define the lower bound. This cannot be changed, of course.
It is true that starting counting at zero takes a bit of getting used to, and there are times when it is more convenient to start at one. But no one solution is ideal all the time, and we have to pick one system or the other, or else we end up with an overly complex syntax with marginal utility.
Lack of a single character in a method to refer to an attribute instead of a local variable, similar to C's "*" for dereferencing a pointer
The lack of single-character syntax for many things helps prevents Python code looking like line noise.
Inability to make simple chained assignments e.g. "a = b = 0"
Python does support chained assignment.
Conditional expression (<true-value> if <condition> else <false-value>) in Python is less intuitive than in C (<condition> ? <true-value> : <false-value>). Ref PEP308. Why BDFL chose the syntax he did is not at all clear.
What is "intuitive" to people who have learned C and C-influenced languages is a perplexing, mysterious enigma almost impossible to google for or look up in books. flag ? 1 : 2 means nothing to people who haven't memorized what it means. But Python's ternary syntax is actual grammatically correct English: 1 if flag else 2 and it puts one of the values first, in the most important part, instead of the flag.
The Ugly: Persisting with the crapulence from C where a non zero integer is true and zero is false - only ever done because C lacked a boolean data type. This is a flagrant violation of PEP 20.2 "Explicit is better than implicit" and should be removed without providing backwards compatibility.
Thanks for your opinion, but I prefer the status quo. Welcome to the list. -- Steve
On Mon, Jan 9, 2017 at 11:40 AM, Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, Jan 09, 2017 at 07:25:45PM +0800, Simon Lovell wrote:
Lack of a with statement which only obscures the code
Python has a `with` statement.
I suspect Simon means similar to the VB with statement, which allows an object to become the default namespace. Basically, rather than: object.alpha() object.beta() you can do: with object: alpha() beta() or some slight variant thereof. Both cases do the same thing. Personally, I vastly prefer the explicit naming of self, and it should be noted that the style guides I have seen for C/C++ code have required member variable names to be prefixed with something like "m_" or just "_" to keep them from getting confused with local variables. Python solves this problem by requiring the object reference, and generally, I have found that much of the time it does not add that much extra to the code to have the explicit references, while making it very clear what is intended.
No do-while construct
What do you mean by "do-while" and how do you expect it to differ from "while"?
This one is somewhat common in other languages, and a do-while executes at least once (checks the condition at the end), while while executes at least zero times (checks the condition at the start). In C: do { ..block.. } while (condition); is the same as: ..block.. while (condition) { ..block.. } where both "..block.." are the same. The exact same result can be gotten with (in Python): while True: ..block.. if condition: break That said, I've only occasionally had use for the construct, and the most common case I've seen for it is multi-line macros in C/C++ where it is needed to get proper handling to require a semicolon at the end of the macro invocation and handle the optional braces in the flow control structures. Almost always, if checking the condition at the start is not good enough, I almost always want to check the condition somewhere in the middle instead, so the "while True:" works better.
This is very counter intuitive. e.g. range(1,4) returns [1,2,3]. Better to have the default base as one rather than zero IMO. Of course, the programmer should always be able to define the lower bound. This cannot be changed, of course.
It is true that starting counting at zero takes a bit of getting used to, and there are times when it is more convenient to start at one. But no one solution is ideal all the time, and we have to pick one system or the other, or else we end up with an overly complex syntax with marginal utility.
I've had to deal with mixed zero-based and one-based in coding before (and still often), and it was a huge pain. Zero-based works much better for many practical programming tasks, but can be difficult to get used to. I work in game development, so it is not uncommon to have mixed (artists and designers like one-based as they are generally non-technical). It is pretty annoying when may variable names have to be post fixed by "number" or "index" to try to keep it straight (and that fails half the time). The worst I had to deal with regarding it was in code that was crossing C++ and LUA, where C++ is zero-based and LUA is one-based - it was extremely difficult to remember all the needed +1s and -1s in the boundary code...
Lack of a single character in a method to refer to an attribute instead of a local variable, similar to C's "*" for dereferencing a pointer
The lack of single-character syntax for many things helps prevents Python code looking like line noise.
I would not recommend this, however it should be noted that Python assigns no special meaning to the name "self", and the variable could be named anything you want in your methods, including single character names. You would still need to type a minimum of two characters though. This is perfectly valid Python code, though would be against many (if not most) style guides: class MyObject: def __init__(s, myArg1, myArg2): s.myArg1 = myArg1 s.myArg2 = myArg2 a = MyObject(1, 2) print(a.myArg1, a.myArg2) # prints "1 2" If the variable "s" were renamed to "self", you would get the exact same result, however the code would match most style guides, and linters will likely stop complaining.
On 9 January 2017 at 12:25, Simon Lovell <simon58500@bigpond.com> wrote:
Python Reviewed
The Good : ... The Bad: ...
I agree with many points, but:
No end required for if/while/for blocks. .. Makes the code less readable
Nope, it makes code significantly better readable. I'm sort of past master in such questions so there is very little chance of BSing me. In some cases I'd want some space after the block, so probably future IDEs will allow placing small vertical indents to help with that.
No do-while construct
I don't think it is needed much, I never came up with thoughts that I want it. If I'd design a syntax from scratch, there would be only infinite loop and break.
Conditional expression in Python is less intuitive than in C
Probably, but conditional expressions are IMO not needed and I'd remove them just not to pollute the syntax. Mikhail
I just noticed a logical inconsistency here: The Good:
Syntactically significant indenting
The Bad: Colons at the end of if/while/for blocks.
No end required for if/while/for blocks.
Huh? if you have Syntactically significant indenting, then an "end" indicator is redundant. and right above, you say you don't like the redundant colon.... which is it?? It also is a potential problem with tab expansion tricking the programmer. I do wish tabs had been banned from indentation from the beginning.... -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov
Oh, Here is the history of the colon: http://python-history.blogspot.com/2009/02/early-language-design-and-develop... -CHB On Mon, Jan 9, 2017 at 5:30 PM, Chris Barker <chris.barker@noaa.gov> wrote:
I just noticed a logical inconsistency here:
The Good:
Syntactically significant indenting
The Bad: Colons at the end of if/while/for blocks.
No end required for if/while/for blocks.
Huh? if you have Syntactically significant indenting, then an "end" indicator is redundant. and right above, you say you don't like the redundant colon....
which is it??
It also is a potential problem with tab expansion tricking the programmer.
I do wish tabs had been banned from indentation from the beginning....
--
Christopher Barker, Ph.D. Oceanographer
Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception
Chris.Barker@noaa.gov
-- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov
participants (7)
-
Chris Angelico
-
Chris Barker
-
Chris Kaynor
-
Mikhail V
-
Ned Batchelder
-
Simon Lovell
-
Steven D'Aprano