A Summary: Expression-Assignments. (Very Long)

Moshe Zadka moshez at math.huji.ac.il
Tue May 11 16:30:15 EDT 1999


Hi. This is a rather flame-bait-ic mail about expression assignments in 
Python. I am sending it to get more opinions, which will be put in, 
and hopefully creating a ``current opinions'' document. It should be 
posted somewhere permanent, thus limiting further discussion only to the
degree it contains new ideas.

Assignments as returning a value in Python -- some discussion
=============================================================

Introduction
------------
Unlike C and Perl, in Python the code 'a=b' is a statement, not an
expression. That means it does not return any value, and hence does
not allow the common idiom of 'assign output of function to variable,
and immediately branch on the result', common in both Perl and C.
Indeed, the question often arises in the form of a newbie asking the
question
"How to translate Perl's while(<>){print;} idiom to Python?"

A similar question, which will be discussed in a seperate post, is
"Why doesn't Python have the C, Perl assignment operator, e..g. +=.
(Or even ++)".

Current Day Pythonic Answers to the Newbie Question
---------------------------------------------------

Way 1: Call the function in two places. The code usually looks like

line=sys.stdin.readline()
while line:
	sys.stdout.write(line)
	line=sys.stdin.readline()

Reasons for:
Simple, elegant, clear.

Reasons against:
Clumsy, verbose, same code in two places leads to trouble.

Way 2: Infinite loop and break. The code usually looks like

while 1:
	line=sys.stdin.readline()
	if not line: break
	sys.stdout.write(line)

Reasons for:
Short, clear.

Reasons against:
Infinite loops seem to remind people of BASIC's wonderful goto statements.

Way 3: Iterator class. The code usually looks like

class iterator:
	def __init__(self, function, *arguments):
		# self.i=0
		self.function=function
		self.arguments=arguments

	def __getitem__(self, i)
		# if self.i!=i: raise ValueError, 'non-consecutive access'
		# self.i=self.i+1
		val=apply(self.function, self..arguments)
		if not val: raise IndexError, 'no more items'
		return val

for line in iterator(sys.stdin.readline):
	sys.stdout.write(line)

Reasons for:
(After a one-time class definition) Really short, say-what-you-mean-ish.

Reasons against:
Poses as an array but isn't one. Unclear whether things in comments should
be there or not.

Pythoneers Rebuttals
--------------------

The expression-assignment is also the source of another common C and
Perl idiom, commonly known as the:

if(test=0) {
	do_something();
} else {
	launch_missile();
}

last bug....

in Python, 

if test=0:
	do_something()
else:
	launch_missile()

Is a compile-time syntax error, saving the world ;-).

Seriously, expression-assignment causes many such bugs, and not allowing
it transforms them into syntax errors, which is a Good Thing(TM).

The above Python solutions are workable (in particular, see the fileinput
module for more a true replacement for the diamond operator), and allowing

c=(a=b)+4

isn't very high on Python's list of priorities.

Core Changes Proposals
----------------------
1. Allow the assignment as an expression.

Code would look like:

while line=sys.stdin.readline():
	sys.stdout.write(line)
	
2. Allow another version the assignment operator, say :=, to be an expression.

Code would look like:

while line:=sys.stdin.readline():
	sys.stdout.write(line)

(This shows why := is a bad choice for such an operator. Perhaps |=?)

3. Allow test-in-the-middle loops

Code would look like:

do:
	line=sys.stdin.readline()
while line:
	sys.stdout.write(line)

A variant syntax suggested (and claimed to be endorsed by the Guido) is
``and while'', a distant cousin of ``else if''

while 1:
	line=sys.stdin.readline()
and while line:
	sys.stdout.write(line)

4. Make a version of ``for'' which uses a __getnext__/EndError instead
of __getitem__/IndexError. 

Code would look like:

class iterator:
	def __init__(self, function, *arguments):
		self.function=function
		self.arguments=arguments

	def __getitem__(self)
		val=apply(self.function, self.arguments)
		if not val: raise EndError, 'no more items'
		return val

another_for line in iterator(sys.stdin.readline):
	sys.stdout.write(line)

Please, do *not* follow-up to the group, mail me personally. I promise
I will not leave any opinion out because I disagree (I might make changes,
or merge similar opinions into a single suggestion, in the interest of 
clarity).

Also note that I disagree with many of the solutions purposed: this
document is not my personal opinion, but rather a collection of opinions
I saw on c.l.py

summarizing-discussions-ly y'rs, Z.
--
Moshe Zadka <mzadka at geocities.com>. 
QOTD: My own exit is more likely to be horizontal then perpendicular.






More information about the Python-list mailing list