Weird lambda rebinding/reassignment without me doing it

S

ssecorp

I am never redefining the or reassigning the list when using validate
but since it spits the modified list back out that somehow means that
the modified list is part of the environment and not the old one.
i thought what happend inside a function stays inside a function
meaning what comes out is independent of what comes in.
Meaning if I want the list I send as a parameter to the function I
have to do x = func(x) and not just func(x) and x is magically what
comes out of func().
Doesnt Python have closure or that isnt what this is about?


def validate(placed):
student = round(random.random()*401)
if student in placed:
return validate(placed)
else:
placed.append(student)
return student, placed

def val(placed):
student = round(random.random()*401)
if student in placed:
return validate(placed)
else:
placed.append(student)
return student


g = lambda x:validate(x)
l=[]
for x in range(1,10):
g(l)


(141.0, [141.0])
(19.0, [141.0, 19.0])
(86.0, [141.0, 19.0, 86.0])
(120.0, [141.0, 19.0, 86.0, 120.0])
(76.0, [141.0, 19.0, 86.0, 120.0, 76.0])
(262.0, [141.0, 19.0, 86.0, 120.0, 76.0, 262.0])
(234.0, [141.0, 19.0, 86.0, 120.0, 76.0, 262.0, 234.0])
(74.0, [141.0, 19.0, 86.0, 120.0, 76.0, 262.0, 234.0, 74.0])
(325.0, [141.0, 19.0, 86.0, 120.0, 76.0, 262.0, 234.0, 74.0, 325.0])
g = lambda x:val(x)
l=[]
for x in range(1,10):
g(l)


183.0
33.0
315.0
244.0
308.0
168.0
146.0
378.0
297.0
 
T

Terry Reedy

David said:
A function cannot modify the value of a global variable

Yes it can.
a.append('yes I can')
['yes I can']
(unless it specifies "global"). It doesn't reassign anything.

The statement 'global a' would allow f to *rebind* the global *name*
'a'. The word 'variable' should almost not be used in discussing Python
since it is often unclear whether it refers to a name (or collection
slot) or an object bound thereto.
But in the functions below you're not reassigning a variable,
you're _modifiying_ an object. A function _can_ modify an
object you pass to it:

It can modify any mutable object it can access.

Python does have closures. This is not about that.

Delete this. It is redundant with the below.

I believe this is equivalent to

def addval(placed):
while True:
student = round(random.random()*401)
if student not in placed:
break
placed.append(student)
return student

While this avoids the indefinite recursion depth problem, it does not
avoid the indefinite time problem. Use random.shuffle, or write your
own version if doing this for practice. Also consider removing the
return statement unless you actually directly use the added value. It
is easier to remember that addval mutates 'placed' without the return.

This is doubly diseased.

First, never write a 'name = lambda...' statement since it is equivalent
to a def statement except that the resulting function object lacks a
proper .funcname attribute. The above only trivially abbreviates
def g(x): return validate(x)
by 3 characters. Another reason is that the lambda form somehow more
often tricks people into the next mistake .

Second, never write a function (with either def or lambda) that simply
returns a function of the argument(s). The wrapping does nothing! This
is a waste of time and space for both you and the interpreter. The
above is functionally equivalent to
g = validate
and if you want that, you could name the function 'g' when you define it.
In some fonts, 'l' and '1' are nearly identical; please use something
else for public code, which you made this to be by posting it;-)
As said, the 'g' wrapper is useless.
addval(l)

Hope this helps.

Terry Jan Reedy
 
S

ssecorp

ty very good answer. i know i shouldn't use lambda like that, i never
do i was just playing around there and then this happened which i
thought was weird.






A function cannot modify the value of a global variable

Yes it can.
 >>> a=[]
 >>> def f():
        a.append('yes I can')

 >>> f()
 >>> a
['yes I can']
(unless it specifies "global"). It doesn't reassign anything.

The statement 'global a' would allow f to *rebind* the global *name*
'a'.  The word 'variable' should almost not be used in discussing Python
since it is often unclear whether it refers to a name (or collection
slot) or an object bound thereto.
But in the functions below you're not reassigning a variable,
you're _modifiying_ an object. A function _can_ modify an
object you pass to it:

