python questions / suggestions

7 views
Skip to first unread message

Quinn Dunkan

unread,
Dec 13, 1998, 3:00:00 AM12/13/98
to
Ok, so I'm just getting into python, and it's a neat little language, but
I have a few questions:

Why doesn't assignment return a value? In other words, why is it a statement
rather than an expression? This is such an obvious issue I'm guessing it's a
concious design decision, in which case I'd like to hear the rationale
(there's no mention in the FAQ). I'm doing:

while 1:
line = f.readline()
if not line:
break
m = re_foo.search(line)
if m:
...
else:
m = re_bar.search(line)
if m:
...
else:
ad nauseum
other_stuff

I can't use loop control to eliminate this endless ugly indentation because
other_stuff needs to get executed. The problem is that you can't use more
than one expression or the continuity of if, elif, else is broken. This isn't
normally a problem, but python makes it one by not letting you squeeze
multiple expressions into one, and then forcing you to indent how it wants you
to (not that I think this is such a bad feature, it just interacts poorly with
the above).

How is this better than:

while line = readline():
if m = re_foo.search(line):
...
elif m = re_bar.search(line):
...
else:
...
other_stuff

'a = b = 1' works, but 'a = (b = 1)' doesn't, so 'a = b = ...' must be some
kind of weird special case. How would I accomplish the above the "right way"?
Some people have said that using assignment as a test is harder to read, but I
have trouble understanding that. Doing assignment seperatly produces more
noise, an extra variable to keep track of, and makes expressions like the
above impossible with extreme ugliness. If you don't like assignment return
values you don't have to use them, but if you do like them and they're not
there, you're stuck.

I did a quick deja-news search and found some references to this, but none of
them were answered to my satisfaction... Perhaps this should go into the
'language design' section of the faq? (Seems like something a lot of people
would trip over, since every other language I know of does it the other
way).

Multi-level loop control statements would be very useful. Has anyone
thought of adding them? I know that you can use exceptions for that sort of
thing, but they can be really clumsy in certain instances where a simple
"break 2 levels" would suffice. This would just allow "break" and "continue"
to take integer arguments, no syntax difficulties.

Evan Simpson

unread,
Dec 13, 1998, 3:00:00 AM12/13/98
to

Quinn Dunkan wrote in message ...

>Ok, so I'm just getting into python, and it's a neat little language, but
>I have a few questions:
>
>Why doesn't assignment return a value? In other words, why is it a
statement
>rather than an expression? This is such an obvious issue I'm guessing it's
a
>concious design decision, in which case I'd like to hear the rationale
>(there's no mention in the FAQ).

First the nitpick: It's not assignment, it's name binding. You're not
putting a value in an existing reserved slot as you would in static
declared-variable languages, you're adding the value to a dictionary with
the 'variable' name as a key. This may seem like a quibble over airy
nothingness, but it's important to keep in mind as you delve further into
Python.

Now your anwer: I know of only two reasons; First, allowing a binding
statement to appear in an expression would allow it to be confused with or
mistyped as "==", the equality test. This is one of the nastiest ways to
shoot yourself in the foot in, say, C or C++. My personal answer to this
objection has been to propose the syntax " = a = 3" for passing on the value
of a name binding (that is, add a leading "="). This, unfortunately,
doesn't answer the second and more important reason, which is that Guido
doesn't want name binding expressions, so there won't be any :-)

>I did a quick deja-news search and found some references to this, but none
of
>them were answered to my satisfaction... Perhaps this should go into the
>'language design' section of the faq? (Seems like something a lot of
people
>would trip over, since every other language I know of does it the other
>way).


Do you know Pascal? ;-)

>Multi-level loop control statements would be very useful. Has anyone
>thought of adding them? I know that you can use exceptions for that sort
of
>thing, but they can be really clumsy in certain instances where a simple
>"break 2 levels" would suffice. This would just allow "break" and
"continue"
>to take integer arguments, no syntax difficulties.

