Python Mystery Theatre -- Episode 2: Así Fue

Jason Trowbridge ratman at nmt.edu
Mon Jul 14 22:58:22 CEST 2003


I didn't look at docs or try out the code until after trying to solve
the problem.  I'm using Python 2.2.1.  I did not solve Act I or Act
III, and tried them out directly.

Act I
I didn't know that python formatting could do that!  I've always
treated it like C's printf-style of statements, as that seems to be
what it's primarily based off.  I've always used two string
substitutions to first replace the formatting parts, then actually
insert the real substitutions!

Eg:
>>> print ('%%%if' % (10,)) % (0.5,)
  0.500000
Now to find I can do:
>>> print '%*f' % (10, 0.5)
  0.500000

In some respects, moving from C/C++ to Python is a bit like moving
from Linux to Mac OS X.  I use the basic screwdriver, since I know how
and where it is, and don't see the nifty cordless power screwdriver
placed nicely in the cabinet.

Luckily, I have browsed through the entire module index at least once,
so I don't miss the jackhammers and use a trowel instead.


Act II
Again, there's behavior here that I didn't expect.  I first assumed
that the results would be:

print int('0100')          -> 100    (Correct)
print 16, int('0100', 16)  -> 16 256 (Correct)
print 10, int('0100', 10)  -> 10 100 (Correct)
print 8, int('0100', 8)    -> 8 64   (Correct)
print 2, int('0100', 2)    -> 2 4    (Correct)
print 0, int('0100', 0)    -> ?
print -909, int('0100', -909)  -> ?
print -1000, int('0100', -1000) -> ?
print None, int('0100', None) -> None 100 (Wrong, TypeError occurs)

The interesting thing is when I tried it out:
>>> int('0100', 0)
64
>>> int('0100', -909)
100
>>> int('0100', -1000)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: int() base must be >= 2 and <= 36

I am using Python 2.2.1.  According to the doc for int(x, radix), the
part about the radix behaviour is as follows:

The radix parameter gives the base for the conversion and may be any
integer in the range [2, 36], or zero. If radix is zero, the proper
radix is guessed based on the contents of string; the interpretation
is the same as for integer literals.

That explains the 0 radix and the exception caused by the -1000 radix.
 So why does it work with a radix of -909?  I presume a bug (which
probably got fixed in later versions of Python).  I'll have to see if
this behavior is present under Python 2.3b at home.


Act III
Ick.  Lambda's.  Skipping for now.


Act IV
I would guess that it would print:
'Now there are three'
Since the environmental variable one is 'Now there are', and the
environmental variable two is 'three'.

My bad.  Upon running, I get:
'Now there are None'

Apparently, os.putenv() doesn't work like I thought.

Ah!  os.putenv() updates the environment, but not the os.environ
dictionary.  It looks like os.getenv() retrieves the environmental
variables from os.environ, and just assumes that it is up to date. 
Since it defaults to None if the environmental variable doesn't exist
in os.environ[], that's what I get.

Hmm, so os.getenv() and os.putenv() are not symmetric.  This isn't
mentioned in the Python 2.2.1 documentation, as os.getenv() is listed
as:

Return the value of the environment variable varname if it exists, or
value if it doesn't. value defaults to None.

This misleads that it is getting the value from the actual
environment, not the os.environ[] variable.  Nasty and subtle, too!


Act III

Ick.  Lambda's.  Oh well, here's a stab at it.

funcs is a list of functions.
flim is a list of unnamed functions that call the functions in funcs.
flam is a list comprehension of lambda's that call the functions in
funcs.

flim and flam should be functionally equivalent (2/3 pun intended).

The output should be:
 1 2 3
 1 2 3

Since they are just calling the functions listed in funcs (once,
twice, thrice).

Hmm, the output is really:
 1 2 3
 3 3 3

That's odd.  Why is this the result here?

>>> print  [ f.__name__  for f in funcs]
['once', 'twice', 'thrice']

So, f is updating correctly to the next value.

>>> for test in [ lambda x: f.__name__  for f in funcs]: print
test(1), id(test)
...
thrice 135971068
thrice 136291772
thrice 135757396

Okay, so the lambda's being created are unique, yet are being mapped
to the third function.

>>> def fourth(x): return 4*x
...
>>> f
<function thrice at 0x81c36bc>
>>> f = fourth
>>> f
<function fourth at 0x817383c>
>>> flam[0](1)
4

Aha!  So the lambda is looking up f in the current scope when it is
executed! Instead of binding to the actual function object being
iterated over in the list comprehension, the lambda is binding to the
variable 'f' itself?

Ick!  Ick!  Ick!  Bad touch!

(Hey, these are fun!)

 _  () ()      Jason Trowbridge | "... but his last footer says 'page
( '  .~.     Generic Programmer | 3 of 2', which leads me to believe
 \  = o =                       |  something is wrong."
---"`-`-'"---+   ratman at nmt.edu |   --Scott Bucholtz




More information about the Python-list mailing list