Re: scope of function parameters (take two)

2011-05-31 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 Chris Rebert
On Mon, May 30, 2011 at 11:37 PM, Henry Olders henry.old...@mcgill.ca wrote:
 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.

So, you have no problem with *global* side effects, but side effects
with a /more constrained/ scope bother you?
That's kinda odd, IMO.

Cheers,
Chris
-- 
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 henry.old...@mcgill.ca 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 henry.old...@mcgill.ca 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


Re: scope of function parameters (take two)

2011-05-31 Thread Daniel Kluev
On Tue, May 31, 2011 at 6:17 PM, Henry Olders henry.old...@mcgill.ca wrote:
 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?

As I've pointed, you can make decorator to do that. Adding @copy_args
to each function you intend to be pure is not that hard.

import decorator
import copy

@decorator.decorator
def copy_args(f, *args, **kw):
nargs = []
for arg in args:
nargs.append(copy.deepcopy(arg))
nkw = {}
for k,v in kw.iteritems():
nkw[k] = copy.deepcopy(v)
return f(*nargs, **nkw)

@copy_args
def test(a):
a.append(1)
return a

 l = [0]
 test(l)
[0, 1]
 l
[0]


 inspect.getargspec(test)
ArgSpec(args=['a'], varargs=None, keywords=None, defaults=None)

So this decorator achieves needed result and preserves function signatures.


-- 
With best regards,
Daniel Kluev
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: scope of function parameters (take two)

2011-05-31 Thread Chris Angelico
On Tue, May 31, 2011 at 5:17 PM, Henry Olders henry.old...@mcgill.ca wrote:
 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?

Yes, it would make Python quite different. If suddenly you couldn't
pass a mutable object to a function to get it muted (that sounds
wrong), then code will break. Also, there's a fairly serious
performance penalty to copying everything when it's not necessary. As
has been suggested, you can specifically and deliberately cause this
effect for any function(s) you wish to protect in this way; there's
no need to change the language's fundamentals to do it.

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


Re: scope of function parameters (take two)

2011-05-31 Thread Wolfgang Rohdewald
On Dienstag 31 Mai 2011, Henry Olders wrote:
 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.

I don't know any object oriented language where it is not
possible to change objects passed in as parameters. It
is up to the passed object (a list in your case) to allow
or disallow manipulations no matter how they are invocated,
and the object is the same in the calling code and in the
called function.

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


Re: scope of function parameters (take two)

2011-05-31 Thread Terry Reedy

On 5/31/2011 2:37 AM, Henry Olders wrote:


what I want is a function that is free of side effects back through
the parameters passed in the function call.


You can get that by refraining from mutating parameter objects.
Simple as that.
Just do not expect Python to enforce that discipline on everyone else.
To be really functional, and never mutate objects, do not use Python 
lists, which are arrays. Use linked-list trees, like Lisp languages and 
perhaps others do. One can easily do this with tuples, or a subclass of 
tuples, or a class wrapping tuples.


Linked-lists and functional programming go together because prepending 
to a linked list creates a new object while appending to a Python list 
mutates an existing list. Similarly, popping from a linked list 
retrieves an item and an existing sublist while popping from a Python 
list retrieves and item and mutates the list.


--
Terry Jan Reedy

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


Re: scope of function parameters (take two)

2011-05-31 Thread Terry Reedy

On 5/31/2011 3:17 AM, Henry Olders wrote:


Clearly, making a copy within the function eliminates the possibility of
the side effects caused by passing in mutable objects.


Mutable objects and mutating methods and functions are a *feature* of 
Python. If you do not like them, do not use them.


 Would having the  compiler/interpreter do this automatically
 make python so much different?

Yes. How would you then write a function like list.sort or list.pop?

It is fundamental that parameters are simply local names that must be 
bound as part of the calling process. After that, they are nothing special.


Python is a language for adults that take responsibility for what they 
do. If you do not like argument-mutating functions, then do not write 
them and do not use them (without making a copy yourself).


Python was not designed to model timeless immutable mathematics. It is 
an information-object manipulation language and in real life, we mutate 
collections and associations all the time.


--
Terry Jan Reedy

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


Re: scope of function parameters (take two)

