473,386 Members | 1,766 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

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.copy() 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 #1
49 2548
On Tue, 15 Jun 2004 15:07:05 -0700, Mark Hahn wrote:
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?


I like 2 the most. Well, actually I like only 2 :-)

I'm not sure why it would be expensive, it's a pity if it's expensive,
but it should be appropriate for most cases and it's easy to understand.

--
__("< Marcin Kowalczyk
\__/ qr****@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

Jul 18 '05 #2
On Tuesday 15 June 2004 02:07 pm, Mark Hahn wrote:
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 ] : 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.
Here's an idea: if it ain't broke, don't fix it.

Seriously, you see a "wart" and a "problem". I see a pleasant side-effect of
the documented semantics. True, new folks are surprised by the behavior, but
once it's understood, it becomes more powerful.

How do you intend to account for code like this:

def F(a, b, cache={}):
try:
return cache[(a,b)]
except (IndexError, ):
value = cache[(a,b)] = do_some_long_calc(a,b)
return value

Or even this:

shared_cache = {}

def F(a, b, cache=shared_cache):
...

Of course you can argue that this is bad style, but the counter argument is
just as strong: this is quite pythonic and quite readable.

Python is a tool, and you decrease the utility of that tool when you limit
it's idioms.
How much Python code would these different proposals break?


A lot. I ran this:

$ find /usr/lib/python2.3/ -name "*.py" -exec grep "def.*=\[\]" {} \; | wc

And see 67 instances just in the standard library. Multiply that by a factor
of 1000, 10000 or more to reflect code in the field, and you might start to
understand the significance of changing the language definition.

--
Troy Melhase, tr**@gci.net
--
I have sworn upon the altar of God eternal hostility against every form of
tyranny over the mind of man. - Thomas Jefferson
Jul 18 '05 #3
On Tue, 15 Jun 2004 22:01:19 -0800, Troy Melhase <tr**@gci.net> wrote:
Seriously, you see a "wart" and a "problem". I see a pleasant side-effect of
the documented semantics. True, new folks are surprised by the behavior, but
once it's understood, it becomes more powerful.

How do you intend to account for code like this:

def F(a, b, cache={}):
try:
return cache[(a,b)]
except (IndexError, ):
value = cache[(a,b)] = do_some_long_calc(a,b)
return value
I'm new to python. To my eyes this is a pretty poor attempt to
have static variables. I've implemented in the past a few
scripting languages, and it's not really difficult to
implement static variables... it's quite surprising for me
there's no such a concept in python and just that wart...
hmmm... excuse me... that bad smelling wart has to be used instead.
Or even this:

shared_cache = {}

def F(a, b, cache=shared_cache):
...
A global you mean ? Why not just saying "global shared_cache"
at the start of the function ?

If you need more power than a global then you probably a
(callable) class is going to serve you better than a function.

Is really a feature that if someone passes (by mistake) an extra
parameter to the function the script silently swallows it and
behaves strangely ? Or you also double-wrap the function, so
that a(x,y) calls real_a(x,y,cache=[]) ?
Of course you can argue that this is bad style, but the counter argument is
just as strong: this is quite pythonic and quite readable.
Pythonic ? In the sense that this is for example more explicit ?
Are you kidding ?
Python is a tool, and you decrease the utility of that tool when you limit
it's idioms.
How much Python code would these different proposals break?
A lot. I ran this:


This doesn't surprise me. Static variables are useful when you
don't really need the power of a class instance. Too bad that
them are missing in python, and for unknown (to me) reasons
they haven't been added in the evolution of the language.
$ find /usr/lib/python2.3/ -name "*.py" -exec grep "def.*=\[\]" {} \; | wc

And see 67 instances just in the standard library. Multiply that by a factor
of 1000, 10000 or more to reflect code in the field, and you might start to
understand the significance of changing the language definition.


That something is used is one thing. That something is elegant
is another.

Andrea
Jul 18 '05 #4
Troy Melhase wrote:
Here's an idea: if it ain't broke, don't fix it.

Seriously, you see a "wart" and a "problem". I see a pleasant
side-effect of the documented semantics. True, new folks are
surprised by the behavior, but once it's understood, it becomes more
powerful.
All four of the Python gotcha's, wart's and regrets lists I have found
included this problem. It is not only a newbie's problem as I showed in my
posting.
How do you intend to account for code like this:

def F(a, b, cache={}):
try:
return cache[(a,b)]
except (IndexError, ):
value = cache[(a,b)] = do_some_long_calc(a,b)
return value

Or even this:

shared_cache = {}

def F(a, b, cache=shared_cache):
...
The first example is very unreadable and uncool in general. Your second
example will work just fine with our fix.
Of course you can argue that this is bad style,
Yes I (and many others) will.
but the counter
argument is just as strong: this is quite pythonic and quite
readable.
I disagree strongly. I would never be caught coding something like that and
I love Python dearly.
Python is a tool, and you decrease the utility of that tool when you
limit it's idioms.


So far you have only shown me an idiom that many say should not be used.
Show me one that everyone agrees is useful.
How much Python code would these different proposals break?


A lot. I ran this:

$ find /usr/lib/python2.3/ -name "*.py" -exec grep "def.*=\[\]" {}
\; | wc

