scope of function parameters

2011-05-29 Thread Henry Olders
I just spent a considerable amount of time and effort debugging a program. The 
made-up code snippet below illustrates the problem I encountered:

def main():
a = ['a list','with','three elements']
print a
print fnc1(a)
print a

def fnc1(b):
return fnc2(b)

def fnc2(c):
c[1] = 'having'
return c

This is the output:
['a list', 'with', 'three elements']
['a list', 'having', 'three elements']
['a list', 'having', 'three elements']

I had expected the third print statement to give the same output as the first, 
but variable a had been changed by changing variable c in fnc2.

It seems that in Python, a variable inside a function is global unless it's 
assigned. This rule has apparently been adopted in order to reduce clutter by 
not having to have global declarations all over the place.

I would have thought that a function parameter would automatically be 
considered local to the function. It doesn't make sense to me to pass a global 
to a function as a parameter.

One workaround is to call a function with a copy of the list, eg in fnc1 I 
would have the statement "return fnc2(b[:]". But this seems ugly.

Are there others who feel as I do that a function parameter should always be 
local to the function? Or am I missing something here?

Henry




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


Re: scope of function parameters

2011-05-29 Thread Henry Olders

Henry




On 2011-05-29, at 5:47 , Wolfgang Rohdewald wrote:

> On Sonntag 29 Mai 2011, Henry Olders wrote:
>> It seems that in Python, a variable inside a function is
>> global unless it's assigned.
> 
> no, they are local
> 
>> I would have thought that a function parameter would
>> automatically be considered local to the function. It doesn't
>> make sense to me to pass a global to a function as a
>> parameter.
> 
> it is local. But consider what you actually passed:
> You did not pass a copy of the list but the list itself.
> You could also say you passed a reference to the list.
> All python variables only hold a pointer (the id) to
> an object. This object has a reference count and is
> automatically deleted when there are no more references
> to it.
> 
> If you want a local copy of the list you can either
> do what you called being ugly or do just that within
> the function itself - which I think is cleaner and
> only required once.
> 
> def fnc2(c):
>   c = c[:]
>c[1] = 'having'
>return c

Thank you, Wolfgang. That certainly works, but to me it is still a workaround 
to deal with the consequence of a particular decision. From my perspective, a 
function parameter should be considered as having been assigned (although the 
exact assignment will not be known until runtime), and as an assigned variable, 
it should be considered local.

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


Re: scope of function parameters (take two)

2011-05-30 Thread Henry Olders

On 2011-05-29, at 4:30 , Henry Olders wrote:

> I just spent a considerable amount of time and effort debugging a program. 
> The made-up code snippet below illustrates the problem I encountered:
> 
> def main():
>   a = ['a list','with','three elements']
>   print a
>   print fnc1(a)
>   print a
>   
> def fnc1(b):
>   return fnc2(b)
> 
> def fnc2(c):
>   c[1] = 'having'
>   return c
> 
> This is the output:
> ['a list', 'with', 'three elements']
> ['a list', 'having', 'three elements']
> ['a list', 'having', 'three elements']
> 
> I had expected the third print statement to give the same output as the 
> first, but variable a had been changed by changing variable c in fnc2.
> 
> It seems that in Python, a variable inside a function is global unless it's 
> assigned. This rule has apparently been adopted in order to reduce clutter by 
> not having to have global declarations all over the place.
> 
> I would have thought that a function parameter would automatically be 
> considered local to the function. It doesn't make sense to me to pass a 
> global to a function as a parameter.
> 
> One workaround is to call a function with a copy of the list, eg in fnc1 I 
> would have the statement "return fnc2(b[:]". But this seems ugly.
> 
> Are there others who feel as I do that a function parameter should always be 
> local to the function? Or am I missing something here?
> 

My thanks to all the people who responded - I've learned a lot. Sadly, I feel 
that the main issue that I was trying to address, has not been dealt with. 
Perhaps I didn't explain myself properly; if so, my apologies.

I am trying to write python programs in a more-or-less functional programming 
mode, ie functions without side effects (except for print statements, which are 
very helpful for debugging). This is easiest when all variables declared in 
functions are local in scope (it would also be nice if variables assigned 
within certain constructs such as for loops and list comprehensions were local 
to that construct, but I can live without  it). 

It appears, from my reading of the python documentation, that a deliberate 
decision was made to have variables that are referenced but not assigned in a 
function, have a global scope. I quote from the python FAQs: "In Python, 
variables that are only referenced inside a function are implicitly global. If 
a variable is assigned a new value anywhere within the function’s body, it’s 
assumed to be a local. If a variable is ever assigned a new value inside the 
function, the variable is implicitly local, and you need to explicitly declare 
it as ‘global’.
Though a bit surprising at first, a moment’s consideration explains this. On 
one hand, requiring global for assigned variables provides a bar against 
unintended side-effects. On the other hand, if global was required for all 
global references, you’d be using global all the time. You’d have to declare as 
global every reference to a built-in function or to a component of an imported 
module. This clutter would defeat the usefulness of the global declaration for 
identifying side-effects." (http://docs.python.org/faq/programming.html)

