473,785 Members | 2,209 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

mutable default parameter problem [Prothon]

As we are addressing the "warts" in Python to be fixed in Prothon, we have
come upon the
mutable default parameter problem. For those unfamiliar with the problem,
it can be seen in this Prothon code sample where newbies expect the two
function calls below to both print [ 1 ] :

def f( list=[ ] ):
print list.append!(1)

f() # prints [ 1 ]
f() # prints [ 1, 1 ]

It is more than just a newbie problem. Even experts find themselves having
to do things like this which is a waste of programming effort:

def f( list = None ):
if list == None: list = [ ]

We have three proposals in the Prothon mailing list right now to fix this.
I'd like to bounce these off of the Python list also since this will
possibly make a big difference in Python code ported over to Prothon and we
can always use more advice.

1) Only allow immutable objects as default values for formal parameters. In
Prothon immutable objects are well-defined since they have an immutable flag
that write-protects them. This (as the other solutions below) would only
solve the problem in a shallow way as one could still have something like a
tuple of mutable objects and see the problem at a deeper level. If the
newbie is going to be dealing with something this complex though then they
are dealing with the overall problem of references versus copies and that is
a bigger overall issue.

2) Evaluate the default expression once at each call time when the default
value is needed. The default expression would be evaluated in the context
of the function definition (like a closure).

3) Evaluate the expression at definition time as it is done now, but at call
time do a defaultValue.co py() operation. This would be a shallow copy so
again it would be a shallow solution.

Choice 2 is my favorite in that it matches the dynamic nature of Prothon,
but it is an expensive solution. Choice 1 is the least expensive solution
but it is limiting to the user. Choice 1 does not help the second code
sample above. Choice 3 is a good compromise since an object.copy() is
pretty fast in Prothon.

Comments? How much Python code would these different proposals break?
Jul 18 '05
49 2629

Nice !
I wish Python had this, too.

Also, re-evaluating each time will allow one to use a global variable
whose value could change as a default parameter. Which looke suspicious.
Or something like that, which is GOOD :

class youpi( object ):
def mymethod( self, a=self.a, b=self.b ):
...

Pierre-Frédéric Caillaud wrote:
2) Evaluate the default expression once at each call time when the
default
value is needed. The default expression would be evaluated in the
context
of the function definition (like a closure).

I like Choice 2 because I've always wanted to do the following :

def func( x, y=2*x ):


It looks like you will get your wish. The voting has been pretty much
unanimous for option 2.


--
Using Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 18 '05 #31
Rob Williscroft <rt*@freenet.co .uk> wrote:

But python has static variables.

def another( x ):
y = getattr( another, 'static', 10 )
another.static = x
return y

print another(1), another(2), another(4)


What about the following case:

def f():
f.static = getattr(f, 'static', 0)
f.static += 1
return f.static

print f(), f(), f() # prints 1 2 3

As opposed to C++, you now have a line of code that is always executed
in subsequent calls, for no good reason. This is worse than:

def f(static=[0]):
static[0] += 1
return static[0]

in the sense that you have a wasteful call ("getattr") that doesn't do
anything productive in subsequent calls. (You could change that to
f.static = getattr(f, 'static', 0) + 1, but the "getattr" is surely
inefficient compared to f.static += 1, and internally likely incurs
some conditional statement at some level.)

Maybe one can do instead:

def f():
global f
def f():
f.static += 1
return f.static
f.static = 0 # initialization
return f()

print f(), f(), f() # prints 1 2 3

The advantage is there is no more wasteful statements in subsequent
calls. No "if" conditional statement. The above is of course a toy
example to illustrate the case of a function that needs to perform
something special the first time it is called. (I am well aware of the
outside assignment like:

def f():
f.static += 1
return f.static
f.static = 0

mentioned in this thread, but I am talking about something more
general. Notice that in the latter case, f.static=0 is done before f()
is called, which may not be what one wants. E.g.: if f() is never
called, this assignment is wasteful. Not a problem in this case, for
if complicated initialization is needed, like requiring a timestamp,
it may not be a good idea.)