I agree.

Evan

Dirk Heise

unread,
Dec 13, 1998, 3:00:00 AM12/13/98
to
Quinn Dunkan wrote:
> (there's no mention in the FAQ). I'm doing:
>
> while 1:
> line = f.readline()
> if not line:
> break
> m = re_foo.search(line)
> if m:
> ...
> else:
> m = re_bar.search(line)
> if m:
> ...
> else:
> ad nauseum
> other_stuff
>
What about this:


def search(some_re,line):
m = some_re.search(line)
return m


while 1:
line = f.readline()
if not line:
break

if search(re_foo,line):
...
elif search(re_bar,line):


...
else:
ad nauseum
other_stuff

Dirk

Richard Jones

unread,
Dec 13, 1998, 3:00:00 AM12/13/98
to

["Dirk Heise"]

The problem there is that you're then losing the match object. If that's not
important, then this solution is fine. Hell, you don't even need to have a
wrapper for it. From the example given, I assume that retaining the match
object is important.


Or how about my favourite:

patterns = {
re_foo: foo_handler,
re_bar: bar_handler,
...
}

while 1:
line = f.readline()

for pattern, handler in patterns.items():
m = pattern.search(line)
if m:
handler(m , other stuff)
break
else:
possible stuff to do if there's no match
other_stuff

Where foo_handler and bar_handler are just functions or methods. Catch here is
that the handlers aren't ordered. That may be achieved by using two lists to
store the pattern/handler pairs instead of a mapping (or use an order -
preserving mapping).


Richard

Tim Peters

unread,
Dec 14, 1998, 3:00:00 AM12/14/98
to
[Quinn Dunkan]
> ...

> Why doesn't assignment return a value? In other words, why is it
> a statement rather than an expression?

Maybe Guido got sick of tracking down "if (i = 0) {}" bugs in C? I know I
did. Over the years there have been many suggestions for safer syntax to
get at the functionality; most people lose interest after a short while,
though, so it hasn't become anyone's ongoing crusade.

> ...


> I'm doing:
>
> while 1:
> line = f.readline()
> if not line:
> break

Check out fileinput.py in the std library for a "for line in ...:" way to do
this instead. Or if the file is small enough, the obvious

for line in f.readlines():

works great.

> m = re_foo.search(line)
> if m:
> ...
> else:
> m = re_bar.search(line)
> if m:
> ...
> else:
> ad nauseum
> other_stuff
>

> I can't use loop control to eliminate this endless ugly
> indentation because other_stuff needs to get executed.

Deep nesting of any kind (ifs, loops, functions, parens, ...) is often a
sign of convoluted design, and is almost always hard to maintain regardless.
Exaggerate the problem and you'll find better approaches; e.g., if you had
500 regexps to search over here, you might end up reinventing the simple
table-driven pattern+action approach. At worst you'd end up with a more
maintainable hack, like the flat:

for justonce in ["faking a switch stmt"]:


m = re_foo.search(line)
if m:
...

break


m = re_bar.search(line)
if m:
...

break
ad nauseum
other_stuff

> ...


> but python makes it one by not letting you squeeze multiple
> expressions into one,

Python does discourage that, yes. When you feel you absolutely have to, you
can fake it with a simple class; e.g.,

class Tester:
def set(self, val):
self.result = val
return val
def get(self):
return self.result

re1 = re.compile("xyz").search
re2 = re.compile("kyd").search
re3 = re.compile("c").search

t = Tester()
s = "abc"
if t.set(re1(s)):
print t.get().group(0)
elif t.set(re2(s)):
print t.get().group(0)
elif t.set(re3(s)):
print t.get().group(0)

Or, if you're feeling perverse, go on to the revolting <0.9 wink>:

class Tester:
def __call__(self, val):
global result
result = val
return val

re1 = re.compile("xyz").search
re2 = re.compile("kyd").search
re3 = re.compile("c").search

test = Tester()
s = "abc"
if test(re1(s)):
print result.group(0)
elif test(re2(s)):
print result.group(0)
elif test(re3(s)):
print result.group(0)

Search DejaNews long enough and you'll find some quite elaborate classes for
doing similar stuff -- probably more fun to take it as an opportunity to try
a different programming style, though.

> ...


> How is this better than:
>
> while line = readline():
> if m = re_foo.search(line):
> ...
> elif m = re_bar.search(line):
> ...
> else:
> ...
> other_stuff

Just in that nobody (and esp. not a compiler) reading "if x = f(y):" can
guess whether it's a typo or your intent. Of course we could say that about
any line of code, but experience has shown this particular typo is nasty in
practice. Python has *many* times caught me doing this (intending "==" but
typing "="), and each resulting compile-time SyntaxError saved me only God
knows how much time tracking down a runtime bug instead.

> 'a = b = 1' works, but 'a = (b = 1)' doesn't, so 'a = b = ...'
> must be some kind of weird special case.

The syntax is spelled out in section 6.3 of the Reference Manual
("Assignment statements"). It's only a "weird special case" if, against all
evidence, you insist Python is some other language <wink>.

> ...


> If you don't like assignment return values you don't have to use
> them,

The problem is the same as in C -- you end up using them by mistake, when
they weren't intended. Guido's not going to change this. He may or may not
be open to adding a *different* syntax for embedded assignments in some
revision of the language, although I've seen no sign that he will.

> ...


> Multi-level loop control statements would be very useful.

You sure do love nesting <0.7 wink>.

> Has anyone thought of adding them?

It's been discussed, sure.

> I know that you can use exceptions for that sort of thing,

Yuck! That's been discussed too, of course. Better to design code so it
isn't necessary.

> but they can be really clumsy in certain instances where a simple
> "break 2 levels" would suffice. This would just allow "break"
> and "continue" to take integer arguments, no syntax difficulties.

That's brittle under code modification -- insert or delete an enclosing
loop, and the meaning of nested "break 2"/"continue 5" stmts can change
radically, in ways the compiler can't help catch. I feel a need for a
multi-level break or continue about once a year, so I'm not much of a fan of
this stuff. If it has to be added, better to follow the Perl or Java way
(i.e. via mnemonic alphabetic loop labels; "break reading"/"continue
writing"; those continue to target the intended loop under modification, and
if the intended loop goes away a compiler reliably catches the error).

simple-code-for-our-simple-minds-ly y'rs - tim

Quinn Dunkan

unread,
Dec 18, 1998, 3:00:00 AM12/18/98
to
On Mon, 14 Dec 1998 02:26:26 GMT, Tim Peters <tim...@email.msn.com> wrote:
[ lots of stuff ]

And it's all right, of course. Thanks for setting me straight :)

I have another weird idea, though.

A problem I have is that python's functional-esque features lure me into
functional-mode, and then I wind up trying to generate closures with lambdas
and nested definitions, and get lots of [a[0]] + process(a[1:]) looking things.

So I thought, how about partial application? E.g.:

>>> def foo(a, b): return a + b
...
>>> foo(5)
<function <closure> at xyzabc>
>>> foo(3)(5)
8

Which would make using map and friends much easier (instead of having
to generate the closures yourself with labmda). You can already get that
effect sort of: an instance method has its object already applied, but it only
works for the first argument of methods (which has to be self).

But then, python-is-not-haskell :)

