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 the return statement is for.

I don't believe I'm the only person who thinks this way. Here is a quote from 
wikipedia: "It is considered good programming practice to make the scope of 
variables as narrow as feasible so that different parts of a program do not 
accidentally interact with each other by modifying each other's variables. 
Doing so also prevents action at a distance. Common techniques for doing so are 
to have different sections of a program use different namespaces, or to make 
individual variables "private" through either dynamic variable scoping or 
lexical variable scoping." 
(http://en.wikipedia.org/wiki/Variable_(programming)#Scope_and_extent).

It also seems that other languages suitable for functional programming take the 
approach I think python should use. Here is another quote from the wikipedia 
entry for Common Lisp: "the use of lexical scope isolates program modules from 
unwanted interactions. Due to their restricted visibility, lexical variables 
are private. If one module A binds a lexical variable X, and calls another 
module B, references to X in B will not accidentally resolve to the X bound in 
A. B simply has no access to X. For situations in which disciplined 
interactions through a variable are desirable, Common Lisp provides special 
variables. Special variables allow for a module A to set up a binding for a 
variable X which is visible to another module B, called from A. Being able to 
do this is an advantage, and being able to prevent it from happening is also an 
advantage; consequently, Common Lisp supports both lexical and dynamic scope. 
(http://en.wikipedia.org/wiki/Common_Lisp#Determiners_of_scope).


If making python behave this way is impossible, then I will just have to live 
with it. But if it's a question of "we've always done it this way", or, " why 
change? I'm not bothered by it", then I will repeat my original question: Are 
there others who feel as I do that a function parameter should always be local 
to the function?

And again, thank you all for taking the time to respond.

Henry


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

Reply via email to