Re: scope of function parameters (take two)

Discussion in 'Python' started by Henry Olders, May 31, 2011.

  1. Henry Olders

    Henry Olders Guest

    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
    Henry Olders, May 31, 2011
    #1
    1. Advertising

  2. On Mon, 30 May 2011 20:28:34 -0400, Henry Olders wrote:

    > 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


    They are.


    > (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).


    for loop variables are local to the function, by design.

    List comprehension variables leak outside the comprehension. That is an
    accident that is fixed in Python 3. Generator expression variables never
    leaked.


    > 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.

    [...]
    > 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.


    Then you haven't understood the problem at all.


    > 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?


    How else would the functions be found if they were ONLY treated as local?

    Consider this code:

    def spam():
    print a_name
    print len(a_name)

    This includes two names: "a_name" and "len". They both follow the same
    lookup rules. Functions in Python are first-class values, just like ints,
    strings, or any other type.

    You don't want spam() to see any global "a_name" without it being
    declared. But how then can it see "len"?

    Don't think that Python could change the rules depending on whether
    you're calling a name or not. Consider this:

    def spam():
    print a_name
    print a_name(len)


    Do you expect the first lookup to fail, and the second to succeed?



    > 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.


    They are. Always have been, always will be.


    > This would prevent the sort
    > of problems that I encountered as described in my original post.


    No it wouldn't. You are confused by what you are seeing, and interpreting
    it wrongly.

    I believe that what you want is pass-by-value semantics, in the old-
    school Pascal sense, where passing a variable to a function makes a copy
    of it. This is not possible in Python. As a design principle, Python
    never copies objects unless you as it to.


    > 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.


    Of course. You could create a language that supports any passing models
    you want. Pascal and VB support pass-by-value ("copy on pass") and pass-
    by-reference. Algol supports pass-by-value and (I think) pass-by-name.
    There's no reason why you couldn't create a language to support pass-by-
    value and pass-by-object. Whether this is a good idea is another story.

    However, that language is not Python.


    > 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.


    Er, yes? That already happens.


    > 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.


    You keep bring up the function parameter list as if that made a
    difference. It doesn't.

    Perhaps this will enlighten you:

    >>> alist = [1, 2, 3, 4]
    >>> blist = alist
    >>> blist[0] = -999
    >>> alist

    [-999, 2, 3, 4]


    Passing alist to a function is no different to any other name binding. It
    doesn't make a copy of the list, it doesn't pass a reference to the name
    "alist", it passes the same object to a new scope.



    > 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.


    Function parameters are never global. You are misinterpreting what you
    see if you think they are.


    [...]
    > 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?


    They are. You are misinterpreting what you see.




    --
    Steven
    Steven D'Aprano, May 31, 2011
    #2
    1. Advertising

  3. Am 31.05.2011 02:28 schrieb Henry Olders:

    > 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?


    Not for the builtins, but for the global ones.

    Suppose you have a module

    def f(x):
    return 42

    def g(x, y):
    return f(x) + f(y)

    Would you really want to need a "global f" inside g?


    Besides, this doesn't have to do with your original problem at all. Even
    then, a

    def h(x):
    x.append(5)
    return x

    would clobber the given object, because x is just a name for the same
    object which the caller has.

    > 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)


    They are - in terms of name binding. In Python, you always have objects
    which can be referred to from a variety of places under different names.

    Maybe what you want are immutable objects (tuples) instead of mutable
    ones (lists)...


    > 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


    Again: It is the way here.

    Think of C: there you can have a

    int f(int * x) {
    *x = 42;
    return x;
    }

    The scope of the variable x is local here. Same in Python.
    The object referred to by *x is "somewhere else", by design. Same in Python.


    > If making python behave this way is impossible, then I will just have
    > to live with it.


    Even if I still think that you are confusing "scope of a name binding"
    and "scope of an object", it is a conceptual thing.

    Surely it would have been possible to do otherwise, but then it would be
    a different language. Objects can be mutable, period.

    In MATLAB, e.g., you have what you desire here: you always have to pass
    your object around and get another ne back, even if you just add or
    remove a field of a struct or change the value of a field.

    HTH,

    Thomas
    Thomas Rachel, May 31, 2011
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. ann
    Replies:
    13
    Views:
    661
    Patricia Shanahan
    Sep 13, 2005
  2. Boris Sargos
    Replies:
    3
    Views:
    414
    Boris Sargos
    Apr 23, 2004
  3. Master
    Replies:
    5
    Views:
    349
    Julie
    Sep 10, 2004
  4. Henry Olders

    scope of function parameters

    Henry Olders, May 29, 2011, in forum: Python
    Replies:
    21
    Views:
    495
  5. Andrew Falanga
    Replies:
    2
    Views:
    198
    Andrew Falanga
    Nov 22, 2008
Loading...

Share This Page