David Ascher

unread,
Dec 18, 1998, 3:00:00 AM12/18/98
to
On 18 Dec 1998, Quinn Dunkan wrote:

> So I thought, how about partial application? E.g.:

Check out Don Beaudry's functor module available from python.org.

--david


Tim Peters

unread,
Dec 18, 1998, 3:00:00 AM12/18/98
to
[Tim, hazing a newbie with lots of argumentative stuff]

[Quinn Dunkan]


> And it's all right, of course. Thanks for setting me straight :)

That attitude will take you far in the Python world, Quinn! Much better
than having Guido come to your house 3am every night six weeks in a row,
breaking the windows and abusing your dog (or vice versa, when he's in a
really foul mood).

> I have another weird idea, though.

Confess them all! As the folks in comp.lang.perl will tell you, you'll be
miserable with Python until you let us crush every last shred of your ego,
creativity, and joy <wink>.

> A problem I have is that python's functional-esque features lure me
> into functional-mode,

If it's any consolation, you're in very good company. An interview with
Guido in the November '98 "Linux Journal" finally made official what's been
obvious for years to everyone who wasn't willfully blind <wink>: "... some
of the functional programming language features, such as lambda functions
..." was Guido's response to the question "What feature of Python are you
least pleased with?".

Python didn't have any of that stuff at the start, and a surprising number
of functional language fans (like, for instance, me) adjusted surprisingly
quickly, actually coming to prefer iteration over recursion, flat modules
over nested functions, and classes over closures.