And see 67 instances just in the standard library. Multiply that by
a factor of 1000, 10000 or more to reflect code in the field, and you
might start to understand the significance of changing the language
definition.


That count is not accurate. Fixing this will not break every use of [] as a
default formal param. Using [] in __init__ for example would break nothing.
I can think of many other cases where it is legal to use []. The only case
I can think of that would break would be the idiom we disagree on above. If
I am wrong, then show me other cases.

If I also might make a general argument for the fix then let me continue.
Doing a late evaluation of the default expression makes the language more
dynamic, which fits the overall goal of making Prothon more dynamic. Using
prototypes instead of classes, dynamic var scoping, this fix, and many other
Prothon changes from Python all work towards that goal.

Dynamic var scoping fixed another Python gotcha which doesn't break
anything. Here are the two versions of code showing the problem and the
fix:

--- Python ---
x = 1
def f(): .... x = x + 1
.... print x
.... f()

UnboundLocalError: local variable 'x' referenced before assignment

--- Prothon ---

O>> x = 1
1
O>> def f():
.... x = x + 1
.... print x
....
O>> f()
2

Prothon's scoping rules are dynamic which means that x comes from outside
the function until the actual assignment happens. At that point x becomes a
local variable. This, along with the fact that vars are inherited from
ancestors along with methods, allow for some intuitive and simple var
initialization techniques.

Obviously it is the responsibility of the programmer to make sure that the
outer x has the proper initialization value for the local x. This can cause
a hiding-of-uninitialized-vars bug if the programmer uses the same names for
unrelated variables but it is worth the extra power and intuitiveness.


Jul 18 '05 #5
Andrea Griffini wrote in news:dq********************************@4ax.com
in comp.lang.python:
def F(a, b, cache={}):
try:
return cache[(a,b)]
except (IndexError, ):
value = cache[(a,b)] = do_some_long_calc(a,b)
return value
I'm new to python. To my eyes this is a pretty poor attempt to
have static variables. I've implemented in the past a few
scripting languages, and it's not really difficult to
implement static variables...


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)

it's quite surprising for me
there's no such a concept in python and just that wart...
hmmm... excuse me... that bad smelling wart has to be used instead.