In code refactoring, the equivalent is to replace conditional
statements by polymorphism. In terms of codeblocks, what I mean is
dynamic hook-on and hook-off of codeblocks. If the underlying language
is powerful enough, one should be able to achieve runtime
restructuring of code, without performance impact for subsequent
calls.

regards,

Hung Jung
Jul 18 '05 #32
Hung Jung Lu wrote:
In code refactoring, the equivalent is to replace conditional
statements by polymorphism. In terms of codeblocks, what I mean is
dynamic hook-on and hook-off of codeblocks. If the underlying language
is powerful enough, one should be able to achieve runtime
restructuring of code, without performance impact for subsequent
calls.


What about this Prothon code:

def f():
if 'static' not in f.attrs_:
f.static = 0
f.static += 1

tmp = f.static
def outer.f():
f.static += 1
return f.static
f.static = tmp

return f.static

print f() # 1
print f() # 2
print f() # 3

I'm sure Python can do this better so please don't barrage me with replies
saying so. I'm not going to brag this time :-)
Jul 18 '05 #33
Mark Hahn wrote:
I also said I was
sorry in my very first posting. If you want me to repeat this a third time
I will.


It's likely that Christos' Usenet link (if that's how he gets
this group) is much slower than yours or mine... he may not
have received (or at least read) either of the other replies
by the time he sent his own...

Or, he might just want you to repeat it a third time. ;-)

-Peter
Jul 18 '05 #34
Hung Jung Lu wrote in news:8e******** *************** ***@posting.goo gle.com
in comp.lang.pytho n:
Rob Williscroft <rt*@freenet.co .uk> wrote:

But python has static variables.

def another( x ):
y = getattr( another, 'static', 10 )
another.static = x
return y

print another(1), another(2), another(4)
What about the following case:

def f():
f.static = getattr(f, 'static', 0)
f.static += 1
return f.static

print f(), f(), f() # prints 1 2 3


Yep this would be the way I would instinctivly write it. However
Andrea suggested:

def f():
if not hasattr( f, 'static', 0 ):
f.static = 0
f.static += 1
return f.static

Which my primitive timing tests show to be faster.

