Optional parameter object re-used when instantiating multiple objects

A

Aaron Brady

On Nov 16, 12:52 am, Steven D'Aprano <st...@REMOVE-THIS-
I'm not so sure.
## Default evaluated at definition time.  (Current.)
# Static arg.
def f( a= [] ):
  ...
# Non-static arg.
def f( a= None ):
  if a is None: a= []

Oops.  Forgot one, after the subsequent posts.

# Non-static arg.
@nonstatic( a= list )
def f( a ):
  ...

This can achieve the 'if a is None' effect.  'nonstatic' takes a
callable or a string, '@nonstatic( a= "[]" )'.

I don't see a way to achieve George Sakkis's example:

    if y is None: y = x*x
    if z is None: z = x+y

Without a change to the language (the other options don't need one).

#emulates 'def foo(x, y=`x*x`, z=`x+y`):'
@nonstatic( y= 'x*x' ) #illegal
@nonstatic( z= 'x+y' ) #illegal
def foo(x, y, z):
    return x+y+z

Sorry for the second reply to myself. With a small change, the above
works (excited!), using George Sakkis's 'getcallargs' recipe: 'Recipe
551779: Introspecting call arguments', http://code.activestate.com/recipes/551779/

#emulates 'def foo(x, y=`x*x`, z=`x+y`):'
@nonstatic( y= 'x*x', z= 'x+y', a= '[]' )
def foo(x, y, z, a):
a.append( 0 )
print x, y, z, a
return x+y+z

print foo( 2 )
print foo( 1 )

/Output:

2 4 6 [0]
12
1 1 2 [0]
4

So, + a fraction for adding 'nonstatic' to 'functools', because there
is such high demand for it. We could just say, 'see the "nonstatic"
function!' when newbies ask. + a fraction on string or callable. No
change to language, no new syntax. Note: some concern about the
**kwargs iteration over variables in the same order they appear in the
arguments to the decorator, may be a deal-breaker.

In reply to Steve Holden,
Consider, though, the case were one argument value has to refer to
another. I would say the function body is the proper place to be doing
that computation. You appear to feel the def statement is the
appropriate place. If multiple statements are needed to perform the
argument initialization, how would you then propose the problem should
be solved?

I don't advocate prohibiting initialization of variables in the
function body, just adding this utility decorator. Both places are
appropriate to perform the initialization. I don't imagine the
decorator can cover every use case, just many.

If the **kwargs dictionary can reproduce the order in which the args
appeared, just initialize them in that order. Otherwise, since each
variable can only have one value in **kwargs, loop over the variables,
passing on "NameError: name ... is not defined", until all variables
are defined or no assignments can be made. It requires O( n^2 )
running time only on the first time through, since we can cache the
order that the assignments succeeded in.
 
R

Rick Giuly

Thanks to all for your replies

All things considered, I vote for evaluating the arguments at runtime
(each time the function is called). Good reasons for this have already
been mentioned so I won't repeat them. A programming language is a
user interface of sorts. (Pretty much all languages are logical so
"correctness" is not what I'm meaning to discuss - I'm thinking about
usability.) Python provides, for the most part, an *excellent* user
interface to the programmer. Why not make it even "better" by
evaluating the arguments each time the function is called? It will be
harder to change the language 10 years from now, so why not change it
now?

(By "better" I mean that over many years of time programmers will be
more productive because the language will be learned a bit faster with
a fewer surprises - and still retain its power.)

-Rick

For the Nth time this year that this has come up, I'll point out yet
again that this issue has already been discussed to death before:

[Python-ideas] proto-PEP: Fixing Non-constant Default Argumentshttp://mail.python.org/pipermail/python-ideas/2007-January/000121.html

[Python-3000] pre-PEP: Default Argument Expressionshttp://mail.python.org/pipermail/python-3000/2007-February/005704.html

Result: Evaluating the arguments at runtime rather than
definition-time was deemed too magical; the backward compatibility
issues make changes unlikely; it's hard to find an acceptable syntax.

But hey, if some people want to have another go at it, best of luck.

Cheers,
Chris
--
Follow the path of the Iguana...http://rebertia.com

Why, with another function of course!
def f(x, y=`f_arg_computation(x)`): ...
Or my personal favourite:
def f(x, **`f_arg_computation(x)`): ...
Seriously, though, I agree with Steve; the function body -is- the
place for computation to occur.
 
G

George Sakkis

Python provides, for the most part, an *excellent* user
interface to the programmer. Why not make it even "better"
by evaluating the arguments each time the function is called?
It will be harder to change the language 10 years from now,
so why not change it now?

You probably messed up with your time machine; "now" is 2008, not
1991 ;-)

George
 
G

George Sakkis

        Your opinion... I'm sure there are some libraries out there that
rely upon the current behavior

That's a straw man; of course they rely on it since they can. The same
would be even more true if the opposite behavior was selected from the
beginning, many more libraries would rely on it instead of (ab)using
None as default.
-- not to mention the hit on processing
speed.

That's the only argument that may worth some merit. It's interesting
though that C++, a language much more obsessed with performance, picks
the runtime semantics:

#include <iostream>
using namespace std;

int a = 1;

int f(int a) {
cout << "f(" << a << ") called\n";
return a;
}

int g(int x = f(a)) { return x; }

int main() {
cout << g() << endl;
a = 2;
cout << g() << endl;
}

#===== output ==============
f(1) called
1
f(2) called
2
        I wouldn't expect a language like Ada to somehow re-evaluate a
default argument on each call; why would I expect Python to do such?

Again, non sequitur. Python is not Ada (neither is C++, which does the
expected thing in this case).
        Besides, learning is good for one -- and having something like this
in Python gives one an excuse to learn something about language
implementation choices <G>

        And what behavior do you expect from:


d1 = [ 1, 2 ]
def what(arg = d1):
...     print arg

...    
what() [1, 2]
d1 = ( 3.14159, 2.78)
what()
[1, 2]

        If one does reevaluation of the default on each call, the second
call should be printing

(3.14159, 2.78)


[1, 2]

        And that sequence would result in an exception

Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
NameError: name 'd1' is not defined

        Do you really want a "default" argument that changes value depending
upon actions performed in the /surrounding/ scope?

Yes, the surrounding scope in this case is the global scope; changing
or deleting globals has by definition global reach. Regardless, how
common is this usage in real code ? I believe an order of magnitude
less than the need for fresh mutable objects.

George
 
A

Aaron Brady

        I wouldn't expect a language like Ada to somehow re-evaluate a
default argument on each call; why would I expect Python to do such?

Lots of people do.

If you had a menu in a browser interface that had the items, say,
'Stop' and 'Reload', what would you expect to happen if you clicked on
them?
 
A

alex23

If you had a menu in a browser interface that had the items, say,
'Stop' and 'Reload', what would you expect to happen if you clicked on
them?

If you had a keyword called 'def', which defined functions, would you
expect it to define said functions when it executed, or on each
function call?
 
A

Aaron Brady

If you had a keyword called 'def', which defined functions, would you
expect it to define said functions when it executed, or on each
function call?

At first, I would expect it to define them at compile-time. Then,
when I learned there was no such thing, I would expect it to define
them at execute-time. What does that have to do with evaluating a
default argument?
 
A

alex23

At first, I would expect it to define them at compile-time.  Then,
when I learned there was no such thing, I would expect it to define
them at execute-time.  What does that have to do with evaluating a
default argument?

It has -everything- to do with it when we're talking about the
defining of functions, and certainly a lot more relevance than straw
man arguments on the behaviour of browser buttons.

You said that you "would expect it to define them at execute-time". So
is that when the interpreter first hits the def or when the
interpreter hits every single function call? Because I personally
consider it to clearly occur at the point of definition and not at the
point of calling.
 
A

Aaron Brady

It has -everything- to do with it when we're talking about the
defining of functions, and certainly a lot more relevance than straw
man arguments on the behaviour of browser buttons.

You said that you "would expect it to define them at execute-time". So
is that when the interpreter first hits the def or when the
interpreter hits every single function call? Because I personally
consider it to clearly occur at the point of definition and not at the
point of calling.

Why, I would expect the interpreter to define the functions when it
first hits the def, that is, at the point of definition.
 
S

Steven D'Aprano

Do you really want a "default" argument that changes value depending
upon actions performed in the /surrounding/ scope?

And if you do, it is easy to get:

default_y = "something"

def parrot(x, y=None):
if y is None:
y = default_y
 
S

Steven D'Aprano

That's a straw man; of course they rely on it since they can. The same
would be even more true if the opposite behavior was selected from the
beginning, many more libraries would rely on it instead of (ab)using
None as default.

"Many more"? You're guessing. By memory, most uses in the standard
library for default values either use immutables (strings or ints), in
which case rebinding the default at runtime is irrelevant, or are using
None to trigger special behaviour. (Please feel free to go through the
std lib and verify my feeble memories.) The *specific* gotcha people are
complaining about only affects a tiny proportion of default values: in
practice, it is almost always either [] or {}.

It seems a waste to have Python pay the cost of re-evaluating all those
default=0 and default=None calls just to satisfy a small proportion of
users whose intuition about Python's behaviour happens to be wrong. Since
that intuition has real costs and the current behaviour has real
benefits, people should just learn the Python way.

(I'm sympathetic for requests for syntactic sugar to get runtime
evaluation of defaults. I'm not sympathetic at all for requests to change
the default behaviour.)


That's the only argument that may worth some merit. It's interesting
though that C++, a language much more obsessed with performance, picks
the runtime semantics:

You're not going to hold C++ up as a shining beacon of the right way to
do things, are you?
 
S

Steven D'Aprano

At first, I would expect it to define them at compile-time. Then, when
I learned there was no such thing,

Of course there is compile-time. When did you think the .pyc files got
created?

The same thing happens in the interactive interpreter: the function is
compiled to byte-code, and then the compiled function is executed by the
VM.
 
A

alex23

Why, I would expect the interpreter to define the functions when it
first hits the def, that is, at the point of definition.

Then why are you arguing that the parameters should be re-defined at
the point of calling?
 
C

Chris Rebert

Then why are you arguing that the parameters should be re-defined at
the point of calling?

I would assume because using the "def is start of definition" theory,
anything after the "def" is part of the function definition/body and
should not be immediately executed, but rather be executed at
call-time. The function body comes after the "def" and doesn't get run
immediately, so neither should the default argument values.

I take no position on the virtue of said theory.

Cheers,
Chris
 
A

Aaron Brady

Then why are you arguing that the parameters should be re-defined at
the point of calling?

Hmm. You have conflated defining the function with defining the
parameters.

My argument is that the syntax doesn't obviously imply either
interpretation.

I advocate a 'nonstatic' decorator. Also, I don't think a 'static'
decorator, had Python taken the other interpretation, would have been
that bad a combination.
 
A

alex23

Hmm.  You have conflated defining the function with defining the
parameters.

No, I think I've made the mistake of joining yet another pointless
thread arguing about semantics.
 
A

Aaron Brady

No, I think I've made the mistake of joining yet another pointless
thread arguing about semantics.

I was making the case that the options were about the same. Then you
had some questions.

I said, I would expect the interpreter to define the functions at a
certain time.

You asked, why are you arguing that the parameters should be re-
defined at a different time.

You assumed that defining a function and defining its parameters
necessarily happen at the same time. Not so.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top