It seems to me in python "everything is an object" leads to
"everything is a dictionary" (except when it isn't:).

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 18 '05 #6
Mark Hahn wrote:
Fixing this will not break every use of
[] as a default formal param. Using [] in __init__ for example would
break nothing.


Correction: Using [] as a default param is the same problem in __init__ as
it is anywhere else. My brain went south for a moment.

My point still stands though. There are many cases where a formal param can
default to [] and can be evaluated early or late without breaking the code.

My question asking for comments on code breakage also still stands. Does
anyone have personal experience with usages of [] or {} as default params
that would break with late evaluation? Is there any common idiom other than
the non-recommended use of them as static vars that would break?


Jul 18 '05 #7
On Tuesday 15 June 2004 10:52 pm, Mark Hahn wrote:
Troy Melhase wrote:
Here's an idea: if it ain't broke, don't fix it.

Seriously, you see a "wart" and a "problem". I see a pleasant
side-effect of the documented semantics. True, new folks are
surprised by the behavior, but once it's understood, it becomes more
powerful.
All four of the Python gotcha's, wart's and regrets lists I have found
included this problem. It is not only a newbie's problem as I showed in my
posting.


You're right, it's not a newbie problem. It's a problem for everyone who
hasn't bothered to read the documentation.
How do you intend to account for code like this:

def F(a, b, cache={}):
try:
return cache[(a,b)]
except (IndexError, ):
value = cache[(a,b)] = do_some_long_calc(a,b)
return value

Or even this:

shared_cache = {}

def F(a, b, cache=shared_cache):
...


The first example is very unreadable and uncool in general. Your second
example will work just fine with our fix.


Uncool? Do you mean "uncool" as in "forking a language and distracting a
bunch of folks because I don't like its otherwise hard-earned design
decisions" or "uncool" as in "I don't know how to otherwise express my
thoughts and therefore will assign to them some magnificently subjective
expression"?
I disagree strongly. I would never be caught coding something like that
and I love Python dearly.
Then you are limiting yourself to a subset of something wonderful.

(And by the way, one definition of love means to accept what we perceive as
deficiencies. So maybe you don't love Python as dearly as you love the idea
of Python.)
Python is a tool, and you decrease the utility of that tool when you
limit it's idioms.


So far you have only shown me an idiom that many say should not be used.
Show me one that everyone agrees is useful.


If you're goal is universal acceptance, you should stop now.
And see 67 instances just in the standard library. Multiply that by
a factor of 1000, 10000 or more to reflect code in the field, and you
might start to understand the significance of changing the language
definition.


That count is not accurate. Fixing this will not break every use of [] as
a default formal param. Using [] in __init__ for example would break
nothing. I can think of many other cases where it is legal to use []. The
only case I can think of that would break would be the idiom we disagree on
above. If I am wrong, then show me other cases.


Oh, but it will. You'll have to read and comprehend every function definition
that uses mutable default arguments to start to prove otherwise.
If I also might make a general argument for the fix then let me continue.
Doing a late evaluation of the default expression makes the language more
dynamic, which fits the overall goal of making Prothon more dynamic. Using
prototypes instead of classes, dynamic var scoping, this fix, and many
other Prothon changes from Python all work towards that goal.

Dynamic var scoping fixed another Python gotcha which doesn't break
anything. Here are the two versions of code showing the problem and the
fix:


[snip]

Maybe you should take a step back and look at what you're doing. From my
perspective, you're adding a whole lot of additional rules to the language,
and a completely different way of doing things. That's fine, and more power
to you, but if you're bent on changing so much, you should stop looking to
c.l.p to validate your ideas.

(Of course, I don't speak for the Python community or c.l.p, but I am
horrified nonetheless with what you're doing. Please forgive me if I've been
disagreeable while disagreeing.)

--
Troy Melhase, tr**@gci.net
--
It is terrible to contemplete how few politicians are hanged. - G. K.
Chesterton
Jul 18 '05 #8
On Tuesday 15 June 2004 10:53 pm, Andrea Griffini wrote:
On Tue, 15 Jun 2004 22:01:19 -0800, Troy Melhase <tr**@gci.net> wrote:
Or even this:

shared_cache = {}

def F(a, b, cache=shared_cache):
...
A global you mean ? Why not just saying "global shared_cache"
at the start of the function ?


The shared_cache dictionary is mutable, making the global statement redundant
at best. It could have just as easily been left out of the function argument
list.

The definition above would also allow clients of the function to pass in a
cache of their choosing. (Granting, of course, that this would most likely
be an obscure use.)
If you need more power than a global then you probably a
(callable) class is going to serve you better than a function.
Of course. I was trying to illustrate using a named module-level dict instead
of a mutable value that could not be accessed outside of the function body.
Is really a feature that if someone passes (by mistake) an extra
parameter to the function the script silently swallows it and
behaves strangely ? Or you also double-wrap the function, so
that a(x,y) calls real_a(x,y,cache=[]) ?
How many times have you passed an extra parameter by mistake? This is Python,
and one of the mottos is "we're all consenting adults".
Of course you can argue that this is bad style, but the counter argument
is just as strong: this is quite pythonic and quite readable.


Pythonic ? In the sense that this is for example more explicit ?
Are you kidding ?


No, I'm not. The function specified a muteable argument. Nothing implicit
about it.
That something is used is one thing. That something is elegant
is another.


Of course. But using a mutable default function parameter as a cache is
elegant as far as I'm concerned.
--
Troy Melhase, tr**@gci.net
--
I have sworn upon the altar of God eternal hostility against every form of
tyranny over the mind of man. - Thomas Jefferson
Jul 18 '05 #9
Troy Melhase wrote:
You're right, it's not a newbie problem. It's a problem for everyone
who hasn't bothered to read the documentation.
Having a language with features intuitive enough to require less trips to
the documention is a laudable goal. Of course you know what I really meant
was what I said in the original posting. Even experts have to write extra
code to get around this problem The following is a common piece of Python
code that is a problem work-around:

def f( a = None ):
if a == None: a = []
Uncool? Do you mean "uncool" as in "forking a language and
distracting a bunch of folks ...
Evolution works by forking. If you have some problem with Prothon then
let's discuss it. Don't hide your problem with Prothon behind your
discussion of this thread's topic.
(And by the way, one definition of love means to accept what we
perceive as deficiencies. So maybe you don't love Python as dearly
as you love the idea of Python.)
You can love something and still want to improve it. Don't tell my wife I
said this :-)
That count is not accurate. Fixing this will not break every use of
[] as a default formal param. <correction removed>. I can think of many other cases where it is legal to use []. The only case I can think of that would break would be the idiom we disagree on above. If I am wrong, then show
me other cases.


Oh, but it will. You'll have to read and comprehend every function
definition that uses mutable default arguments to start to prove
otherwise.


I'm not sure I follow you. I'm saying that you only have to show me one or
two cases for me to realize I'm wrong.
Maybe you should take a step back and look at what you're doing.
From my perspective, you're adding a whole lot of additional rules to
the language, and a completely different way of doing things. That's
fine, and more power to you, but if you're bent on changing so much,
you should stop looking to c.l.p to validate your ideas.
I'm not looking for validation, just a reasonable discussion of the issue of
which of the three methods to use to fix the problem. You are the one that
started the seperate discussion as to whether it should be fixed or not.

