Default Value

A

alex23

Okay. So now you are admitting the problem. That is a good
start. Thanks for being honest.

If you think mutable default arguments is a "problem", then you don't
really understand Python. The only "gotcha" here is in people's heads.
 
S

Steven D'Aprano

Oh wait a minute. i think it's becoming clear to me now!

If only that were true, but I fear that you're just looking for an
argument.

Python functions are objects that take arguments, of which (the
arguments) are then converted to attributes of the function object.

Arguments in general are *not* converted to attributes. If you call a
function func(x=1, y=2), arguments 1 and 2 are not stored as attributes
anywhere. That would be silly, since 1 and 2 become local variables and
there is no point in storing them for later use since they don't get used
later.

Only default values for parameters are stored for later use. They have to
be stored *somewhere*, and Python chooses to store them as an attribute
of the function object, where they can be easily inspected, rather than
by storing them inside some undocumented and hidden location locked up in
a binary blob.

Even if Python chose late binding instead of early binding for function
defaults, the default would *still* need to be stored somewhere. The only
difference is that with early binding, the default value is calculated
once, and the object stored, while with late binding the default value is
re-calculated over and over again, every time it is needed, and a piece
of code that calculates the default value is stored.

Ah-Ha! Urm, but wait! We already have a method to define Objects. Heck,
I can even create my own callable objects if i want!
[snip]

Yes, you can create callable objects by defining a __call__ method.
That's okay. It is pure slander, invented by Perl programmers, that
Python gives you "only one way to do it". Python has functions for 99% of
your subroutine needs, and for more complex cases where your subroutine
needs to store permanent data and make them available to the caller, you
have class-based callables.

But really, using a class-based callable is a PITA. You have to define a
class, write an __init__ method, write a __call__ method, instantiate the
class, store the instance, and call it. 99% of the time a function will
do the job more easily, especially now that Python supports closures.


[...]
But then, i can even do it WITHOUT creating an object definition:

py> mutable = []
py> def expandMutable(arg):
... mutable.append(arg)
...
[...]

The biggest, most obvious problem with the expandMutable approach is that
it relies on a global variable. I find it implausible that you, an
experienced programmer, is unaware of the dangers of global variables.
"Global variables considered harmful" has been a well-known principle of
programming dating back to the 1970s.

ANY of those approaches are much less confusing than the current flaw

Confusing for whom? Beginners, who don't know the first thing about
Python? Programmers who are experienced with some other language but have
no clue about what these weird __init__ and __call__ methods do?
Programmers with 40 years experience who don't know anything about object-
oriented code? People who have been immersed in Python coding for 15
years? Every feature is confusing to *some* people and not others.

and do not violate the least astonishment law..

That's a misuse of the Principle of Least Astonishment. Not just a
misuse, but in fact you've got it backwards: late binding of default
variables would violate the principle of least astonishment.

Python functions are created *once*, when defined. The cost of building
the function -- compiling the source code to byte code, assembling the
pieces into a function object, binding it to a name -- happens once and
once only, not every time you call the function. So it is reasonable to
expect that since the function is defined once, so are any default
arguments.

There are two obvious arguments against late binding: efficiency, and
consistency. Late binding is less efficient: if the code that defines the
default is expensive, you have to pay that cost once, that can't be
avoiding. But late binding makes you pay it again and again and again:

def f(arg, value=time.sleep(100)+5): # simulate an expensive default
...

I have no doubt that if Python recalculated the default every time, you
would be arguing that this is surprising and painful and that Python
ought to calculate the value once and cache it somewhere.

The other gotcha with late binding is that because the default is
recalculated each time, it can surprise you by changing unexpectedly:

def f(arg, value=x+5):
...

If the value of x changes, so does the value of the default. This may
surprise you. (It would certainly surprise me.)

I'm quite Okay with
Python functions being first class objects, however, i am not okay with
violating the fundamental nature of subroutines,

In what way to Python functions violate the fundamental nature of
subroutines?

especially when that
violation can offer no clear and arguable benefits and is in fact
unreasonably esoteric in nature.

Early binding offers clear benefits:

- consistency: the default cannot mysteriously change value due
to an unrelated change;

- efficiency: the default is calculated once, not over and over;