It can modify any mutable object it can access.

Python does have closures.  This is not about that.

Delete this. It is redundant with the below.

I believe this is equivalent to

def addval(placed):
   while True:
     student = round(random.random()*401)
     if student not in placed:
       break
   placed.append(student)
   return student

While this avoids the indefinite recursion depth problem, it does not
avoid the indefinite time problem.  Use random.shuffle, or write your
own version if doing this for practice.  Also consider removing the
return statement unless you actually directly use the added value.  It
is easier to remember that addval mutates 'placed' without the return.

This is doubly diseased.

First, never write a 'name = lambda...' statement since it is equivalent
to a def statement except that the resulting function object lacks a
proper .funcname attribute.  The above only trivially abbreviates
   def g(x): return validate(x)
by 3 characters.  Another reason is that the lambda form somehow more
often tricks people into the next mistake .

Second, never write a function (with either def or lambda) that simply
returns a function of the argument(s).  The wrapping does nothing!  This
is a waste of time and space for both you and the interpreter.  The
above is functionally equivalent to
   g = validate
and if you want that, you could name the function 'g' when you define it.

In some fonts, 'l' and '1' are nearly identical; please use something
else for public code, which you made this to be by posting it;-)

As said, the 'g' wrapper is useless.
        addval(l)

Hope this helps.

Terry Jan Reedy
 
S

ssecorp

def mod(x,y):
return x.append(y)
mod([1,2],3)
k=[1,2,3]
k [1, 2, 3]
l = mod(k,4)
l
k [1, 2, 3, 4]
l
k==l False
mod(k,5)
k [1, 2, 3, 4, 5]
mod(l,4)

Traceback (most recent call last):
File "<pyshell#29>", line 1, in <module>
mod(l,4)
File "<pyshell#18>", line 2, in mod
return x.append(y)
AttributeError: 'NoneType' object has no attribute 'append'
l
l=k
l [1, 2, 3, 4, 5]
i=mod(k,1)
i

same stuff but i dont find this intuitive.
 
M

MRAB

        return x.append(y)
append adds y to list x and returns None, which is then returned by
mod.
mod([1,2],3)
k=[1,2,3]
k [1, 2, 3]
l = mod(k,4)
4 has been appended to list k and mod has returned None, so l is now
None.
Because k is [1, 2, 3, 4] and l is None.
mod(k,5)
k [1, 2, 3, 4, 5]
mod(l,4)

Traceback (most recent call last):
  File "<pyshell#29>", line 1, in <module>
    mod(l,4)
  File "<pyshell#18>", line 2, in mod
    return x.append(y)
AttributeError: 'NoneType' object has no attribute 'append'
l is None, not a list, so it complains!
l
l=k
l [1, 2, 3, 4, 5]
i=mod(k,1)
i

same stuff but i dont find this intuitive.

It's usual for methods which modify to return None.
 
T

Terry Reedy

Steven said:
Using lambda in this way is no more "diseased" than aliasing any other
object.

In the context of giving advice to a confused beginner, I disagree.
He must learn def statements. Lambda expressions are optional.
> It's a matter of personal preference not to bind a lambda to a
name. Functions, whether created by lambda or def, are first class
objects, and as such there's nothing wrong with binding them to names.

When I brought this up on pydev, in the context of a style guide
addition, about 9 of 10 respondants agreed that this should be
discouraged. Alex Martelli reported his experience that this
construction more often leads people to the useless wrapping of function
calls, such as the OP posted, than the def statement equivalent does.

One of the major reasons people give for wanting lambda expressions kept
in Python and for using them is that they do not want to have to think
up a name for short expressions. If such a person then turns around and
binds the resulting function object to a name, then that rationale
disappears.

Over the years, people have written on c.l.p about 'lambdas' as if they
were a separate class of objects somehow different from def objects (and
not just an expression). I believe writing and reading both 'name =
lambda ...' and 'def name(...' engenders and reinforces this delusion,
especially for beginners.
Admittedly, the lack of a func_name attribute can sometimes make
tracebacks harder to understand, especially if you've got many bound
lambdas.

Is saving two keystrokes worth that disadvantage, and the confusions
mentioned above? To me, no. Hence my advice.

Terry Jan Reedy
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top