This suggests that the decision to make unassigned (ie "free" variables) have a 
global scope, was made somewhat arbitrarily to  prevent clutter. But I don't 
believe that the feared clutter would materialize. My understanding is that 
when a variable is referenced, python first looks for it in the function's 
namespace, then the module's, and finally the built-ins. So why would it be 
necessary to declare references to built-ins as globals?

What I would like is that the variables which are included in the function 
definition's parameter list, would be always treated as local to that function 
(and of course, accessible to nested functions) but NOT global unless 
explicitly defined as global. This would prevent the sort of problems that I 
encountered as described in my original post. I may be wrong here, but it seems 
that the interpreter/compiler should be able to deal with this, whether the 
parameter passing is by value, by reference, by object reference, or something 
else. If variables are not assigned (or bound) at compile time, but are 
included in the parameter list, then the binding can be made at runtime.
And I am NOT talking about variables that are only referenced in the body of a 
function definition; I am talking about parameters (or arguments) in the 
function's parameter list. As I stated before, there is no need to include a 
global variable in a parameter list, and if you want to have an effect outside 
of the function, that's what th

Re: scope of function parameters (take two)

2011-05-30 Thread Henry Olders
On 2011-05-31, at 1:13 , Wolfgang Rohdewald wrote:

> 
> what you really seem to want is that a function by default
> cannot have any side effects (you have a side effect if a
> function changes things outside of its local scope). But
> that would be a very different language than python

You're partially right - what I want is a function that is free of side effects 
back through the parameters passed in the function call. Side effects via 
globals or print statements is fine by me. 

python seems to be undergoing changes all the time. List comprehensions were 
added in python 2.0, according to wikipedia. I like list comprehensions and use 
them all the time because they are powerful and concise.
> 
> did you read the link Steven gave you?
> http://mail.python.org/pipermail/tutor/2010-December/080505.html

Yes, I did, thanks.

Henry

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


Re: scope of function parameters (take two)

2011-05-31 Thread Henry Olders
On 2011-05-30, at 20:52 , Benjamin Kaplan wrote:

> On Mon, May 30, 2011 at 5:28 PM, Henry Olders  wrote:
>> 
>> On 2011-05-29, at 4:30 , Henry Olders wrote:
>> 
> 
> Python doesn't have true globals. When we say "global" what we mean is
> "module or built-in". Also, consider this code
> 
> from math import sin
> def redundant_sin(x) :
>return sin(x)
> 
> In Python, everything is an object. That includes functions.  By your
> definition, that function would either have to be written as
> def redundant_sin(sin, x) :
> and you would have to pass the function in every time you wanted to
> call it or have a "global sin" declaration in your function. And you
> would need to do that for every single function that you call in your
> function body.
> 
I don't believe so. Within redundant_sin, x is local, so if I change x, it will 
not change any objects named x outside of the function. As far as sin is 
concerned, if it were passed to redundant_sin via the parameter list, then it 
would be local, but otherwise sin would be looked for in the function 
definition; if not found there, it would be looked for in the module, where it 
would be found. I am not suggesting any changes to how names are looked up or 
scoped.

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


Re: scope of function parameters (take two)

2011-05-31 Thread Henry Olders

On 2011-05-31, at 24:35 , Dan Stromberg wrote:

> 
> On Mon, May 30, 2011 at 5:28 PM, Henry Olders  wrote:
> 
> Be careful not to conflate global scoping or global lifetime, with mutability 
> or pure, side-effect-free functions (callables).  It sounds like what you 
> want is immutability and/or freedom from side effects, which is found most 
> often in (pure) functional languages - which is not what Python is, nor does 
> it attempt to be so.

I think you're right, I've been conflating scoping with side effects caused by 
passing mutable objects. 
> 
> In Python, and in many other languages, if you pass a scalar (or more 
> generally, an object of an immutable type) to a function, and then change the 
> scalar in that function, you only change the scalar within that function, not 
> within the caller.
> 
> However, if you pass an aggregate type like a list (array) or dictionary 
> (hash table), then the formal argument itself that you've passed is still 
> only changeable within that function, however what it points off at _is_ 
> changeable via that formal argument.  This is of course because otherwise 
> passing a 1 gigabyte dictionary to a function would either have to copy the 
> whole dictionary, or implement some sort of Copy-on-Write semantics, or make 
> it somehow readonly.
> 
> If you need side effect-free functions in Python, you'd probably best copy 
> your aggregates, EG:
> 
> import copy
> 
> def main():
>   a = ['a list','with','three elements']
>   print a
>   print fnc1(a)
>   print a
> 
> def fnc1(b):
>   b = copy.deepcopy(b)
>   return fnc2(b)
> 
> def fnc2(c):
>   c = copy.deepcopy(c)
>   c[1] = 'having'
>   return c

Clearly, making a copy within the function eliminates the possibility of the 
side effects caused by passing in mutable objects. Would having the 
compiler/interpreter do this automatically make python so much different? What 
if it were optional, like nested scopes were in python 2.1 until they became 
standard in 2.2?

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