As opposed to C++, you now have a line of code that is always executed
in subsequent calls, for no good reason. This is worse than:
Well C++ has `if ( __compiler_gene rated_bool__ )` that will always
be executed ( except in the case of POD's (char, int, double etc) ).

def f(static=[0]):
static[0] += 1
return static[0]

I don't know but surely the interpreter is doing some kind of
extra if/attribute lookup in there, its just faster than the
other versions.

I've timed both, and this was by far the fastest. I don't think
the problems is the `if` though. I don't know but I suspect this
version finds the local/paramiter 'static' with __slots__ like
performance, all other version's suffer from attribute lookup
problems.

in the sense that you have a wasteful call ("getattr") that doesn't do
anything productive in subsequent calls. (You could change that to
f.static = getattr(f, 'static', 0) + 1, but the "getattr" is surely
inefficient compared to f.static += 1, and internally likely incurs
some conditional statement at some level.)

Maybe one can do instead:

def f():
global f
def f():
f.static += 1
return f.static
f.static = 0 # initialization
return f()

print f(), f(), f() # prints 1 2 3

The advantage is there is no more wasteful statements in subsequent
calls. No "if" conditional statement.
Intresting, but it looses (in my timing tests) to the f(static=[0])
version, static[0] must be faster than f.static I guess.
The above is of course a toy
example to illustrate the case of a function that needs to perform
something special the first time it is called. (I am well aware of the
outside assignment like:

def f():
f.static += 1
return f.static
f.static = 0

mentioned in this thread, but I am talking about something more
general. Notice that in the latter case, f.static=0 is done before f()
is called, which may not be what one wants. E.g.: if f() is never
called, this assignment is wasteful. Not a problem in this case, for
if complicated initialization is needed, like requiring a timestamp,
it may not be a good idea.)
Indeed, the fast version is:

_f_static = 0
def f():
global _f_static
_f_static += 1
return _f_static

This is directly equivalent to
<c++>
static int f_static = 0;
int f() { return ++f_static; }
</c++>

C++ name hiding is with the static keyword, python name hiding
is with a leading underscore.

In code refactoring, the equivalent is to replace conditional
statements by polymorphism. In terms of codeblocks, what I mean is
dynamic hook-on and hook-off of codeblocks. If the underlying language
is powerful enough, one should be able to achieve runtime
restructuring of code, without performance impact for subsequent
calls.


Nice.

<c++>
static int f_static;
static int f_init();
static int f_run();
int (*f)() = f_init();

static int f_init()
{
f_static = /* dynamic value */ 0;
f = f_run;
return f();
}
static int f_run()
{
return ++f_static;
}
</c++>

In C++ the (performance) cost is visible and only f() and its callers
pay for it, in Python the the (performance) cost is invisible and
everybody pays for it.

In C++ I write:

int f()
{
static int var = 0;
return ++var;
}

And I let the compiler worry about the best way to implement it,
in Python I write:

class f( object ):
def __init__( self ):
self.var = 0;
def run( self ):
self.var += 1
return self.var

Though in a simple case (as all the examples have been) I
might write:

def f():
if not hasattr( f, 'static' ):
f.static = 0
f.static += 1
return f.static

Clarity (my clairty of purpose, as a programmer) wins in
both languages.

I won't be writing:

def f(static=[0]):
#etc

It simply isn't clear.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 18 '05 #35
On Fri, 18 Jun 2004 20:38:51 -0400, rumours say that Peter Hansen
<pe***@engcorp. com> might have written:
I also said I was
sorry in my very first posting. If you want me to repeat this a third time
I will.


It's likely that Christos' Usenet link (if that's how he gets
this group) is much slower than yours or mine... he may not
have received (or at least read) either of the other replies
by the time he sent his own...

Or, he might just want you to repeat it a third time. ;-)


I am actually reading through Usenet... the newsserver link is fast, but
the arrival order of posts is not guaranteed, so I don't read messages
in the same order as people using the mail list. Often I post a reply
to a single message, only to find in the next synchronisation that
others did reply even earlier than me saying more or less the same
things. Other times I don't bother, saying "somebody else will reply to
this trivial question", and next day I find out that everybody else
thought the same as I did. That's Usenet (sigh).
--
TZOTZIOY, I speak England very best,
"Tssss!" --Brad Pitt as Achilles in unprecedented Ancient Greek
Jul 18 '05 #36

"Dave Brueck" <da**@pythonapo crypha.com> wrote
FYI: It's not that the exclamation mark causes append to return the
sequence. The exclamation mark is always there and the sequence is always returned. The exclamation mark is the universal symbol for in-place
modification. This is straight from Ruby and solves the problem that caused
Guido to not allow sequences to be returned. And, yes, I do think that's worth bragging about ;-)


Wait, so is the exclamation point required or not? IOW, say you have a

class like this:

class List(list):
def append(self, what):
list.append(sel f, what)
return self

a = List()
b = a.append(5)

So in the call to append, is it a.append!(5) or just a.append(5) ? If it's
the former, then does the compiler detect that it's required because the
function returns 'self' or is the determining factor something else?

Or, does the append method not really return anything, and the language
takes care of substituting in the object? (in which case, does that mean you can override the return value by adding '!' - such that z=foo.bar!() ignores the return value of bar and z references foo?)
As I said above: It's not that the exclamation mark that causes append to
return the sequence. The exclamation mark is always there and the sequence
is always returned. In Prothon (and Ruby and other languages before) the
exclamation mark is just part of the method name and is there to warn you
that in-place modification is happening.
Personally, I don't like the modify-in-place-and-return-the-object
'feature' - it's not needed _that_ often, but more importantly, it makes the code harder to read (to me at least).


If you use the Prothon append!() exactly as you use the Python append() you
will get the exact same results. This is just an extra feature for those
that want it.

