[Python-ideas] For-loop variable scope: simultaneous possession and ingestion of cake

Carl Johnson carl at carlsensei.com
Mon Oct 6 04:43:22 CEST 2008


Why does the evil default args hack work? Because it immediately  
evaluates the argument and stores the result into the lambda object's  
default values list. So, what we need is a keyword that means  
"immediately evaluate the argument and store the result, instead of  
looking up the name again later." Since I can't think of a good name  
for this, I will use a terrible name for it, "immanentize."

 >>> i = 1
 >>> def f():
...     return immanentize i
...
 >>> i = 2
 >>> f()
1

 >>> lst = []
 >>> for i in range(10):
...     lst.append(lambda: immanentize i)
...
 >>> [f() for f in _]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


 >>> def side_effect_maker():
...     print("blah")
...     return datetime.datetime.now()
...
 >>> def f():
...     creation_time = immanentize side_effect_maker()
...     return datetime.datetime.now() - creation_time
...
"blah"
 >>> f() #Assume it takes you 5 seconds to type "f()" and press enter
datetime.timedelta(0, 5, 0)

Etc. Behind the scenes, this would look like syntatic sugar for  
something like creating a new variable name, evaluating the expression  
at initial compile time, setting the variable name to be the result of  
the evaluation, and replacing the immanentize expression with the  
variable name. Like this:

 >>> lst = []
 >>> for i in range(10):
...     random_variable_name_0..9 = i
...     # ie. on the first loop around random_variable_name_0 = 0, on  
the second loop random_variable_name_1 = 1, etc.
...     lst.append(lambda: random_variable_name_0..9)
...     # ie. the first loop is evaluated as  
lst.append(lambda(random_variable_name_0)), etc.
...
 >>> [f() for f in _]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


 >>> def side_effect_maker():
...     print("blah")
...     return datetime.datetime.now()
...
 >>> random_variable_name_number_0 = side_effect_maker()
"blah"
 >>> def f():
...     creation_time = random_variable_name_number_0
...     return datetime.datetime.now() - creation_time

I think this proposal is better than using "local:" or "scope:"  
because it doesn't create nested blocks when you just want to freeze  
out one particular value.

One interesting side effect of having an immanentize keyword is that  
in Python 4000, we could (if we wanted to) get rid of the supposed  
"wart" of having x=[] as a default arg leading to unexpected results  
for Python newbies. Just make it so that to get the current behavior  
you type

 >>> def f(x=immanentize []):
...     x.append(1)
...     return x
...
 >>> f()
[1]
 >>> f()
[1, 1]

Whereas, without immanentize we can do what newbies expect and  
evaluate the defaults afresh each time.

 >>> def f(x=[]):
...     x.append(1)
...     return x
...
 >>> f()
[1]
 >>> f()
[1]

Obviously, "immanentize" is a terrible name, and only just barely a  
word of English. Perhaps we could call it an "anonymous variable"  
instead?

-- Carl




More information about the Python-ideas mailing list