default value in __init__

Aaron "Castironpi" Brady castironpi at gmail.com
Thu Oct 16 22:29:36 EDT 2008


On Oct 16, 7:54 pm, Steven D'Aprano <st... at REMOVE-THIS-
cybersource.com.au> wrote:
> On Thu, 16 Oct 2008 12:18:49 -0700, Aaron \"Castironpi\" Brady wrote:
>
> [snip]
>
> >> If Python re-evaluated the default value i=i at runtime, the above
> >> would break.
>
> > Not with a mere extra lambda.
>
> Not so. It has nothing to do with lambda, lambda just happens to be a
> convenient example. Here's the code I demonstrated:
>
> >>> for i in xrange(len(callbacks)):
>
> ...     callbacks[i] = lambda s, i=i: '%d %s' % (i, s)
> ...>>> for cb in callbacks:
>
> ...     print cb('string')
> ...
> 0 string
> 1 string
> 2 string
> 3 string
>
> At the end of the first loop, i == 3. If the default value i=i was re-
> evaluated each time the function was called, then i would get the value 3
> each time, which is the same behaviour you get from the version with this:
>
> callbacks[i] = lambda s: '%d %s' % (i, s)
>
> Worse, because you're now relying on i as a global, it's subject to
> strange and mysterious bugs if you later change i and then call the
> callback.
>
> > The fact that a syntax is an opportunity
> > to have a behavior does not imply that it should have one.  The fact
> > that newbies ask about these semantics doesn't imply that they'd ask
> > about another one just as much.  The fact that these semantics have
> > these two uses, doesn't imply that the others don't have more.
>
> Nowhere did I say that the one logically implies the other. I was asked
> for examples of how the current behaviour is useful, not to prove that
> the current behaviour logically follows from first principles. If you
> want to use a programming language where function default values are re-
> evaluated at runtime, you know where to find them.
>
> By the way, for the record I myself has found that behaviour useful on
> occasion. But that's easy to do with current Python:
>
> def spam(x, y=None):
>     if y is None:
>         # re-evaluate the default value at runtime
>         y = get_some_other_value()
>     return x + y
>
> So if you want that behaviour, you can get it. But if Python's semantics
> changed, then how would you implement today's semantics where the default
> is evaluated once only? I don't think you can. So Python's current
> semantics allows the behaviour you want, but in a slightly inconvenient
> form; but the re-evaluate-at-runtime semantics would prevent the
> behaviour I want completely.
>
> > Immutable defaults behave identically in both.
>
> Not quite. To have immutable defaults behave identically, you would need
> to remove at least one more feature of Python: the ability to set a
> default value to an arbitrary expression, not just a literal.
>
> Why do you need to do this? This toy example demonstrates the problem if
> you don't:
>
> yy = 3  # immutable value bound to the name yy
> def spam(x, y=yy-1):
>     return x + y
>
> will have the *expression* yy-1 re-evaluated when they call the function.
> That means that even though 2 is immutable, you can no longer rely on the
> default value being 2, or even existing at all. (What if I del yy at some
> point, then call the function?)
>
> So now to get the behaviour you desire, you not only have to change the
> way Python functions are implemented (and that will have a real and
> significant performance cost), but you also have to change the parser to
> only allow literals as default values.
>
> Note that there is absolutely nothing wrong with using an expression when
> setting default values. But you have to prohibit it, or else introduce
> unexpected behaviour which will trip up not just noobs but everybody. And
> then you'll have noobs writing in weekly asking why they can't write
> "def foo(x, y=10**6)" instead of y=10000000.
>
> --
> Steven

You're correct.  I overstated the fact.

> > Immutable defaults behave identically in both.
> Not quite. To have immutable defaults behave identically, you would need

Immutable literal defaults behave identically in both.  Obviously
semantics would change if semantics change.

> > Not with a mere extra lambda.
>
> Not so. It has nothing to do with lambda, lambda just happens to be a
> convenient example. Here's the code I demonstrated:
snip

What I stated is true, but I may not have stated it verbosely enough.

>>> callbacks= [ None ]* 3
>>> for i in range( len( callbacks ) ):
...     callbacks[ i ]= ( lambda i: ( lambda s: '%d %s' % (i, s) ) )
( i )
...
>>> for cb in callbacks:
...     print cb('string')
...
0 string
1 string
2 string

One extra lambda, called on the spot, as I stated.  The 'callbacks'
items do not rely on 'i' as a global; they contain a cell that refers
to the contents of 'i' at the time they're called, that is, a entry in
unique namespaces.  When 'i' is rebound the next time through the loop
or later, or deleted, they still have their values of it.

Of course, if you're iterating over a collection of mutables, the
namespaces get references to those.  Then, if you mutate your iterator
variable later, as opposed to rebind it, the namespace which contains
it in a cell will see that change; they are one in the same object.
Yours does that too.

> Nowhere did I say that the one logically implies the other. I was asked
> for examples of how the current behaviour is useful, not to prove that
> the current behaviour logically follows from first principles. If you
> want to use a programming language where function default values are re-
> evaluated at runtime, you know where to find them.

I'm almost getting the feeling that you're being defensive/protective
of Python of one of its quirks, as though that was important to its
identity, and you just couldn't look on it the same without it.  (A
pop t.v. character exclaimed, "I can't believe you caved!")  I'm not
sure to what extent pride factors in to stubbornness like that.  Is it
a selection criteria, such as you have to like quirks to be friends
with Pythoners?  No offense or anything; we're all proud of Python.

With, adopting the term, 'per-call evaluations', expressions
participate at their own risk.  If they refer to a variable that isn't
in their namespace closure, the evaulation of them fails.  It's true
whether you do it by hand:

def f( a= None ):
  if a is None:
    a= i #i non-local

Or it occurs in the language's definition.

> ... not to prove that
> the current behaviour logically follows from first principles.

Well, does it?  I jest.  It's not clear either one does.

Incidentally, you could combine the two issues to get a persistent
reference to the non-local variable you want to use in a default
value, copying it into the namespace; but you just have to mutate it
via its location when you do.

I think a decorator would be a good compromise.  Python keeps its
definition-time evaluation, and newbies, or whoever needs them, are
still able to use per-call evaluation.

> yy = 3  # immutable value bound to the name yy
> def spam(x, y=yy-1):
>     return x + y

yy= 3
@calltime_default( y= yy- 1 ) #might need quotes
def spam(x, y):
    return x + y

Then if you delete yy, 'spam' fails correctly.  I don't really see an
argument that it hurts the language (or the standard library) any...
provided its possible without syntax support.

I'm not so sure that weeding out newcomers by inattentiveness isn't
unconstructive or unbeneficial to either group, vets or newcomers.
You might argue that the same individuals would just ask just as
inattentive questions later on... but at least vets would have some
variety, and Python would have more fans.  It's not clear that the
feature is doing them the favor of gauging for them the level of
attentiveness writing Python requires.  I'll point out though as an
aside, that harmless introversion can be mistaken for actual elitism.



More information about the Python-list mailing list