By the way, Prothon is removing a lot of rules, not adding them, by it's
simplification in almost all areas.
(Of course, I don't speak for the Python community or c.l.p, but I am
horrified nonetheless with what you're doing. Please forgive me if
I've been disagreeable while disagreeing.)


No problem. I am quite thick-skinned. I also apologize if I have been
harsh.

I am sorry you are so upset that someone is daring to make a changed Python.
I expected this reaction from many Python afficionados who may love the
warts as much as the beauty. I was surprised that Prothon was received as
warmly as it was here at c.l.p. All you have to do is ignore any postings
with subjects that end in [Prothon].


Jul 18 '05 #10
On Tue, 15 Jun 2004 22:01:19 -0800, Troy Melhase wrote:
Or even this:

shared_cache = {}

def F(a, b, cache=shared_cache):
...


This would work in the 2nd scheme.

--
__("< Marcin Kowalczyk
\__/ qr****@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

Jul 18 '05 #11
On Tue, 15 Jun 2004 22:01:19 -0800, Troy Melhase wrote:
$ find /usr/lib/python2.3/ -name "*.py" -exec grep "def.*=\[\]" {} \; | wc

And see 67 instances just in the standard library.


I don't know how you counted them:

[qrczak ~/src/python/Python-2.3.4]$ egrep 'def.*= ?\[\]' **/*.py | wc -l
45
[qrczak ~/src/python/Python-2.3.4]$ egrep 'def.*= ?None' **/*.py | wc -l
1420

Now consider that many of the Nones are a workaround for the current
Python behavior.

I agree that it's probably impractical to change Python rules because
some code relies on the current behavior. OTOH evaluating the default
argument each time when the function is applied is technically better.
This is one of warts which is hard to fix because of compatibility.

--
__("< Marcin Kowalczyk
\__/ qr****@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

Jul 18 '05 #12
On 16 Jun 2004 07:52:04 GMT, Rob Williscroft <rt*@freenet.co.uk>
wrote:
But python has static variables.
That's what I mean with "I'm new to python" :-)
def another( x ):
y = getattr( another, 'static', 10 )
another.static = x
return y

print another(1), another(2), another(4)
I like more as an example:
def foo(x): ... if not hasattr(foo,'list'):
... foo.list = []
... foo.list.append(x)
... print foo.list
... foo(1) [1] foo(2) [1, 2] foo(3) [1, 2, 3]


In C++ you get the "if" part for free, but the python
approach is still goodlooking enough.

C++:

static int y = 12;

Python:

if not hasattr(foo,'y'):
foo.y = 12

The python "static" also worked as I expected when
they're inside locally defined functions returned as
callable objects.
It seems to me in python "everything is an object" leads to
"everything is a dictionary" (except when it isn't:).


This language looks better every day :-) ...

But if there are those better-looking statics, why so
much use of that modifiable-default-of-a-fake-parameter
ugly trick ? Is this something that became legal
only recently ?

I found the description of that 'wart' and the corresponding
'trick' it in some doc about python (don't remember which
one), and even started using it myself! shame on me!

Now that I see this other approach, the function object
attributes look way better IMO...
Why pushing the uglyness instead of the beauty ?

Historical reasons may be ? Those are behind a lot of
C++ horrible parts...
Andrea
Jul 18 '05 #13
Andrea Griffini wrote:
That's what I mean with "I'm new to python" :-)
def another( x ):
y = getattr( another, 'static', 10 )
another.static = x
return y

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


I like more as an example:
>>> def foo(x):

... if not hasattr(foo,'list'):
... foo.list = []
... foo.list.append(x)
... print foo.list


In Prothon:

def foo(x):
print foo.list.append!(x)
foo.list = []

(Sorry. I couldn't resist bragging.)
Jul 18 '05 #14
Mark Hahn wrote:
Andrea Griffini wrote:
I like more as an example:
>>> def foo(x):

... if not hasattr(foo,'list'):
... foo.list = []
... foo.list.append(x)
... print foo.list


In Prothon:
def foo(x):
print foo.list.append!(x)
foo.list = []

(Sorry. I couldn't resist bragging.)


About what?

Python 2.3.3 (#51, Dec 18 2003, 20:22:39) [MSC v.1200 32 bit (Intel)]
def foo(x): .... foo.list.append(x)
.... print foo.list
.... foo.list = []
foo('test')

['test']

(Oh, did you mean bragging about how a hard-to-see exclamation
mark causes append() to return the sequence? I thought
maybe it was about the function attribute or something.)

-Peter
Jul 18 '05 #15
Peter Hansen wrote:
In Prothon:
def foo(x):
print foo.list.append!(x)
foo.list = []

(Sorry. I couldn't resist bragging.)


About what?

Python 2.3.3 (#51, Dec 18 2003, 20:22:39) [MSC v.1200 32 bit (Intel)]
>>> def foo(x): ... foo.list.append(x)
... print foo.list
... >>> foo.list = []
>>> foo('test')

['test']

(Oh, did you mean bragging about how a hard-to-see exclamation
mark causes append() to return the sequence? I thought
maybe it was about the function attribute or something.)


Actually, I didn't know if the function attribute assignment outside the
function would work in Python or not. I guess I'll know better than to try
to play one-upmanship with Python next time. I did say I was sorry :-)

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 ;-)
Jul 18 '05 #16
On Wed, 16 Jun 2004 12:40:10 -0700, "Mark Hahn" <ma**@prothon.org>
wrote:
In Prothon:

def foo(x):
print foo.list.append!(x)
foo.list = []

(Sorry. I couldn't resist bragging.)


The very first thing I tried was assigning foo.list
outside of the function (and, by the way, that works in
python too); this however doesn't mimic C++ static,
as initialization of the static local variable is done
in C++ when (and only IF) the function is entered.
The value used for initialization can for example
depend on local parameters or global state at *that time*.

Using "hasattr" seemed ugly to me at first, but after
all you need an additional flag anyway, so why not
checking the presence of a certain key in foo.__dict__ ?
That way both initialization and setting the flag are
done at the same time using just one clean statement.

The only (very small) syntax price is the if (that in
C++ is implicit in the overused keyword "static").

Andrea
Jul 18 '05 #17
Andrea Griffini <ag****@tin.it> writes:
But if there are those better-looking statics, why so
much use of that modifiable-default-of-a-fake-parameter
ugly trick ? Is this something that became legal
only recently ?


Function attributes were in fact somewhat recently added (as of Python
2.1, so circa April of 2001).

-- David
Jul 18 '05 #18
"Mark Hahn" <ma**@prothon.org> wrote in message news:<5L2Ac.26$u%3.13@fed1read04>...
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 ;-)


I think the esclamation mark comes from Scheme if not from a more
ancient language. It is certainly not a new idea. OTOH, it is a good
idea, no question
about that. Same for "?" in booleans.
Michele Simionato
Jul 18 '05 #19
Mark wrote:
I like more as an example:
>>> def foo(x):

... if not hasattr(foo,'list'):
... foo.list = []
... foo.list.append(x)
... print foo.list


In Prothon:

def foo(x):
print foo.list.append!(x)
foo.list = []

(Sorry. I couldn't resist bragging.)


About what?

-Dave
Jul 18 '05 #20

"Dave Brueck" <da**@pythonapocrypha.com> wrote
I like more as an example:

>>> def foo(x):
... if not hasattr(foo,'list'):
... foo.list = []
... foo.list.append(x)
... print foo.list


In Prothon:

def foo(x):
print foo.list.append!(x)
foo.list = []

(Sorry. I couldn't resist bragging.)


About what?


Is there an echo in here? :-)
Jul 18 '05 #21
On Wed, 16 Jun 2004 21:41:07 -0700, rumours say that "Mark Hahn"
<ma**@prothon.org> might have written:
"Dave Brueck" <da**@pythonapocrypha.com> wrote
> > I like more as an example:
> >
> > >>> def foo(x):
> > ... if not hasattr(foo,'list'):
> > ... foo.list = []
> > ... foo.list.append(x)
> > ... print foo.list
>
> In Prothon:
>
> def foo(x):
> print foo.list.append!(x)
> foo.list = []
>
> (Sorry. I couldn't resist bragging.)


About what?


Is there an echo in here? :-)


Probably not. Given the python version:

def foo(x):
foo.list.append(x)
print foo.list
foo.list = []

Dave's question is legitimate. Are you bragging about the two lines
combined into one?
--
TZOTZIOY, I speak England very best,
"I have a cunning plan, m'lord" --Sean Bean as Odysseus/Ulysses
Jul 18 '05 #22
Christos TZOTZIOY Georgiou wrote:
Dave's question is legitimate. Are you bragging about the two lines
combined into one?


As I replied before to the same exact question, I didn't know that Python
could do the function attribute assignment outside of the function (yes it
was stupid of me) and I was bragging about taking six lines to three. It
turns out that Prothon can only improve it by one line. I also said I was
sorry in my very first posting. If you want me to repeat this a third time
I will.
Jul 18 '05 #23

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 ):
do stuff...

ie. use default values for function parameters which depend on previous
function parameters.
This can be very practical at times :

before (suppose 'info' is a class which has "text" and "htmlclass" as
members):

def format_information( info, htmlclass = None ):
if htmlclass == None:
htmlclass = info.htmlclass
return "<p class=%s>%s</p>" % (htmlclass, info.text )

after:

def format_information( info, htmlclass = info.htmlcass ):
return "<p class=%s>%s</p>" % (htmlclass, info.text )
the intended use would be :
format_information( info )
format_information( info, 'red_text' ) overrides the html_class in info.
The former example could be simplified (below) but I still think the
second example using your choice 2 is more elegant.

def format_information( info, htmlclass = None ):
return "<p class=%s>%s</p>" % (htmlclass or info.htmlclass, info.text )
Jul 18 '05 #24


I'm new to python. To my eyes this is a pretty poor attempt to
have static variables. I've implemented in the past a few


static variables in python

def accumulate( x ):
accumulate.accumulator += x
return accumulate.accumulator

accumulate.accumulator = 0
Jul 18 '05 #25
Mark Hahn wrote:
Troy Melhase wrote:

Here's an idea: if it ain't broke, don't fix it.

Seriously, you see a "wart" and a "problem". I see a pleasant
side-effect of the documented semantics. True, new folks are
surprised by the behavior, but once it's understood, it becomes more
powerful.

All four of the Python gotcha's, wart's and regrets lists I have found
included this problem. It is not only a newbie's problem as I showed in my
posting.

How do you intend to account for code like this:

def F(a, b, cache={}):
try:
return cache[(a,b)]
except (IndexError, ):
value = cache[(a,b)] = do_some_long_calc(a,b)
return value

Or even this:

shared_cache = {}

def F(a, b, cache=shared_cache):
...

The first example is very unreadable and uncool in general. Your second
example will work just fine with our fix.

Of course you can argue that this is bad style,

Yes I (and many others) will.

but the counter
argument is just as strong: this is quite pythonic and quite
readable.

I disagree strongly. I would never be caught coding something like that and
I love Python dearly.

Python is a tool, and you decrease the utility of that tool when you
limit it's idioms.

So far you have only shown me an idiom that many say should not be used.
Show me one that everyone agrees is useful.

How much Python code would these different proposals break?


A lot. I ran this:

$ find /usr/lib/python2.3/ -name "*.py" -exec grep "def.*=\[\]" {}
\; | wc

And see 67 instances just in the standard library. Multiply that by
a factor of 1000, 10000 or more to reflect code in the field, and you
might start to understand the significance of changing the language
definition.

That count is not accurate. Fixing this will not break every use of [] as a
default formal param. Using [] in __init__ for example would break nothing.
I can think of many other cases where it is legal to use []. The only case
I can think of that would break would be the idiom we disagree on above. If
I am wrong, then show me other cases.


Right. I'd like to see how many of these 67 instances of mutable def
args actually mutates them. I hope it is 0. In this case the proposed
modification would break nothing at all.

But I wonder what the actual semantics of the second proposal are.

You say the default argument is evaluated in its declaration context at
each call. But is that evaluation context garanteed to be the same at
every call ?

#Ex:
x=0
def t(a,b,c=x+1):
# could I assert c==1 if t called with 2 args ?

def make_some_obj():
return []
def t(a,b,c=make_some_obj()):
# can I assert c==[] if t called with 2 args ?

I think every data coming from outside the expression (the variables in
the expression including the global variables accessed from a function)
should be preserved. Let aside the eventual mutable state stored in an
object:

#Ex:
class Inc:
def __init__(self):
self.x=0
def newval():
self.x += 1
return self.x
x=Inc()
def t(a,b,c=x.newval()):
# can I assert c will increase by 1 at each call with 2 args ?
If I also might make a general argument for the fix then let me continue.
Doing a late evaluation of the default expression makes the language more
dynamic, which fits the overall goal of making Prothon more dynamic. Using
prototypes instead of classes, dynamic var scoping, this fix, and many other
Prothon changes from Python all work towards that goal.

Dynamic var scoping fixed another Python gotcha which doesn't break
anything. Here are the two versions of code showing the problem and the
fix:

--- Python ---

x = 1
def f():
... x = x + 1
... print x
...
f()

UnboundLocalError: local variable 'x' referenced before assignment

--- Prothon ---

O>> x = 1
1
O>> def f():
... x = x + 1
... print x
...
O>> f()
2

Prothon's scoping rules are dynamic which means that x comes from outside
the function until the actual assignment happens. At that point x becomes a
local variable. This, along with the fact that vars are inherited from
ancestors along with methods, allow for some intuitive and simple var
initialization techniques.


So then
0>> f()
2
0>> x
1

Obviously it is the responsibility of the programmer to make sure that the
outer x has the proper initialization value for the local x. This can cause
a hiding-of-uninitialized-vars bug if the programmer uses the same names for
unrelated variables but it is worth the extra power and intuitiveness.


Nice, I had never heard about Prothon. I'll give it a look.
--
Grégoire Dooms
Jul 18 '05 #26

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 ):
do stuff...

ie. use default values for function parameters which depend on previous
function parameters.
This can be very practical at times :

before (suppose 'info' is a class which has "text" and "htmlclass" as
members):

def format_information( info, htmlclass = None ):
if htmlclass == None:
htmlclass = info.htmlclass
return "<p class=%s>%s</p>" % (htmlclass, info.text )

after:

def format_information( info, htmlclass = info.htmlcass ):
return "<p class=%s>%s</p>" % (htmlclass, info.text )
the intended use would be :
format_information( info )
format_information( info, 'red_text' ) overrides the html_class in info.
The former example could be simplified (below) but I still think the
second example using your choice 2 is more elegant.

def format_information( info, htmlclass = None ):
return "<p class=%s>%s</p>" % (htmlclass or info.htmlclass, info.text )
--
Using Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 18 '05 #27


I'm new to python. To my eyes this is a pretty poor attempt to
have static variables. I've implemented in the past a few


static variables in python

def accumulate( x ):
accumulate.accumulator += x
return accumulate.accumulator

accumulate.accumulator = 0
--
Using Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 18 '05 #28
Grégoire Dooms wrote:
But I wonder what the actual semantics of the second proposal are.

You say the default argument is evaluated in its declaration context
at each call. But is that evaluation context garanteed to be the same
at every call ?

#Ex:
x=0
def t(a,b,c=x+1):
# could I assert c==1 if t called with 2 args ?
No, because x could change value. If you wanted that then you would say
c=1. When you say c = x+1 you get x+1, no more, no less. You can code it
to get whatever you want.
def make_some_obj():
return []
def t(a,b,c=make_some_obj()):
# can I assert c==[] if t called with 2 args ?
This case would always give you [], unless you redefined make_some_obj :-)
This is a dynamic language after all.
I think every data coming from outside the expression (the variables
in the expression including the global variables accessed from a
function) should be preserved. Let aside the eventual mutable state
stored in an object:

#Ex:
class Inc:
def __init__(self):
self.x=0
def newval():
self.x += 1
return self.x
x=Inc()
def t(a,b,c=x.newval()):
# can I assert c will increase by 1 at each call with 2 args ?
If you really want this then you should be asking for solution 3, which just
keeps the results of the expression evalutation at definition time and then
makes a copy at each call. You would be the only one asking for solution 3
so far. It appears that we are going with solution 2.
Nice, I had never heard about Prothon. I'll give it a look.


You can find it at http://prothon.org. Hang out on the Prothon mailing list
for a while. We are discussing some really fun stuff.
Jul 18 '05 #29
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.
Jul 18 '05 #30

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.google.c om
in comp.lang.python:
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_generated_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**@pythonapocrypha.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(self, 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.org> 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(self, 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
Dave Brueck wrote:
Ahh...so the method name is just _spelled_ with an exclamation point?
Yes, a Prothon identifier is the same as a Python identifier except that it
can end with an exclamation mark ( ! )or a question mark ( ? ). These marks
can only appear at the end and there can only be one. It is up to the
programmer to make sure he/she uses them properly.

Exclamation marks are to be used in identifiers if and only if it is a
method that modifies the target in-place.

Question marks are to be used on methods if and only if they return True or
False and nothing else.
IOW, the ! is a token you can use at the end of an identifier, but it
is not actually used by the language itself -
No, the marks are significant to the language just as any other part of the
identifier is.
Seems like a waste to reserve a
symbol for something so rarely needed.


I disagree. In-place modification is significant and happens often.
Ignoring this is dangerous.
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. :)


How can you argue that the exclamation mark indicating in-place-modification
does not make it more readable? Several other languages feel so also. We
didn't just make this up.
Jul 18 '05 #41
Dave Brueck wrote:
Seems like a waste to reserve a
symbol for something so rarely needed.


Lisp and Scheme do the same thing:

(set! a 5) <- sets a variable (i.e. changes its value)
(eq? a 6) <- tests equality (returns true or false)

It's defined precisely because it's not needed often (at least in the !
case): the functions that modify their arguments are few and far between,
so it is best to warn the programmer of this behaviour.

Though ? doesn't have such a pressing case as !, it does make code easier
to read (distinguishing functions that do something from those that make
simple queries).

Jul 18 '05 #42
Christopher wrote:
Seems like a waste to reserve a
symbol for something so rarely needed.


Lisp and Scheme do the same thing:

(set! a 5) <- sets a variable (i.e. changes its value)
(eq? a 6) <- tests equality (returns true or false)

It's defined precisely because it's not needed often (at least in the !
case): the functions that modify their arguments are few and far between,
so it is best to warn the programmer of this behaviour.


An apples-to-oranges comparison, IMO - it makes sense to delimit a side
effect in a functional language.

-Dave
Jul 18 '05 #43

Even though ? and ! feel awkward, I still think they make it more
readable.
Does the language enforce the bool return type on the ? variant ?
Jul 18 '05 #44
Mark wrote:
Ahh...so the method name is just _spelled_ with an exclamation point?
Yes, a Prothon identifier is the same as a Python identifier except that

it can end with an exclamation mark ( ! )or a question mark ( ? ). These marks can only appear at the end and there can only be one. It is up to the
programmer to make sure he/she uses them properly.

Exclamation marks are to be used in identifiers if and only if it is a
method that modifies the target in-place.

Question marks are to be used on methods if and only if they return True or False and nothing else.
IOW, the ! is a token you can use at the end of an identifier, but it
is not actually used by the language itself -
No, the marks are significant to the language just as any other part of

the identifier is.
You cut out my example that elaborated on the questions I was asking:

1) It's just part of the name. So from the language's perspective, whether
you call it appendINPLACE or append! makes no difference?

2) (continuing on #1) that being the case, it's really just a naming
convention, correct? (because the language itself doesn't do anything with
that information - additional checking to enforce that convention, different
functionality, etc)
From what I gather that's a "yes" to both questions.
Seems like a waste to reserve a
symbol for something so rarely needed.


I disagree. In-place modification is significant and happens often.
Ignoring this is dangerous.


Well, now we're down to disagreeing how often it occurs. I just haven't seen
very many cases in practice where (1) I want to both do something to an
object AND have it return itself in a single step and (2) doing so can be
done in a clear way, and (3) I want to do it for a good reason rather than
just wanting to save a line of code. In the few cases I've seen so far, the
rationale has apparently been to make the code shorter, not necessarily
better.
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. :)


How can you argue that the exclamation mark indicating

in-place-modification does not make it more readable?


(1) Because in practice I haven't seen the need for it much, so in my mind
it wouldn't be used that much (making it less familiar & therefore more work
to understand the code) and/or it encourages cramming more onto a line just
because you can.

The "print list.append!(5)" is a fine example of this IMO - you've combined
two *completely unrelated* operations for no good reason. Yes, it's just an
example, I know, but it's probably an example of how it'll be commonly
(mis)used. For every one "good" use of the ! form you'll probably have a
thousand uses where the only benefit was that it saved a line of code. <0.5
wink>

(2) It's just a naming convention, so you can't rely on it's presence or
absence as being accurate (probably not a big deal in practice, but
still...)

(3) Cases like w = x.y.z!() would confuse me, as would

ref! = obj.method!
....
x = ref!(5, 6, 7) # what the heck, x == obj?!

-Dave
Jul 18 '05 #45
Pierre-Frédéric Caillaud wrote:
Does the language enforce the bool return type on the ? variant ?


That's a good idea. I'll throw that out for a vote on the Prothon
discussion group.

You should join our mailing list. We need ideas. The traffic is light so
it won't be much of a bother. We only have 25 to 30 messages a day and
there are no pesky users yet :-) We have some really bright people
contributing right now and it is getting quite exciting.

We are in the middle of figuring out how to add classes to Prothon (just as
a design-contract kind of thing) and are about to move on to language
features (like coroutines) of the stackless engine we are built on. We have
Mr. Stackless himself (Christian) helping out so it should go well.
Jul 18 '05 #46
Dave Brueck wrote:
You cut out my example that elaborated on the questions I was asking:
Sorry.
1) It's just part of the name. So from the language's perspective,
whether you call it appendINPLACE or append! makes no difference?
It makes no difference to the interpreter, but to the other Prothon
programmers it makes a difference because it's a convention. Also append!()
is a standard library function so you don't get to change it :-)
2) (continuing on #1) that being the case, it's really just a naming
convention, correct? (because the language itself doesn't do anything
with that information - additional checking to enforce that
convention, different functionality, etc)
From what I gather that's a "yes" to both questions.
Yes, Yes.
Seems like a waste to reserve a
symbol for something so rarely needed.
I disagree. In-place modification is significant and happens often.
Ignoring this is dangerous.


Well, now we're down to disagreeing how often it occurs. I just
haven't seen very many cases in practice where (1) I want to both do
something to an object AND have it return itself in a single step and
(2) doing so can be done in a clear way, and (3) I want to do it for
a good reason rather than just wanting to save a line of code. In the
few cases I've seen so far, the rationale has apparently been to make
the code shorter, not necessarily better.


There are many clean readable programming conventions that rely on return
values. This is certainly an acceptable construct, right?

while len(list.append!(x)) < 10:
blah blah blah

Compare that to the less readable and less maintainable Python version:

list.append(x)
while len(list) < 10:
blah blah
list.append(x) # duplicate code

I can come up with many more. I think when you have been without a language
feature for a while you tend to block it out of your mind.
How can you argue that the exclamation mark indicating
in-place-modification does not make it more readable?


(1) Because in practice I haven't seen the need for it much,


You don't look for it because in the back of your mind you know you can't
use it.
it encourages
cramming more onto a line just because you can.
You are being pretty negative here. Not everyone is out to cram more into a
line. Give people a little more credit.
The "print list.append!(5)" is a fine example of this IMO - you've
combined two *completely unrelated* operations for no good reason.
Yes, it's just an example, I know, but it's probably an example of
how it'll be commonly (mis)used. For every one "good" use of the !
form you'll probably have a thousand uses where the only benefit was
that it saved a line of code. <0.5 wink>
I don't think I commited any big sin. It seemed plenty readable to me.
Combining operations on one line is done all the time. I suppose you would
code this:

x = a + b
x = x * c
print x

instead of this:

print (a+b) * c
(2) It's just a naming convention, so you can't rely on it's presence
or absence as being accurate (probably not a big deal in practice, but
still...)
All programming involves trust. Any programmer can obfuscate his code a
million ways.
(3) Cases like w = x.y.z!() would confuse me, as would

ref! = obj.method!
You are confused pretty easily. Maybe because you aren't used to them.
They read easily to me.
x = ref!(5, 6, 7) # what the heck, x == obj?!


ref! is invalid by convention and obj?! would throw a syntax exception.
Jul 18 '05 #47
Mark Hahn 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). Comments? How much Python code would these different proposals break?