2011-05-31 Thread Ian Kelly
On Tue, May 31, 2011 at 1:38 AM, Daniel Kluev dan.kl...@gmail.com wrote:
 @decorator.decorator
 def copy_args(f, *args, **kw):
    nargs = []
    for arg in args:
        nargs.append(copy.deepcopy(arg))
    nkw = {}
    for k,v in kw.iteritems():
        nkw[k] = copy.deepcopy(v)
    return f(*nargs, **nkw)

There is no decorator module in the standard library.  This must be
some third-party module.  The usual way to do this would be:

def copy_args(f):
@functools.wraps(f)
def wrapper(*args, **kw):
nargs = map(copy.deepcopy, args)
nkw = dict(zip(kw.keys(), map(copy.deepcopy, kw.values(
return f(*nargs, **nkw)
return wrapper

Note that this will always work, whereas the decorator.decorator
version will break if the decorated function happens to take a keyword
argument named f.

Cheers,
Ian
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: scope of function parameters (take two)

2011-05-31 Thread Chris Kaynor
On Tue, May 31, 2011 at 9:16 AM, Ian Kelly ian.g.ke...@gmail.com wrote:

 On Tue, May 31, 2011 at 1:38 AM, Daniel Kluev dan.kl...@gmail.com wrote:
  @decorator.decorator
  def copy_args(f, *args, **kw):
 nargs = []
 for arg in args:
 nargs.append(copy.deepcopy(arg))
 nkw = {}
 for k,v in kw.iteritems():
 nkw[k] = copy.deepcopy(v)
 return f(*nargs, **nkw)

 There is no decorator module in the standard library.  This must be
 some third-party module.  The usual way to do this would be:

 def copy_args(f):
@functools.wraps(f)
def wrapper(*args, **kw):
nargs = map(copy.deepcopy, args)
nkw = dict(zip(kw.keys(), map(copy.deepcopy, kw.values(
return f(*nargs, **nkw)
return wrapper


Is there any reason not to simplify this to:

def copy_args(f):
   @functools.wraps(f)
   def wrapper(*args, **kw):
   nargs = copy.deepcopy(args)
   nkw = copy.deepcopy(kw)
   return f(*nargs, **nkw)
   return wrapper


It means you will copy the keys as well, however they will (almost)
certainly be strings which is effectively a no-op.


 Note that this will always work, whereas the decorator.decorator
 version will break if the decorated function happens to take a keyword
 argument named f.

 Cheers,
 Ian
 --
 http://mail.python.org/mailman/listinfo/python-list

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


Re: scope of function parameters (take two)

2011-05-31 Thread Ian Kelly
On Tue, May 31, 2011 at 10:34 AM, Chris Kaynor ckay...@zindagigames.com wrote:
 Is there any reason not to simplify this to:
 def copy_args(f):
    @functools.wraps(f)
    def wrapper(*args, **kw):
        nargs = copy.deepcopy(args)
        nkw = copy.deepcopy(kw)
        return f(*nargs, **nkw)
    return wrapper

No reason, good call.

 It means you will copy the keys as well, however they will (almost)
 certainly be strings which is effectively a no-op.

I think the keys will certainly be strings.  Is there any scenario
where they might not be?
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: scope of function parameters (take two)

2011-05-31 Thread Ethan Furman

Henry Olders wrote:

[...] what I want is a function that is free of side effects [...]


Shoot, that's easy!  Just write your function to not have any!

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


Re: scope of function parameters (take two)

2011-05-31 Thread Ethan Furman

Henry Olders wrote:
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?


It would be a different language.

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


Re: scope of function parameters (take two)

2011-05-31 Thread Chris Kaynor
I was thinking you could do something strange like:

kw = {object(): None}
def test(**kw):
   print kw
test(**kw)

however, upon testing it (in Python 2.6), I found that it errors while
trying to unpack the kw dict stating that they must all be strings.

Perhaps making a custom class derived off basestring, str, unicode, or bytes
might allow some oddness and possibly slightly worse performance.

Chris


On Tue, May 31, 2011 at 10:10 AM, Ian Kelly ian.g.ke...@gmail.com wrote:

 On Tue, May 31, 2011 at 10:34 AM, Chris Kaynor ckay...@zindagigames.com
 wrote:
  Is there any reason not to simplify this to:
  def copy_args(f):
 @functools.wraps(f)
 def wrapper(*args, **kw):
 nargs = copy.deepcopy(args)
 nkw = copy.deepcopy(kw)
 return f(*nargs, **nkw)
 return wrapper

 No reason, good call.

  It means you will copy the keys as well, however they will (almost)
  certainly be strings which is effectively a no-op.

 I think the keys will certainly be strings.  Is there any scenario
 where they might not be?
 --
 http://mail.python.org/mailman/listinfo/python-list

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


Re: scope of function parameters (take two)

2011-05-31 Thread Daniel Kluev
On Wed, Jun 1, 2011 at 3:16 AM, Ian Kelly ian.g.ke...@gmail.com wrote:

 There is no decorator module in the standard library.  This must be
 some third-party module.  The usual way to do this would be:

Yes, but its very useful for decorators and provides some
not-readily-available functionality.
http://pypi.python.org/pypi/decorator/3.3.1

 Note that this will always work, whereas the decorator.decorator
 version will break if the decorated function happens to take a keyword
 argument named f.

No, it will not. Its the magic of decorator library, it is
signature-preserving, while your variant breaks function signature and
causes problems to any code that relies on signatures (happens with
Pylons, for example).

 @copy_args
... def test(a, f=None):
... print f
...
 test([], f=123)
123

Basically decorator.decorator uses exec to create new function, with
signature of function you pass to your decorator, so it does not
matter what names you used for args in decorator itself.

-- 
With best regards,
Daniel Kluev
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: scope of function parameters (take two)

2011-05-31 Thread Ian Kelly
On Tue, May 31, 2011 at 6:04 PM, Daniel Kluev dan.kl...@gmail.com wrote:
 On Wed, Jun 1, 2011 at 3:16 AM, Ian Kelly ian.g.ke...@gmail.com wrote:

 There is no decorator module in the standard library.  This must be
 some third-party module.  The usual way to do this would be:

 Yes, but its very useful for decorators and provides some
 not-readily-available functionality.
 http://pypi.python.org/pypi/decorator/3.3.1

 Note that this will always work, whereas the decorator.decorator
 version will break if the decorated function happens to take a keyword
 argument named f.

 No, it will not. Its the magic of decorator library, it is
 signature-preserving, while your variant breaks function signature and
 causes problems to any code that relies on signatures (happens with
 Pylons, for example).

Ah, I see.  I assumed it was much simpler than it is.  I found a way
to break it with Python 3, though:

 @copy_args
... def test(*, f):
... return f
...
 test(f=1)
Traceback (most recent call last):
  File stdin, line 1, in module
  File string, line 2, in test
  File stdin, line 6, in copy_args
TypeError: test() needs keyword-only argument f

The interesting thing here is that the decorated function has exactly
the correct function signature; it just doesn't work.

Cheers,
Ian
-- 
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 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 

Re: scope of function parameters (take two)

2011-05-30 Thread Benjamin Kaplan
On Mon, May 30, 2011 at 5:28 PM, Henry Olders henry.old...@mcgill.ca wrote:

 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 
 

Re: scope of function parameters (take two)

2011-05-30 Thread Daniel Kluev
On Tue, May 31, 2011 at 11:28 AM, Henry Olders henry.old...@mcgill.ca wrote:
 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

You still mis-reading docs and explanations you received from the list.
Let me try again.

First, there are objects and names. Calling either of them as
'variables' is leading to this mis-understanding. Name refers to some
object. Object may be referenced by several names or none.

Second, when you declare function `def somefunc(a, b='c')` a and b are
both local to this function. Even if there are some global a and b,
they are 'masked' in somefunc scope.
Docs portion you cited refer to other situation, when there is no
clear indicator of 'locality', like this:

def somefunc():
 print a

In this - and only in this - case a is considered global.

Third, when you do function call like somefunc(obj1, obj2) it uses
call-by-sharing model. It means that it assigns exactly same object,
that was referenced by obj1, to name a. So both obj1 and _local_ a
reference same object. Therefore when you modify this object, you can
see that obj1 and a both changed (because, in fact, obj1 and a are
just PyObject*, and point to exactly same thing, which changed).

However, if you re-assign local a or global obj1 to other object,
other name will keep referencing old object:

obj1 = []

def somefunc(a):
 a.append(1) # 'a' references to the list, which is referenced by
obj1, and calls append method of this list, which modifies itself in
place
 global obj1
 obj1 = [] # 'a' still references to original list, which is [1]
now, it have no relation to obj1 at all

somefunc(obj1)

-- 
With best regards,
Daniel Kluev
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: scope of function parameters (take two)

2011-05-30 Thread Terry Reedy

On 5/30/2011 8:28 PM, Henry Olders wrote:



Sadly, I feel that the main issue that I was trying to address, has
not been dealt with.


False. Please go back and read what I and others wrote before.

...

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)


You would like Python to be the way it is. Fine. For the Nth time,
PARAMATER NAMES ARE LOCAL NAMES. Period.


but NOT global unless explicitly defined as global.


PARAMETER NAMES **CANNOT** BE DEFINED AS GLOBAL.

 def f(a):
global a

SyntaxError: name 'a' is parameter and global

Again, go back and reread what I and other wrote. I believe that you 
are, in part, hypnotized by the work 'variable'. Can you define the 
word? There are 10 to 20 possible variations, and yours is probably 
wrong for Python.



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.


From 'import this':
Namespaces are one honking great idea -- let's do more of those!
Python is loaded with namespaces.

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


Python is lexically scoped.


another quote from the wikipedia entry for Common Lisp: the use of
lexical scope isolates program modules from unwanted interactions.


Python is lexically scoped.


If making python behave this way is impossible,


How do you expect us to respond when you say Please make Python the way 
it is.? Or is you say If making Python the way it is is impossible...?


Now, if you actually want Python to drastically change its mode of 
operation and break most existing programs, do not bother asking.


 Are there others who feel as I do that a

function parameter should always be local to the function?


Yes, we all do, and they are.

--
Terry Jan Reedy

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


Re: scope of function parameters (take two)

2011-05-30 Thread Daniel Kluev
On Tue, May 31, 2011 at 12:30 PM, Terry Reedy tjre...@udel.edu wrote:
 Again, go back and reread what I and other wrote. I believe that you are, in
 part, hypnotized by the work 'variable'. Can you define the word? There are
 10 to 20 possible variations, and yours is probably wrong for Python.

On a sidenote, I wonder what is the reason to keep word 'variable' in
python documentation at all. I believe word 'name' represents concept
better, and those, who come from other languages, would be less likely
to associate wrong definitions with it.

-- 
With best regards,
Daniel Kluev
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: scope of function parameters (take two)

2011-05-30 Thread Chris Angelico
On Tue, May 31, 2011 at 10:28 AM, Henry Olders henry.old...@mcgill.ca wrote:
 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).


Side point, on variable scope.

There is a philosophical split between declaring variables and not
declaring them. Python is in the latter camp; you are not required to
put int a; char *b; float c[4]; before you use the integer, string,
and list/array variables a, b, and c. This simplifies code
significantly, but forces the language to have an unambiguous scoping
that doesn't care where you assign to a variable.

Example:

def f():
  x=1  # x is function-scope
  if cond: # cond is global
x=2 # Same function-scope x as above
  print(x) # Function-scope, will be 2 if cond is true

This is fine, and is going to be what you want. Another example:

def f():
  print(x) # global
  #  way down, big function, you've forgotten what you're doing
for x in list_of_x:
  x.frob()

By using x as a loop index, you've suddenly made it take function
scope, which stops it from referencing the global. Granted, you
shouldn't be using globals with names like 'x', but it's not hard to
have duplication of variable names. As a C programmer, I'm accustomed
to being able to declare a variable in an inner block and have that
variable cease to exist once execution leaves that block; but that
works ONLY if you declare the variables where you want them.

Infinitely-nested scoping is simply one of the casualties of a
non-declarative language.

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


Re: scope of function parameters (take two)

2011-05-30 Thread Daniel Kluev
On Tue, May 31, 2011 at 2:05 PM, Chris Angelico ros...@gmail.com wrote:
 Infinitely-nested scoping is simply one of the casualties of a
 non-declarative language.

Well, this is not accurate, as you can have 'infinitely-nested
scoping' in python, in form of nested functions. For example, you can
use map(lambda x: expressions with x, including other
map/filter/reduce/lambda's, list_of_x), and you will have your
isolated scopes. Although due to lambdas supporting only expressions,
following this style leads to awkward and complicated code (and/or
instead if, map instead for, and so on).


-- 
With best regards,
Daniel Kluev
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: scope of function parameters (take two)

2011-05-30 Thread Chris Angelico
On Tue, May 31, 2011 at 1:18 PM, Daniel Kluev dan.kl...@gmail.com wrote:
 On Tue, May 31, 2011 at 2:05 PM, Chris Angelico ros...@gmail.com wrote:
 Infinitely-nested scoping is simply one of the casualties of a
 non-declarative language.

 Well, this is not accurate, as you can have 'infinitely-nested
 scoping' in python, in form of nested functions. For example, you can
 use map(lambda x: expressions with x, including other
 map/filter/reduce/lambda's, list_of_x), and you will have your
 isolated scopes. Although due to lambdas supporting only expressions,
 following this style leads to awkward and complicated code (and/or
 instead if, map instead for, and so on).

That's an incredibly messy workaround, and would get ridiculous if you
tried going many levels in. It's like saying that a C-style 'switch'
statement can be implemented in Python using a dictionary of
lambdas... and then trying to implement fall-through. But you're
right; a lambda does technically create something of a nested scope -
albeit one in which the only internal variables are its parameters.

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


Re: scope of function parameters (take two)

2011-05-30 Thread Dan Stromberg
On Mon, May 30, 2011 at 5:28 PM, Henry Olders henry.old...@mcgill.cawrote:

 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.


If you don't want any globals other than callables and modules and builtins,
then don't define any.  I normally code that way, other than an infrequent
(EG) HAVE_CSTRINGIO global or similar (which is module-global, not
program-global) - that is, for variables describing what sorts of optional
modules are available.

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.

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

main()

Or use Haskell.  Please don't try to turn Python into Haskell.  Each has its
own interesting tradeoffs, and we don't really need two Pythons, and we
don't really need two Haskells.

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


Re: scope of function parameters (take two)

2011-05-30 Thread Steven D'Aprano
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
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: scope of function parameters (take two)

2011-05-30 Thread Wolfgang Rohdewald
On Dienstag 31 Mai 2011, Henry Olders wrote:
 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.

the parameter is local but it points to an object from an outer
scope - that could be the scope of the calling function or maybe
the global scope. So if you change the value of this parameter, 
you change that object from outer scope. But the parameter 
itself is still local. If you do 

def fnc2(c):
   c = 5

the passed object will not be changed, c now points to another
object. This is different from other languages where the global
c would change (when passing c by reference)

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

did you read the link Steven gave you?
http://mail.python.org/pipermail/tutor/2010-December/080505.html

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


Re: scope of function parameters (take two)

2011-05-30 Thread Thomas Rachel

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
--
http://mail.python.org/mailman/listinfo/python-list


Re: scope of function parameters (take two)

2011-05-30 Thread Ben Finney
Daniel Kluev dan.kl...@gmail.com writes:

 On a sidenote, I wonder what is the reason to keep word 'variable' in
 python documentation at all. I believe word 'name' represents concept
 better, and those, who come from other languages, would be less likely
 to associate wrong definitions with it.

I agree, but the term “variable” is used freely within the Python
development team to refer to Python's name-to-object bindings, and that
usage isn't likely to stop through our efforts.

So the burden is unfortunately on us to teach each newbie that this
often-used term means something other than what they might expect.

-- 
 \   “In case you haven't noticed, [the USA] are now almost as |
  `\ feared and hated all over the world as the Nazis were.” —Kurt |
_o__)   Vonnegut, 2004 |
Ben Finney
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: scope of function parameters (take two)

2011-05-30 Thread Daniel Kluev
On Tue, May 31, 2011 at 4:13 PM, Wolfgang Rohdewald
wolfg...@rohdewald.de 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

This can be done in Python (to some degree), like this

@copy_args
def somefunc(a, b, c):
 ...

where copy_args would explicitly call deepcopy() on all args passed to
the function.
Or, to save some performance, wrap them in some CopyOnEdit proxy
(although this is tricky, as getattr/getitem can modify object too if
class overrides them).

Obviously it would not save you from functions which use
global/globals() or some other ways to change state outside their
scope.

-- 
With best regards,
Daniel Kluev
-- 
http://mail.python.org/mailman/listinfo/python-list