Adding lambda/map/etc turned out to be a mistake: Guido viewed them as
minor conveniences (or "minor annoyances", as he says more often now <0.6
wink>), and they do work fine in small expert doses. Newcomers from
functional languages naturally read much more into them, though, and get the
initial impression that Python is a functional language wannabe designed by
some Dutchman with tulips for brains (yes, we all knew that's what you were
thinking <wink>).

It's not, though! It's an OO language wannabe designed by some Dutchman
with tulips for brains.

> and then I wind up trying to generate closures with lambdas
> and nested definitions, and get lots of [a[0]] + process(a[1:])
> looking things.

So stop doing that -- you'll never be happy with it in Python's current
incarnation. Classes will grow on you if you give them a chance.

> So I thought, how about partial application? E.g.:
>

> >>> def foo(a, b): return a + b
> ...
> >>> foo(5)
> <function <closure> at xyzabc>
> >>> foo(3)(5)
> 8
>
> Which would make using map and friends much easier (instead of having
> to generate the closures yourself with labmda). You can already get that
> effect sort of: an instance method has its object already applied, but it
> only works for the first argument of methods (which has to be self).

Currying is also easily done with classes. I think David Ascher already
pointed you to Donald Beaudry's functor module, but similar things can be
done in straight Python; e.g.,

class Curry:
def __init__(self, f, *fixedargs):
self.f = f
self.fixedargs = fixedargs

def __call__(self, *args, **kwargs):
return apply(self.f,
self.fixedargs + args,
kwargs)

def sum(a, b): return a + b

print Curry(sum, 3)(5), Curry(sum)(3, 5), Curry(sum, 3, 5)()

Python classes are more flexible than you at first think, and after you
learn how to toss off stuff like the above you'll love them: the closure
state is explicit (stored as instance attributes) so is easy to muck with.
E.g., if you have an already Curry'ed function object, it's a no-brainer to
add a method to Curry that will let you *change* its vector of fixed args.
It's also easy to write variants of Curry that let you fix the trailing
args, or any subset of args.

In fact, if you throw out lambda/map/etc entirely, you don't lose any
functionality. Even better, you usually end up with equivalent code that's
clearer and runs faster (e.g., a Python map is usually slower than a Python
loop) -- which isn't surprising to old-timers, because that's the way Python
was originally designed to be used. You come to the language without that
history, though, and lambda/map/etc look like the half-baked minor
conveniences that they are <0.7 wink>.

Python2 (the fabled successor) will likely support traditional lexical
scoping, which is "the right way" to stop peoples' expectations from getting
violated. On purely technical grounds, though, I think Python's "two level"
scoping was a much more successful experiment than it generally gets credit
for; it's not the least disappointment of lambda that its clumsiness
detracts from that achievement.

> But then, python-is-not-haskell :)

See? You're finding things to be grateful for already <wink>.

although-haskell's-list-comprehensions-are-more-pythonic-than-
many-later-python-additions-ly y'rs - tim

Reply all
Reply to author
Forward
0 new messages