I think not so very much.
The default mutable parameters have been abused to keep
class-like state. Default parameters in general have also been
used to speed up object lookup on certain speed contests.

Both usages are obsolete, since the same effect can be
achieved with a local function sitting in a scope,
from where it can use mutables it it needs to.

So I don't see many remaining advantages, and I think it is
a good idea to make the defaults less sticky.

+1 for 2)

ciao - chris

--
Christian Tismer :^) <mailto:ti****@stackless.com>
Mission Impossible 5oftware : Have a break! Take a ride on Python's
Johannes-Niemeyer-Weg 9a : *Starship* http://starship.python.net/
14109 Berlin : PGP key -> http://wwwkeys.pgp.net/
work +49 30 89 09 53 34 home +49 30 802 86 56 mobile +49 173 24 18 776
PGP 0x57F3BF04 9064 F4E1 D754 C2FF 1619 305B C09C 5A3B 57F3 BF04
whom do you want to sponsor today? http://www.stackless.com/
Jul 18 '05 #48
Christian Tismer 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).

Comments? How much Python code would these different proposals
break?


I think not so very much.
The default mutable parameters have been abused to keep
class-like state. Default parameters in general have also been
used to speed up object lookup on certain speed contests.

Both usages are obsolete, since the same effect can be
achieved with a local function sitting in a scope,
from where it can use mutables it it needs to.

So I don't see many remaining advantages, and I think it is
a good idea to make the defaults less sticky.

+1 for 2)


I'm glad you voted that way because I implemented #2 a few days ago :-o

Not that I couldn't change it.

Jul 18 '05 #49
On Fri, 25 Jun 2004 15:45:51 -0700, Mark Hahn <ma**@prothon.org> wrote:
Pierre-Frédéric Caillaud wrote:
Does the language enforce the bool return type on the ? variant ?
That's a good idea.


In fact I'm not that sure anymore. I like the fact Python operators like
"or" and "and" return values themselves and not booleans ; that's
extremely useful. Thus imposing boolean return types to these "?" method
might break some usability. Then again, it might also be a good idea, but
it needs somoe thinking !
You should join our mailing list. We need ideas. The traffic is light
Done !
and are about to move on to language
features (like coroutines) of the stackless engine we are built on.


Very good !
Jul 18 '05 #50

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

Similar topics

35
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...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
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,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
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,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

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.