list parameter of a recursive function

2010-10-06 Thread TP
Hi,

I have a function f that calls itself recursively. It has a list as second 
argument, with default argument equal to None (and not [], as indicated at:
http://www.ferg.org/projects/python_gotchas.html#contents_item_6 )

This is the outline of my function:

def f ( argument, some_list = None ):

   if some_list == None:
  some_list = []
   [...]
   # creation of a new_argument
   # the function is called recursively only if some condition is respected
   if some_condition:
  some_list.append( elem )
  f( new_argument, some_list )
   # otherwise, we have reached a leaf of the a branch of the recursive tree
   # (said differently, terminal condition has been reached for this branch)
   print "Terminal condition"

The problem is that when the terminal condition is reached, we return back 
to some other branch of the recursive tree, but some_list has the value 
obtained in the previous branch!
So, it seems that there is only one some_list list for all the recursive 
tree.
To get rid of this behavior, I have been compelled to do at the beginning of 
f:

import copy from copy
some_list = copy( some_list )

I suppose this is not a surprise to you: I am compelled to create a new 
some_list with the same content.
So, if I am right, all is happening as if the parameters of a function are 
always passed by address to the function. Whereas in C, they are always 
passed by copy (which gives relevance to pointers).

Am I right?

Julien

-- 
python -c "print ''.join([chr(154 - ord(c)) for c in '*9(9&(18%.\
9&1+,\'Z4(55l4('])"

"When a distinguished but elderly scientist states that something is
possible, he is almost certainly right. When he states that something is
impossible, he is very probably wrong." (first law of AC Clarke)
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-06 Thread Chris Torek
In article 
TP   wrote:
>I have a function f that calls itself recursively. It has a list as second 
>argument, with default argument equal to None (and not [], as indicated at:
>http://www.ferg.org/projects/python_gotchas.html#contents_item_6 )
>
>This is the outline of my function:
>
>def f ( argument, some_list = None ):
>
>   if some_list == None:
>  some_list = []
>   [...]
>   # creation of a new_argument
>   # the function is called recursively only if some condition is respected
>   if some_condition:
>  some_list.append( elem )
>  f( new_argument, some_list )
>   # otherwise, we have reached a leaf of the a branch of the recursive tree
>   # (said differently, terminal condition has been reached for this branch)
>   print "Terminal condition"
>
>The problem is that when the terminal condition is reached, we return back 
>to some other branch of the recursive tree, but some_list has the value 
>obtained in the previous branch!

Yes, this is the way it is supposed to work. :-)

>So, it seems that there is only one some_list list for all the recursive 
>tree.  To get rid of this behavior, I have been compelled to do at the
>beginning of f:
>
>import copy from copy

[from copy import copy, rather]

>some_list = copy( some_list )
>
>I suppose this is not a surprise to you: I am compelled to create a new 
>some_list with the same content.

The above will work, or for this specific case, you can write:

some_list = list(some_list)

which has the effect of making a shallow copy of an existing list:

   >>> base = [1, 2]
   >>> l1 = base
   >>> l2 = list(l1)
   >>> l1 is l2
   False
   >>> l1[0] is l2[0]
   True
   >>> base.append(3)
   >>> l2
   [[1, 2, 3]]
   >>>

but will also turn *any* iterator into a (new) list; the latter
may often be desirable.

>So, if I am right, all is happening as if the parameters of a function are 
>always passed by address to the function. Whereas in C, they are always 
>passed by copy (which gives relevance to pointers).
>
>Am I right?

Mostly.  Python distinguishes between mutable and immutable items.
Mutable items are always mutable, immutable items are never mutable,
and the mutability of arguments is attached to their "fundamental
mutability" rather than to their being passed as arguments.  This
is largely a point-of-view issue (although it matters a lot to
people writing compilers, for instance).

Note that if f() is *supposed* to be able to modify its second
parameter under some conditions, you would want to make the copy
not at the top of f() but rather further in, and in this case,
that would be trivial:

def f(arg, some_list = None):
if some_list is None:
some_list = []
...
if some_condition:
# make copy of list and append something
f(new_arg, some_list + [elem])
elif other_condition:
# continue modifying same list
f(new_arg, some_list)
...

(Note: you can use also the fact that list1 + list2 produces a new
list to make a copy by writing "x = x + []", or "x = [] + x", but
"x = list(x)" is generally a better idea here.)
-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W)  +1 801 277 2603
email: gmail (figure it out)  http://web.torek.net/torek/index.html
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-06 Thread TP
Chris Torek wrote:

>>import copy from copy
> 
> [from copy import copy, rather]

Yes, sorry.

> Note that if f() is *supposed* to be able to modify its second
> parameter under some conditions, you would want to make the copy
> not at the top of f() but rather further in, and in this case,
> that would be trivial:
> 
> def f(arg, some_list = None):
> if some_list is None:
> some_list = []
> ...
> if some_condition:
> # make copy of list and append something
> f(new_arg, some_list + [elem])
> elif other_condition:
> # continue modifying same list
> f(new_arg, some_list)

Thanks a lot Chris!
I think I prefer doing an explicit copy.copy, because it allows to remind 
the reader that it is very important to take care of that. But your trick is 
very interesting also.

Cheers,

Julien


-- 
python -c "print ''.join([chr(154 - ord(c)) for c in '*9(9&(18%.\
9&1+,\'Z4(55l4('])"

"When a distinguished but elderly scientist states that something is
possible, he is almost certainly right. When he states that something is
impossible, he is very probably wrong." (first law of AC Clarke)
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-06 Thread Diez B. Roggisch
TP  writes:

> Hi,
>
> I have a function f that calls itself recursively. It has a list as second 
> argument, with default argument equal to None (and not [], as indicated at:
> http://www.ferg.org/projects/python_gotchas.html#contents_item_6 )
>
> This is the outline of my function:
>
> def f ( argument, some_list = None ):
>
>if some_list == None:
>   some_list = []
>[...]
># creation of a new_argument
># the function is called recursively only if some condition is respected
>if some_condition:
>   some_list.append( elem )
>   f( new_argument, some_list )
># otherwise, we have reached a leaf of the a branch of the recursive tree
># (said differently, terminal condition has been reached for this branch)
>print "Terminal condition"
>
> The problem is that when the terminal condition is reached, we return back 
> to some other branch of the recursive tree, but some_list has the value 
> obtained in the previous branch!
> So, it seems that there is only one some_list list for all the recursive 
> tree.
> To get rid of this behavior, I have been compelled to do at the beginning of 
> f:
>
> import copy from copy
> some_list = copy( some_list )
>
> I suppose this is not a surprise to you: I am compelled to create a new 
> some_list with the same content.
> So, if I am right, all is happening as if the parameters of a function are 
> always passed by address to the function. Whereas in C, they are always 
> passed by copy (which gives relevance to pointers).
>
> Am I right?

Yes and no. For some sensible definition of yes (this is to avoid the
*endless* discussions about python parameter passing semantics and it's
relation to common, yet misunderstood or unprecisely defined definitions
of parameter passing), passing around a mutable object (such as a list
or dictionary) will be like a pointer - mutations are visible to all
others having a reference to it.

You are wrong though that C-semantics are different, meaning that
"passing by copy" is not what happens. Some things are copied (primitive
types, structs that are passed directly). But not, for example - and
relevant to this case - arrays (as they are "only" pointers), lists (as they
don't exist, but are modeled by structs containing pointers).

Back to your example: your solution is perfectly fine, although a bit
costly and more error-prone if you happen to forget to create a copy.
A safer alternative for these cases is using tuples, because they are
immutable. 

Diez
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-06 Thread Steven D'Aprano
On Wed, 06 Oct 2010 23:00:10 +0200, TP wrote:

> I think I prefer doing an explicit copy.copy, because it allows to
> remind the reader that it is very important to take care of that. 

I think a comment is better for that. It's better to explicitly say 
something is important than to assume the reader will guess.

Besides, what if the caller wants to supply their own list, and they 
*want* it modified in place rather than a copy made? (I have no idea why 
they might want this, but anything is possible...) You should avoid 
making copies except in methods that are *for* making copies.


-- 
Steven
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-06 Thread TP
Diez B. Roggisch wrote:

> Back to your example: your solution is perfectly fine, although a bit
> costly and more error-prone if you happen to forget to create a copy.
> A safer alternative for these cases is using tuples, because they are
> immutable.

Thanks Diez for your explanation.
The problem with tuples is that it is not easy to modify them: in my case, I 
need to append a string to the tuple at each recursive step.
So, it seems to me that I need to write a loop to modify the tuple, because 
on a simple example:

>>> a=("foo", "bar")
>>> b=(a[0],a[1],"toto")
>>> b
(u'foo', u'bar', u'toto')

I do not find any other means to obtain that result for b. With lists, I can 
use ".extend()".
Am I right?


-- 
python -c "print ''.join([chr(154 - ord(c)) for c in '*9(9&(18%.\
9&1+,\'Z4(55l4('])"

"When a distinguished but elderly scientist states that something is
possible, he is almost certainly right. When he states that something is
impossible, he is very probably wrong." (first law of AC Clarke)
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-06 Thread TP
Steven D'Aprano wrote:

>> I think I prefer doing an explicit copy.copy, because it allows to
>> remind the reader that it is very important to take care of that.
> 
> I think a comment is better for that. It's better to explicitly say
> something is important than to assume the reader will guess.

Yes, you are right.

> Besides, what if the caller wants to supply their own list, and they
> *want* it modified in place rather than a copy made? (I have no idea why
> they might want this, but anything is possible...) You should avoid
> making copies except in methods that are *for* making copies.

My function gives a sensible result only if the list is not modified in 
place. In fact, this list is only used internally for the recursion. That is 
why I have made this function "private":

def __relation_loop( sqlite_cursor
, table_name
, crossed_tables = None
, previous_step_link = None
, onetomany_allowed_depth = 1 ):

I give to the user (me!) only access to the public function:

def relation_loop( sqlite_cursor
, table_name
, onetomany_allowed_depth = 1 ):

return __relation_loop( sqlite_cursor
, table_name
, crossed_tables = None
, previous_step_link = None
, onetomany_allowed_depth = onetomany_allowed_depth )

So, crossed_tables and previous_step_link, are only used by the recursion 
process, they are not accessible to the user.

-- 
python -c "print ''.join([chr(154 - ord(c)) for c in '*9(9&(18%.\
9&1+,\'Z4(55l4('])"

"When a distinguished but elderly scientist states that something is
possible, he is almost certainly right. When he states that something is
impossible, he is very probably wrong." (first law of AC Clarke)
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-06 Thread Seebs
On 2010-10-07, TP  wrote:
> Diez B. Roggisch wrote:
>> A safer alternative for these cases is using tuples, because they are
>> immutable.

> The problem with tuples is that it is not easy to modify them:

This is probably the best post-and-response I've seen in the last couple
of months.

-s
-- 
Copyright 2010, all wrongs reversed.  Peter Seebach / usenet-nos...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-07 Thread Chris Rebert
On Wed, Oct 6, 2010 at 10:25 PM, TP  wrote:
> Diez B. Roggisch wrote:
>
>> Back to your example: your solution is perfectly fine, although a bit
>> costly and more error-prone if you happen to forget to create a copy.
>> A safer alternative for these cases is using tuples, because they are
>> immutable.
>
> Thanks Diez for your explanation.
> The problem with tuples is that it is not easy to modify them: in my case, I
> need to append a string to the tuple at each recursive step.
> So, it seems to me that I need to write a loop to modify the tuple, because
> on a simple example:
>
 a=("foo", "bar")
 b=(a[0],a[1],"toto")
 b
> (u'foo', u'bar', u'toto')
>
> I do not find any other means to obtain that result for b. With lists, I can
> use ".extend()".
> Am I right?

Nope, sorry:
>>> a = ("foo", "bar")
>>> s = "toto"
>>> b = a + (s,)
>>> b
('foo', 'bar', 'toto')

Cheers,
Chris
--
http://blog.rebertia.com
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-07 Thread Diez B. Roggisch
TP  writes:

> Diez B. Roggisch wrote:
>
>> Back to your example: your solution is perfectly fine, although a bit
>> costly and more error-prone if you happen to forget to create a copy.
>> A safer alternative for these cases is using tuples, because they are
>> immutable.
>
> Thanks Diez for your explanation.
> The problem with tuples is that it is not easy to modify them: in my case, I 
> need to append a string to the tuple at each recursive step.
> So, it seems to me that I need to write a loop to modify the tuple, because 
> on a simple example:
>
 a=("foo", "bar")
 b=(a[0],a[1],"toto")
 b
> (u'foo', u'bar', u'toto')
>
> I do not find any other means to obtain that result for b. With lists, I can 
> use ".extend()".
> Am I right?

Yes and no - again. Of course you cannot use extend. But you can
concatenate:

b = a + ("toto",)

Note the trailing comma. A common misconception is to think that tuples
are build using parethesis. They aren't. They are build using the
comma-operator, and needed for disambiguation, e.g. in this case:

foo(a, (b, c))

Diez
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-07 Thread Terry Reedy

On 10/6/2010 3:22 PM, TP wrote:

Hi,

I have a function f that calls itself recursively. It has a list as second
argument, with default argument equal to None (and not [], as indicated at:
http://www.ferg.org/projects/python_gotchas.html#contents_item_6 )


This sort of function is an exception. If you add the line below, so 
that you can use just one list, you should just say 'some_list = []' in 
the header.




This is the outline of my function:

def f ( argument, some_list = None ):

if some_list == None:
   some_list = []
[...]
# creation of a new_argument
# the function is called recursively only if some condition is respected
if some_condition:
   some_list.append( elem )
   f( new_argument, some_list )

 some_list.pop()

# otherwise, we have reached a leaf of the a branch of the recursive tree
# (said differently, terminal condition has been reached for this branch)
print "Terminal condition"

The problem is that when the terminal condition is reached, we return back
to some other branch of the recursive tree, but some_list has the value
obtained in the previous branch!


Uhless you pop items off the stack as you back up!

This is assuming that you do not need to keep the leaf value of the list 
as the funcions works up and back down to the next leaf, and so on. Even 
if you do, it might still be better to copy the working buffer just once 
when you hit the leaf.



So, it seems that there is only one some_list list for all the recursive
tree.
To get rid of this behavior, I have been compelled to do at the beginning of
f:

import copy from copy
some_list = copy( some_list )

I suppose this is not a surprise to you: I am compelled to create a new
some_list with the same content.
So, if I am right, all is happening as if the parameters of a function are
always passed by address to the function. Whereas in C, they are always
passed by copy (which gives relevance to pointers).


C sometimes copies values and sometimes passes addresses (pointers, 
references). It does the latter for arrays and functions. I think the 
rule has varied for structs. C and Python have quite different 
name-object-value models.


Python calls functions by binding argument objects to parameter names.
Unlike C assignment, Python name binding never copies objects.

CPython implements binding by directly associating name and object 
address. It also does not use a garbage collector that move objects 
around in memory. Other implementation do differently.


--
Terry Jan Reedy

--
http://mail.python.org/mailman/listinfo/python-list


Re: list parameter of a recursive function

2010-10-07 Thread TP
Seebs wrote:

> On 2010-10-07, TP  wrote:
>> Diez B. Roggisch wrote:
>>> A safer alternative for these cases is using tuples, because they are
>>> immutable.
> 
>> The problem with tuples is that it is not easy to modify them:
> 
> This is probably the best post-and-response I've seen in the last couple
> of months.

:-) Yes, they are immutable. I badly expressed the fact that the facilities 
of lists to create new lists are not available with tuples.

But my sentence was funny, I have to admit it :-) I hope that this 
conversation will not be included in the "fortunes"!!

-- 
python -c "print ''.join([chr(154 - ord(c)) for c in '*9(9&(18%.\
9&1+,\'Z4(55l4('])"

"When a distinguished but elderly scientist states that something is
possible, he is almost certainly right. When he states that something is
impossible, he is very probably wrong." (first law of AC Clarke)
-- 
http://mail.python.org/mailman/listinfo/python-list