- it's easy to get late binding semantics starting with early
binding, but more difficult to go the other way.

- simplicity of implementation: late binding requires that
Python store, not the result of the expression, but the
expression itself (together with a context) for later
evaluation; early binding simply requires that the expression
is evaluated at runtime, like any other expression.


That's not to say that there are no arguments in favour of late binding.
But on balance, despite the odd sharp corner, I believe that early
binding's benefits far outweigh its gotchas.
 
S

Steven D'Aprano

Additionally, with late-binding semantics the default values would no
longer be default *values*. They would be initialization code instead,
which sort of flies in the face of the idea that late-binding would
somehow be better for functional programming -- if the default
expressions have no side effects and since they don't depend on the
function arguments, why should they need to run more than once? If the
goal is indeed to make the the functions more functional, then the
proper solution would be to keep the binding early but just disallow
mutable defaults altogether -- which is tricky to achieve in Python, so
we simply emulate it with the advice "don't use mutable function
defaults".


Well said!


At the risk of having twice as many gotchas, and at making the
implementation become even more complex, I would very slightly lean
towards allowing some sort of syntactic sugar for late binding default
values just to shut people up about this issue. Here's my syntax plucked
out of thin air:

def func(arg, x=expression, !y=expression):
...

where y=expression is late-bound, and the above is compiled to:

def func(arg, x=expression, y=None):
if y is None:
y = expression
...


Then Ranting Rick can bitch and moan about how this violates "Explicit is
better than implicit".
 
T

Tim Chase

Here's my syntax plucked out of thin air:

def func(arg, x=expression, !y=expression):
...

where y=expression is late-bound, and the above is compiled to:

def func(arg, x=expression, y=None):
if y is None:
y = expression
...

I think it would have to translate to something like

_temp_semaphore = object()
def func(arg, x=expression, y=_temp_semaphore):
if y is _temp_semaphore:
y = expression

...

because there are times you want to be able to pass None as a legit
value in such a case (speaking from the trenches after getting stung
by exactly that case on multiple occasions).

-tkc
 
S

Steven D'Aprano

When the subroutine is completed, all inputs and local variables are
expected to be destroyed. If the programmer wants a return value, he
need simply ask. Data persistence is not a function of subroutines!
Finally, a subroutine should never have side effects UNLESS the
programmer explicitly ask for a side effect.