Guido avoided returning values from in-place modification functions because
of the confusion as to whether in-place mods were happening or not. We have
solved that confusion with the exclamation mark. Our code is very readable
because of this.


Jul 18 '05 #37
Christos TZOTZIOY Georgiou wrote:
Or, he might just want you to repeat it a third time. ;-)


I am actually reading through Usenet... the newsserver link is fast,
but the arrival order of posts is not guaranteed, so I don't read
messages in the same order as people using the mail list. Often I
post a reply to a single message, only to find in the next
synchronisation that others did reply even earlier than me saying
more or less the same things. Other times I don't bother, saying
"somebody else will reply to this trivial question", and next day I
find out that everybody else thought the same as I did. That's
Usenet (sigh).


I'm sorry if I was rude. It is always when I make a stupid remark on a
public forum that I am asked to repeat it :-) No one ever asks me to repeat
a witty gem. (Of course maybe that never occurs).
Jul 18 '05 #38
On Thu, 24 Jun 2004 17:06:33 -0700, rumours say that "Mark Hahn"
<ma**@prothon.o rg> might have written:
No one ever asks me to repeat
a witty gem.


You can say that again :)
--
TZOTZIOY, I speak England very best,
"Tssss!" --Brad Pitt as Achilles in unprecedented Ancient Greek
Jul 18 '05 #39
Mark wrote:
Wait, so is the exclamation point required or not? IOW, say you have a class
like this:

class List(list):
def append(self, what):
list.append(sel f, what)
return self

a = List()
b = a.append(5)

So in the call to append, is it a.append!(5) or just a.append(5) ? If it's the former, then does the compiler detect that it's required because the
function returns 'self' or is the determining factor something else?

Or, does the append method not really return anything, and the language
takes care of substituting in the object? (in which case, does that mean

you
can override the return value by adding '!' - such that z=foo.bar!()

ignores
the return value of bar and z references foo?)


As I said above: It's not that the exclamation mark that causes append to
return the sequence. The exclamation mark is always there and the

sequence is always returned. In Prothon (and Ruby and other languages before) the
exclamation mark is just part of the method name and is there to warn you
that in-place modification is happening.
Ahh...so the method name is just _spelled_ with an exclamation point? IOW,
the ! is a token you can use at the end of an identifier, but it is not
actually used by the language itself - it's some sort of pseudo-syntax? I
think I understand now. But is it truly part of the name in that you are
required to include the ! when calling the method? (I'm still thinking of
the confusion I'd experience with something like w = x.y.z!() )

So if I want a reference to one of those methods I could end up doing

ref = obj.method! or ref! = obj.method!

and the program runs the same either way, it's just that in one case the
code is misleading?

If it's up to the programmer to remember to add it (meaning that it doesn't
cause an error to forget to use it), and if is really just part of the name,
then it's just a naming convention, right? Wouldn't you get the same result
by establishing the convention that e.g. method names ending in a single
underscore signify in-place modification (foo.append_() ) ? Seems like a
waste to reserve a symbol for something so rarely needed.
Personally, I don't like the modify-in-place-and-return-the-object
'feature' - it's not needed _that_ often, but more importantly, it makes

the
code harder to read (to me at least).


If you use the Prothon append!() exactly as you use the Python append()

you will get the exact same results. This is just an extra feature for those
that want it.

Guido avoided returning values from in-place modification functions because of the confusion as to whether in-place mods were happening or not. We have solved that confusion with the exclamation mark. Our code is very readable because of this.


Clearly, readability is in the eye of the beholder. :)

-Dave
Jul 18 '05 #40

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

35
2253
by: bukzor | last post by:
I've found some bizzare behavior when using mutable values (lists, dicts, etc) as the default argument of a function. I want to get the community's feedback on this. It's easiest to explain with code. This example is trivial and has design issues, but it demonstrates a problem I've seen in production systems: def main(argv = ): 'print out arguments with BEGIN and END' argv.insert(1, "BEGIN")
0
9646
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
10346
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
8982
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7504
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6742
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5386
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5514
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
2
3658
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2887
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.