Correct. And by using a default value, you are explicitly asking for a
side-effect, namely, "use this object as the default" (not, "use this
expression, and re-evaluate it at call-time").

That some people do not comprehend the *consequences* of doing so is not
Python's fault, any more that it is Python's fault when people write:

a = b = []
a.append(1)

and then are surprised that b is no longer empty. That doesn't happen
with

x = y = 0
x += 1

therefore Python is buggy, yes? No.

In short, your invalid understanding of Python's execution model is your
lack of knowledge, not a bug in Python to be fixed.
 
R

Roy Smith

Tim Chase said:
I think it would have to translate to something like

_temp_semaphore = object()
def func(arg, x=expression, y=_temp_semaphore):
if y is _temp_semaphore:
y = expression

...

because there are times you want to be able to pass None as a legit
value in such a case (speaking from the trenches after getting stung
by exactly that case on multiple occasions).

You can go around and around on that one. Whatever distinguished value
you set aside to mean "no argument passed", somebody will come up with a
use case for why they might want to pass that as a real value.

If you really want to avoid then, then you need to go to something like:

def func(arg, **kwargs):
if 'x' not in kwargs:
kwargs['x'] = expression

and so on. But that's klunky enough, I'm willing to accept the in-band
signalling consequences of using None as a sentinel.
 
T

Tim Chase

You can go around and around on that one. Whatever distinguished
value you set aside to mean "no argument passed", somebody will
come up with a use case for why they might want to pass that as a
real value.

That's why I suggested using an "is" test against a custom/local(ish)
semaphore instance that is only used for the particular invocation in
which the "def" statement occurs.

-tkc
 
8

88888 Dihedral

rusiæ–¼ 2013å¹´6月21日星期五UTC+8上åˆ1時12分01秒寫é“:
You know Rick, you are good at python, you are better at polemics.

If only you would cut the crap I would (be able to) agree with you.

See below



So are we purposely injecting illogic into our language just
to be part of some "cool crowd" of programming languages with


 "You thought intuitiveness was a virtue? Haha, we gotcha!"





Python (and all the other 'cool' languages) dont have gotchas because

someone malevolently put them there.

In most cases, the problem is seen too late and the cost of changing

entrenched code too great.

Or the problem is clear, the solution is not etc etc.


Or maybe this is reminiscent of the fraternity hazing rituals
of days gone by:
 *POP*

 "Thank you Sir, may i have another?"
If you are a beginning python programmer, really the best
answer is: Just dont do it!  Dont do what? Dont use
mutable arguments as function defaults. Once you cross the
noob stage you can check that its a standard gotcha: Just
run a search for 'python gotcha default []'
And when you feel that you need to, use Steven's trick:
use a immutable indicator 'None' for the mutable []. Once
you cross the noob stage you can check that its a standard
gotcha: Just run a search for 'python gotcha default []'
Its even been discussed repeatedly on the python-ideas
list
Your attempting to excuse an inexcusable language flaw by
pointing out that the effects of the flaw can be avoided by
using a workaround. And, to add insult to injury, you
provide no good reason for the flaw to exist:

 "Just do x, y, and z and shut up. Yes we made a mistake
 but we are not about to admit our mistake and the onerous
 will be on all the noobs to re-invent the workaround each
 time"

To me that is unacceptable.
Heres a correction suggestion: [...] Here's Terry Reedy's
nicely explaining the 'why-of-the-how' : [...] FWIW here
is a conceptual/philosophical explanation of your
confusion: There are 2 fundamental ways for approaching
the programming task -- functional and imperative.
All these "explanations" ignore a fundamental fact about
subroutines[1].



A call to a subroutine should exists as a unique transaction
within the entire program. It is expected to optionally take
inputs, and optionally return an output (depending on the


When the subroutine is completed, all inputs and local
variables are expected to be destroyed. If the programmer
wants a return value, he need simply ask. Data persistence
is not a function of subroutines! Finally, a subroutine
should never have side effects UNLESS the programmer
explicitly ask for a side effect.

However, the Python flaw of allowing mutable sequence
arguments to persist between successive calls of a
subroutine is breaking the entire definition of a
subroutine, and for what noble purpose i ask? What purpose
is SO important that you change a well established interface
in a totally unintuitive manner?

If your answer is that recursion is easier, then i say that
is foolish because a programmer can keep a reference
to a mutable sequence OUTSIDE the subroutine and you can
save the "gotchas" for Guido's next birthday party.
[1]:http://en.wikipedia.org/wiki/Subroutine





You are saying in different language what I said: Functional

programming is a good idea, imperative programming is a bad idea.

From the invention of subroutines (ie 50 years) PL designers are

hearing this truth but somehow or other fail to listen; for more

details see http://blog.languager.org/2012/11/imperative-programming-lessons-not.html

Well, that was the story of the expensive memory era.the
In 198X -2000 the auto GC of LISP was beaten
by practical engineers using C, C++, PASCAL, and object pascal.

But now it is different in 201X.
 
S

Steven D'Aprano

Python (and all the other 'cool' languages) dont have gotchas because
someone malevolently put them there.
In most cases, the problem is seen too late and the cost of changing
entrenched code too great.
Or the problem is clear, the solution is not etc etc.

And sometimes the gotcha is unavoidably in the nature of the problem.
This is remarkably common in real life, so much so that in English there
is even a proverb for it: "You can't have your cake and eat it too", or
to put it another way, you can't save your cake for later and eat it now
at the same time.

For default arguments, you cannot simultaneously have them evaluated once
*and* evaluated each time they are needed. Whichever choice you make,
there will be situations that lead to a gotcha. Even if Python supported
syntax for both, there would be cases where people used the wrong syntax,
not knowing any better, and would trip over unexpected behaviour.
 
R

Rick Johnson

Arguments in general are *not* converted to attributes. If
you call a function func(x=1, y=2), arguments 1 and 2 are
not stored as attributes anywhere. That would be silly,
since 1 and 2 become local variables and there is no point
in storing them for later use since they don't get used
later. Only default values for parameters are stored for
later use.

Obviously you've lost the ability to recognize sarcasm. :(
They have to be stored *somewhere*, and Python chooses to
store them as an attribute of the function object, where
they can be easily inspected, rather than by storing them
inside some undocumented and hidden location locked up in
a binary blob.

I like how that last sentence implicitly argues against an
argument i never made, whilst explicitly supporting your
argument. But, by all means Steven, proceed.
Even if Python chose late binding instead of early binding
for function defaults, the default would *still* need to
be stored somewhere.

No, if your passing in a symbol, then the object it points to
must exist *somewhere*. OTOH if your passing in a literal,
or an expression, then the subroutine will need to "store"
the resulting object. Yes.
The only difference is that with early binding, the
default value is calculated once, and the object stored,
while with late binding the default value is re-calculated
over and over again, every time it is needed, and a piece
of code that calculates the default value is stored.

And that is *ONLY* the case using the currently broken
system. You're arguing that the status-quo is better
because the only alternative would be to re-evaluate the
argument every time, when in fact, i provided three code
examples that will not require re-evaluation of the mutable
and i did so without sacrificing readability. Your argument
is weak.
Ah-Ha! Urm, but wait! We already have a method to define
Objects. Heck, I can even create my own callable objects
if i want!
[snip]
Yes, you can create callable objects by defining a
__call__ method. That's okay. It is pure slander, invented
by Perl programmers, that Python gives you "only one way
to do it". Python has functions for 99% of your subroutine
needs, and for more complex cases where your subroutine
needs to store permanent data and make them available to
the caller, you have class-based callables. But really,
using a class-based callable is a PITA. You have to define
a class, write an __init__ method, write a __call__
method, instantiate the class, store the instance, and
call it.

Mother of GOD call the authorities because a little girly
man has been violated! That mean old Rick and his insistence
on readability is causing a pandemic of carpal tunnel and
PTSD syndrome. Haul him away, lock him up, and throw away
the keys! Psst: That was sarcasm.
But then, i can even do it WITHOUT creating an object
definition:
py> mutable = []
py> def expandMutable(arg):
... mutable.append(arg)
...
[...]
The biggest, most obvious problem with the expandMutable
approach is that it relies on a global variable. I find it
implausible that you, an experienced programmer, is
unaware of the dangers of global variables.

I am quite aware of the dangers of globals, and i abhor
their use in 99 percent of the cases. But we're talking
about Python here *giggle*. The limits of a Python "global"
are the module for which it is declared. Plus, this is
quite amusing that you are now seemingly preferring the OOP
style; how many times have you made snide remarks about my
penchant for OOP? I dunno because i stopped counting after i
ran out of fingers!

Yes i can only count to ten, there, i beat you to the joke.
Now we can focus again.
Confusing for whom? Beginners, who don't know the first
thing about Python?

Of course programming noobs are going to be confused by this.
Programmers who are experienced with
some other language but have no clue about what these
weird __init__ and __call__ methods do?

So now your going to argue that experienced programmers are
going to intuit an IMPLICIT and unconventional subroutine
data persistence, but they cannot intuit EXPLICIT words like
"init" and "call"? Oh Please!
Programmers with
40 years experience who don't know anything about object-
oriented code?

Even those old dogs who refuse to use GUI's and code in the
OOP style deserve a little more credit than you are giving
them Steven. They might be dinosaurs but they're not stupid.
Hardheaded, yes. Incapable of accepting change, yes. But
stupid, no no no.
People who have been immersed in Python coding for 15 years?

Well, if they are confused after 15 years i would suggest
they take up a new profession.
Every feature is confusing to *some* people and not others.

Indeed. But your not going win this argument by offering
such generic postulations. Yes, noobs will be completely
confused and experienced programmers from other languages
will suffer a "WTF" moment, quickly followed by a: "Now i
got to go RTFM because some cowboy designer decided to throw
me a curve ball!!!" moment.

When designing a language, intuitiveness should always be an
important consideration, however, it need not be the ONLY
consideration. There is no such thing an interface that will
be intuitive to *everyone* on planet earth.

But for this PyWart (and that's exactly what it is!) you're
not only dealing with a violation of intuitiveness, your
also dealing with a violation of fundamentals of subroutines;
which has been established for many years across many
languages.
That's a misuse of the Principle of Least Astonishment.
Not just a misuse, but in fact you've got it backwards:
late binding of default variables would violate the
principle of least astonishment. Python functions are
created *once*, when defined. The cost of building the
function -- compiling the source code to byte code,
assembling the pieces into a function object, binding it
to a name -- happens once and once only, not every time
you call the function. So it is reasonable to expect that
since the function is defined once, so are any default
arguments.

Here you go again with this diversionary tactics! Of course
the function is only compiled once! Anything else would be
ludicrous. PAY ATTENTION! I'm not arguing that the damn
function should be compiled each time it's called, no, i am
arguing that each call to a subroutine should be a UNIQUE
TRANSACTION. That means that no state can be carried from
one transaction to another.
There are two obvious arguments against late binding: efficiency, and
consistency. Late binding is less efficient: if the code that defines the
default is expensive, you have to pay that cost once, that can't be
avoiding. But late binding makes you pay it again and again and again:
def f(arg, value=time.sleep(100)+5): # simulate an expensive default
...

You still don't understand what a TRANSACTION is do you? You
would rather beat subroutines with a hammer until they
resemble some pie in the sky, one size fits all
*monstrosity* instead of taking a few seconds out of your
precious time to creating a object to accomplish the same
task.

BamBam Says: "BamBam make square peg fit through round hole"
*BAM*
*BAM*
Mom Says: Okay BamBam. Let's play nice please.
I have no doubt that if Python recalculated the default every time, you
would be arguing that this is surprising and painful and that Python
ought to calculate the value once and cache it somewhere.
The other gotcha with late binding is that because the default is
recalculated each time, it can surprise you by changing unexpectedly:
def f(arg, value=x+5):
...
If the value of x changes, so does the value of the default. This may
surprise you. (It would certainly surprise me.)

No Steven, because i wouldn't be foolish enough to write
code like that. That looks like a disaster waiting to happen!
But i must admit, i wish i could write bad code and then
justify it on the bases that a flaw in the language is going
to save me. Have you actually done this in reality? But more
importantly, do you realize that there exist better ways?
In what way to Python functions violate the fundamental
nature of subroutines?

If you expect me to type it out twice then you're in for a
rude awaking. Next time, try reading my entire post.
Early binding offers clear benefits:
- consistency: the default cannot mysteriously change value due
to an unrelated change;

That's a solution to a problem only a fool would create.
- efficiency: the default is calculated once, not over and over;

I prefer clear and readable code over obfuscation and magic.
If i want to be astonished, i'll go watch David Copperfeild
disappear an elephant, thank you very much.
 
C

Chris Angelico

Here you go again with this diversionary tactics! Of course
the function is only compiled once! Anything else would be
ludicrous. PAY ATTENTION! I'm not arguing that the damn
function should be compiled each time it's called, no, i am
arguing that each call to a subroutine should be a UNIQUE
TRANSACTION. That means that no state can be carried from
one transaction to another.

Why should that be? Why is a subroutine not allowed to retain any state?

You're free to write code in a purely functional style if you like -
all objects must be immutable, all functions must have no side effects
and simply return a value. That's not the only way to code, and it is
*definitely* not a rule to be codified into Python. One of Python's
strengths is that it permits many styles of programming, without
shoehorning every programmer and every task into one model.

Of course, RickPy 4000 is welcome to enforce all these sorts of inane
rules if it likes. I shan't be using it if it does.

ChrisA
 
R

Rick Johnson

Why should that be? Why is a subroutine not allowed to
retain any state?

I refuse to repeat myself for lazy readers!
You're free to write code in a purely functional style if
you like

I don't want to write code in a purely functional style, i
find that style too restricting.
all objects must be immutable,

That would suck!
all functions must have no side effects and simply return
a value.

No thanks!
One of Python's strengths is that it permits many styles
of programming, without shoehorning every programmer and
every task into one model.

Yes, and i consider that aspect of Python a virtue. But this
"persistence of mutable arguments" is not a virtue, it's an
abomination!
 
R

Rick Johnson

[snip example showing dummy coder doing something dumb]

+1. This is what convinces me that keeping references to
keyword arguments is actually the right thing to do.

Perhaps I'm biased because I'm used to the mutable-
argument behavior by now, but the gotcha described above
seems to be much harder to track down and understand than
any bugs caused by mutable arguments. With mutable
arguments, at least you know the cause is in the function
definition. If you initialized variables each time, you'd
have to track the problem down across the entire
namespace.

That's because you and many other folks, who keep supporting
this BS idea, have become SO accustomed to writing code in a
sloppy manner, that you need this flaw to save yourself,
FROM YOURSELF!

" Hello, allow myself to introduce myself"

Apparently you don't understand the value of writing rock
solid code. Instead of passing your mutables in as
arguments, all you have to do is reference them properly in
the BODY of the subroutine and everything will work just
fine. If you fear global level variables then write up a
simple Object definition to encapsulate the mutable and make
it callable.

============================================================
Thought Exercise:
============================================================

Let's imagine for a second if Python allowed mutable keys in
a dictionary, would you be surprised if you used a mutable
for a key, then you mutated the mutable key, then you could
not access the value from the original key?

## Imaginary code ##
py> lst = [3]
py> d = {1:2, lst:4}
py> lst.append(10)
py> d[lst]
opps, KeyError!

Would you REALLY be so surprised? I would not. But more
importantly, does Python need to protect you from being such
an idiot? I don't think so!

Any "intelligent" programmer would NEVER use mutable keys
for a hash table -- unless he had a very good reason and was
willing to take the risks -- EVEN IF the language allowed
such foolishness!

But you people will sit here all day arguing that "early
binding" is warranted on the grounds that people need to be
protected from doing stupid things -- like passing mutables
as arguments to subroutines. This is a weak argument. Tell
me you have something better, tell me the fate of this
language does not revolve around benevolent idiocy!

If you really want to save the poor idiot from writing code
that could fail, a leader ruled by benevolence, but who has
a modicum of intelligence remaining, would simply throw up a
warning.

Warning:
Argument "lst" to function "poopoo" has been mutated,
which could cause a subtle bug on successive calls.
YOU HAVE BEEN WARNED!

What you never, EVER, want to do is to use idiots for an
excuse to create some esoteric abomination of persistent
mutables between successive calls of subroutines.
I, for one, would much rather follow the rule "don't use
mutable arguments in function definitions"

Yes, that's the exact point i'm making! A little self
discipline can go a long way
than "don't ever re-use the name of your variables."

Well, we're not even talking about that, but i understand
the value of your statement. Although, i think your
comparing Apples and Oranges.

PS: First you support the opposition. now you go an do a
180? You're confusing the hell out of me Matthew! @_@
 
S

Steven D'Aprano

Python functions are objects that take arguments, of which (the
arguments) are then converted to attributes of the function object.
Ah-Ha! Urm, but wait! We already have a method to define Objects. Heck,
I can even create my own callable objects if i want!

Observe:
py> class FuncAdd(object):
... def __init__(self, ivalue):
... self.ivalue = ivalue

Notice how you are storing state here? I thought you said, and I quote:

"When the subroutine is completed, ALL INPUTS and local variables are
expected to be destroyed. If the programmer wants a return value, he need
simply ask. Data persistence is NOT a function of subroutines!"
[emphasis added]

And yet here you are defining persistent input ("ivalue") to the callable
subroutine, which is stored as external state and not destroyed at the
end of the subroutine call:
... def __call__(self, numeric):
... return self.ivalue + numeric
...

All you have done here is define a subroutine with state, precisely the
thing that you say subroutines must never have. What you haven't done is
define a function which takes a default value that can be overridden when
called. Your argument is invalid.
 
S

Steven D'Aprano

On Thu, 20 Jun 2013 11:05:32 -0700, Rick Johnson wrote:
They [default argument values] have to be stored *somewhere*, and
Python chooses to store them as an attribute of the function object,
where they can be easily inspected, rather than by storing them inside
some undocumented and hidden location locked up in a binary blob.

I like how that last sentence implicitly argues against an argument i
never made, whilst explicitly supporting your argument.

You're overly defensive. I never said, or even suggested, that you argued
the opposite. I simply state a fact: any language with default arguments
must store the default argument somewhere. In a language with early
binding of default arguments, the *value itself* must be stored. In a
language with late binding, *the expression that generates that value*
(sometimes called a "thunk") must be stored.

The consequence of this fact is that if you wish to argue that functions
have no business storing state, then you're arguing that functions must
not have default arguments.


[...]
No, if your passing in a symbol, then the object it points to must exist
*somewhere*. OTOH if your passing in a literal, or an expression, then
the subroutine will need to "store" the resulting object. Yes.

Passing in a symbol? I'm afraid I don't understand you. Do you mean a
name? Something like:

def func(x=y):
...

A single name, like 'y' above, is an expression. In any case, while the
name must exist at the time the function is defined, it does not need to
exist when the function is called:

py> y = 23
py> def func(x=y):
.... return x
....
py> del y
py> func()
23

And that is *ONLY* the case using the currently broken system.

No. You have failed to understand. ANY language that offers default
values for function parameters must operate under the same limitation.
The language must store either the default value itself, or some
executable expression that generates that default value.

Even if your language doesn't offer default values, all this means is
that the responsibility for doing this is moved away from the compiler to
you. For instance, if your languages offers an "is_undefined" function,
you can manually store the default value in a global variable and then
retrieve it when needed:


# Store function state in a global variable.
default := [];

def func(x):
if is_undefined(x):
{
x := default;
}
...


That suffers the *exact same gotcha* as Python if the default value is
mutable.

Or you can store the state in a callable class, what C++ calls a functor:

class MyClass:
# Store function state in a class member.
default := [];
def __call__(this, x):
if is_undefined(x):
{
x := this.default;
}
...

This too suffers the *exact same gotcha* as Python if the default is
mutable. Storing the function state as an instance member instead of a
class member doesn't save you; the same applies.

There are only three ways to avoid the mutable default gotcha:

* Prohibit default arguments, and don't provide an is_undefined
test. All function arguments *must* be supplied explicitly by
the caller, like in Pascal.

* Use late-binding and re-calculate the default every time it is
needed. This risks being expensive, and has its own share of
gotchas.

* If you can somehow determine which values are mutable and which
are not, you might allow default arguments only for immutable
values. Some restrictive languages can do this: Java, with it's
strongly enforced rules for "private" and "protected" members,
and compile-time checks, may be able to do something like this.
And purely functional languages like Haskell simply avoid the
issue by ensuring that *all* values are immutable. But for
Python, it is impossible.
 
R

Rick Johnson

py> class FuncAdd(object):
... def __init__(self, ivalue):
... self.ivalue = ivalue
Notice how you are storing state here? I thought you said,
and I quote: "When the subroutine is completed, ALL INPUTS
and local variables are expected to be destroyed. If the
programmer wants a return value, he need simply ask. Data
persistence is NOT a function of subroutines!"
[emphasis added]

And yet here you are defining persistent input ("ivalue")
to the callable subroutine, which is stored as external
state and not destroyed at the end of the subroutine call:

Because Steven, my dear lad, what i created is NOT a
subroutine, no, it's an object that stores state and is
callable. Each call to the object is merely a request to
mutate and then return it's current state. A subroutine, at
least REAL subroutines (not the snake oil that Python is
selling) do not have state.

Real subroutines merely:
1. Optionally take inputs
2. Execute a body of code (which could have side effects,
but which is NOT persistent!)
3. Optionally return output

That's it. If your problem requires state persistence, then
you need to move up to an Object definition. Use the correct
tool for the job dammit!
All you have done here is define a subroutine with state,
precisely the thing that you say subroutines must never
have.

It's Okay to carry state EXPLICITLY, it's NOT okay to carry
state IMPLICITLY. My implementation is correct, Python's is
not.

Besides, encapsulated state is one of the fundamental
principles of OOP programming, along with interfaces; and
i've satisfied both! The mutable is protected from the world
by an object, and the object allows manipulation of the
mutable via an interface.

But unlike Python's limited implementation, my approach is
scalable. I can change the interface, i can add more
functionality, i can do anything i want.

"I am the Lizard King, and i can do, well ANYTHING!"

However, with Python's limited approach, i'm just a slave to
the implementation. I'm forced to follow estoeric rules with
no chance of scalability.

########################################################
# Moral of the Day #
########################################################
# You should only use SUBROUTINES for executing #
# subprograms requiring stateless transactions. #
# Alternativly, subprograms requiring persistant state #
# transactions should wield the power of a custom #
# object defintion -- or you can use the #
# global+subroutine method if encapsulation is not #
# warrented. #
########################################################

But in any case, following this advice will ensure less bugs
and more maintainable code than Python's current
implementation of lunacy.
What you haven't done is define a function which takes a
default value that can be overridden when called.

Oh, you mean like this?

py> class Func(object):
... def __call__(self, arg=0):
... print(arg)
...
py> f = Func()
py> f("pwned")
pwned
Your argument is invalid.

My argument is not invalid, i just figured i did not need
wipe your bum for you, "Homer". ~(_8^(I)
 
R

Rotwang


It isn't clear to me from your posts what exactly you're proposing as an
alternative to the way Python's default argument binding works. In your
version of Python, what exactly would happen when I passed a mutable
argument as a default value in a def statement? E.g. this:
a = [1, 2, 3]
a.append(a)
b = object()
def f(x = [None, b, [a, [4]]]):
.... pass # do something

What would you like to see the interpreter do in this case?
 
R

Rick Johnson

It isn't clear to me from your posts what exactly you're
proposing as an alternative to the way Python's default
argument binding works. In your version of Python, what
exactly would happen when I passed a mutable argument as a
default value in a def statement? E.g. this:
a = [1, 2, 3]
a.append(a)
b = object()
def f(x = [None, b, [a, [4]]]):
... pass # do something

What would you like to see the interpreter do in this case?

Ignoring that this is a completely contrived example that has
no use in the real world, here are one of three methods by
which i can handle this:

============================================================
The Benevolent Approach:
============================================================
I could cast a "virtual net" over my poor lemmings before
they jump off the cliff by throwing an exception:

Traceback (most recent screw-up last):
Line BLAH in SCRIPT
def f(x = [None, b, [a, [4]]]):
ArgumentError: No mutable default arguments allowed!

============================================================
The Apathetic Approach:
============================================================
I could just assume that a programmer is responsible for the
code he writes. If he passes mutables into a function as
default arguments, and then mutates the mutable later, too
bad, he'll understand the value of writing solid code after
a few trips to exception Hell.

============================================================
The Malevolent Approach (disguised as beneva-loon-icy):
============================================================
I could use early binding to confuse the hell out of him and
enjoy the laughs with all my ivory tower buddies as he falls
into fits of confusion and rage. Then enjoy again when he
reads the docs. Ahh, the gift that just keeps on giving!

============================================================
Conclusion:
============================================================
As you can probably guess the malevolent approach has some
nice fringe benefits.

You know, out of all these post, not one of you guys has
presented a valid use-case that will give validity to the
existence of this PyWart -- at least not one that CANNOT be
reproduced by using my fine examples. All you can muster is
some weak argument about protecting the lemmings.

Is anyone up the challenge?
Does anyone here have any real chops?

PS: I won't be holding my breath.
 
S

Steven D'Aprano

You know, out of all these post, not one of you guys has presented a
valid use-case that will give validity to the existence of this PyWart

LOL.

Okay, you're trolling. Time for another month in the kill-file.

*plonk*
 
C

Chris Angelico

I could cast a "virtual net" over my poor lemmings before
they jump off the cliff by throwing an exception:

Traceback (most recent screw-up last):
Line BLAH in SCRIPT
def f(x = [None, b, [a, [4]]]):
ArgumentError: No mutable default arguments allowed!

So tell me, oh Great and Powerful Wizard of Rick, how is the
interpreter supposed to know which defaults are mutable? I mean, it's
obviously some intrinsic property of the object. Somehow one thing is
clearly immutable, another thing clearly isn't. Will there be a
PyObject_IsImmutable() API?

Oh! I know. Function argument defaults will now be restricted to
int/float/tuple. That would do it, right? Nobody would be bothered by
little restrictions like that, would they.

ChrisA
 

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,137
Messages
2,570,795
Members
47,342
Latest member
eixataze

Latest Threads

Top