3

I am asking because of the classic problem where somebody creates a list of lambdas:

foo = []
for i in range(3):
    foo.append((lambda: i))

for l in foo:
    print(l())

and unexpectedly gets only twos as output. The commonly proposed solution is to make i a named argument like this:

foo = []
for i in range(3):
    foo.append((lambda i=i: i))

for l in foo:
    print(l())

Which produces the desired output of 0, 1, 2 but now something magical has happened. It sort of did what is expected because Python is pass-by-reference and you didn't want a reference.

Still, just adding a new name to something, shouldn't that just create another reference?

So the question becomes what are the exact rules for when something is not a reference?

Considering that ints are immutable and the following works:

x = 3
y = x
x = 5
print(x, y) // outputs 5 3

probably explains why adding that named parameter works. A local i with the same value was created and captured.

Now why, in the case of our lambdas was the same i referenced? I pass an int to function and it is refenced and if I store it in a variable it is copied. Hm.

Basically I am looking for the most concise and abstract way possible to remember exactly how this works. When is the same value referenced, when do I get a copy. If it has any common names and there are programming languages were it works the same that would be interesting as well.

Here is my current assumption:

  1. Arguments are always passed to functions by reference.
  2. Assigning to a variable of immutable type creates a copy.

I am asking anyway, just to make sure and hopefully get some background.

Charles
  • 50,943
  • 13
  • 104
  • 142
Sarien
  • 6,647
  • 6
  • 35
  • 55
  • 4
    *Assigning to a variable of immutable type creates a copy.* is incorrect. It just assigns that immutable object to that name. – Gareth Latty Jun 03 '13 at 21:57
  • Ah, that makes sense, of course, because these types aren't the kind where copying would be useful (in the sense that it is for reference types where an object can be more than what you just ad-hoc constructed). Thanks! – Sarien Jun 03 '13 at 21:59
  • 2
    @Sarien: It's not just that certain types don't get copied because it wouldn't be useful. In Python, _nothing_ gets copied, ever, unless you explicitly ask for a copy. – abarnert Jun 03 '13 at 22:48
  • Mainly because copying a Python object is a non-trivial task (hence why there is no default implementation for `__copy__()` or `__deepcopy__()`. – Gareth Latty Jun 03 '13 at 23:06

6 Answers6

4

The issue here is how you think of names.

In your first example, i is a variable that is assigned to every time the loop iterates. When you use lambda to make a function, you make a function that accesses the name i and returns it's value. This means as the name i changes, the value returned by the functions also changes.

The reason the default argument trick works is that the name is evaluated when the function is defined. This means the default value is the value the i name points to at that time, not the name itself.

i is a label. 0, 1 and 2 are the objects. In the first case, the program assigns 0 to i, then makes a function that returns i - it then does this with 1 and 2. When the function is called, it looks up i (which is now 2) and then returns it.

In the second example, you assign 0 to i, then you make a function with a default argument. That default argument is the value that is gotten by evaluating i - that is the object 0. This is repeated for 1 and 2. When the function is called, it assigns that default value to a new variable i, local to the function and unrelated to the outer i.

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • While this is true in so far as it goes, it really isn't correct or complete without discussion of function scopes. – Marcin Jun 03 '13 at 22:13
  • @Marcin This was definitely an explanation aimed at a basic understanding of the behaviour, not a complete overview of scoping. – Gareth Latty Jun 03 '13 at 22:16
  • I think this answer verges on the misleading - if anything it tends to deny that in one case there are two (or rather, four) `i` variables, which is the reason why the second case works as expected. – Marcin Jun 03 '13 at 22:40
  • @Marcin Those `i` variables are completely unrelated to the other `i` - that's why I avoided calling them as such. I used the term default argument instead to show the difference. I'll clarify. – Gareth Latty Jun 03 '13 at 22:45
  • "the name is evaluated when the function is defined" that is probably an important point that I had missed which is why I was so utterly confused by this http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#default-parameter-values (from ahuff44's answer). I still don't fully understand it. – Sarien Jun 04 '13 at 08:10
2

Python doesn't exactly pass by reference or by value (at least, not the way you'd think of it, coming from a language like C++). In many other languages (such as C++), variables can be thought of as synonymous with the values they hold. However, in Python, variables are names that point to the objects in memory. (This is a good explanation (with pictures!)) Because of this, you can get multiple names attached to one object, which can lead to interesting effects.


Consider these equivalent program snippets:

// C++:
int x;
x = 10;  // line A
x = 20;  // line B

and

# Python:
x = 10 # line C 
x = 20 # line D

After line A, the int 10 is stored in memory, say, at the memory address 0x1111.

After line B, the memory at 0x1111 is overwritten, so 0x1111 now holds the int 20


However, the way this program works in python is quite different:

After line C, x points to some memory, say, 0x2222, and the value stored at 0x2222 is 10

After line D, x points to some different memory, say, 0x3333, and the value stored at 0x3333 is 20

Eventually, the orphaned memory at 0x2222 is garbage collected by Python.


Hopefully this helps you get a grasp of the subtle differences between variables in Python and most other languages.

(I know I didn't directly answer your question about lambdas, but I think this is good background knowledge to have before reading one of the good explanations here, such as @Lattyware's)

See this question for some more background info.

Here's some final background info, in the form of oft-quoted but instructive examples:

print 'Example 1: Expected:'
x = 3
y = x
x = 2
print 'x =', x
print 'y =', y

print 'Example 2: Surprising:'
x = [3]
y = x
x[0] = 2
print 'x =', x
print 'y =', y

print 'Example 3: Same logic as in Example 1:'
x = [3]
y = x
x = [2]
print 'x =', x
print 'y =', y

The output is:

Example 1: Expected:

x = 2

y = 3

Example 2: Surprising:

x = [2]

y = [2]

Example 3: Same logic as in Example 1:

x = [2]

y = [3]

Community
  • 1
  • 1
ahuff44
  • 1,100
  • 1
  • 9
  • 9
  • 1
    I really like this example form the first link you provided:def bad_append(new_item, a_list=[]): a_list.append(new_item) return a_list – Sarien Jun 04 '13 at 07:35
1

The list of lambdas problem arises because the i referred to in both snippets is the same variable.

Two distinct variables with the same name exist only if they exist in two separate scopes. See the following link for when that happens, but basically any new function (including a lambda) or class establishes its own scope, as do modules, and pretty much nothing else does. See: http://docs.python.org/2/reference/executionmodel.html#naming-and-binding

HOWEVER, when reading the value of a variable, if it is not defined in the current local scope, the enclosing local scopes are searched*. Your first example is of exactly this behaviour:

foo = []
for i in range(3):
    foo.append((lambda: i))

for l in foo:
    print(l())

Each lambda creates no variables at all, so its own local scope is empty. When execution hits the locally undefined i, it is located in the enclosing scope.

In your second example, each lambda creates its own i variable in the parameter list:

foo = []
for i in range(3):
    foo.append((lambda i=i: i))

This is in fact equivalent to lambda a=i: a, because the i inside the body is the same as the i on the left hand side of the assignment, and not the i on the right hand side. The consequence is that i is not missing from the local scope, and so the value of the local i is used by each lambda.

Update: Both of your assumptions are incorrect.

Function arguments are passed by value. The value passed is the reference to the object. Pass-by-reference would allow the original variable to be altered.

No implicit copying ever occurs on function call or assignment, of any language-level object. Under the hood, because this is pass-by-value, the references to the parameter objects are copied when the function is called, as is usual in any language which passes references by value.

Update 2: The details of function evaluation are here: http://docs.python.org/2/reference/expressions.html#calls . See the link above for the details regarding name binding.

* No actual linear search occurs in CPython, because the correct variable to use can be determined at compile time.

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • Where is the other part of that *? – Sarien Jun 04 '13 at 07:37
  • "Function arguments are passed by value. The value passed is the reference to the object. Pass-by-reference would allow the original variable to be altered." I'm not sure how passing a reference by value is different from passing by reference. Is this an implementation issue? – Sarien Jun 04 '13 at 08:14
  • @Sarien It is in no way the same. Pass-by-reference shares the variables between the stack frames. This does not do that, each stack frame gets a separate copy of the value. – Marcin Jun 04 '13 at 12:28
  • What? Now things are getting copied again? :) – Sarien Jun 04 '13 at 12:30
  • @Sarien Yes, the references are copied, the objects are not. – Marcin Jun 04 '13 at 12:43
  • I still don't understand what the difference between pass-by-reference and "passing a reference by value" is. Isn't the latter just one way to achieve the former? – Sarien Jun 04 '13 at 13:47
  • @Sarien No, there is no similarity. Why don't you go read about what pass-by-reference is? – Marcin Jun 04 '13 at 16:54
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/31201/discussion-between-sarien-and-marcin) – Sarien Jun 04 '13 at 18:48
1
foo = []
for i in range(3):
    foo.append((lambda: i))  

Here since all the lambda's were created in the same scope so all of them point to the same global variable variable i. so, whatever value i points to will be returned when they are actually called.

foo = []
for i in range(3):
    foo.append((lambda z = i: id(z)))

print id(i)      #165618436
print(foo[-1]()) #165618436

Here in each loop we assign the value of i to a local variable z, as default arguments are calculated when the function is parsed so the value z simply points to the values stored by i during the iteration.

Arguments are always passed to functions by reference?

In fact the z in foo[-1] still points to the same object as i of the last iteration, so yes values are passed by reference but as integers are immutable so changing i won't affect z of the foo[-1] at all.

In the example below all lambda's point to some mutable object, so modifying items in lis will also affect the functions in foo:

foo = []
lis = ([], [], [])
for i in lis:
    foo.append((lambda z = i: z))

lis[0].append("bar")
print foo[0]()          #prints ['bar']
i.append("foo")         # `i` still points to lis[-1]
print foo[-1]()         #prints ['foo']

Assigning to a variable of immutable type creates a copy?

No values are never copied.

>>> x = 1000
>>> y = x       # x and y point to the same object, but an immutable object.

>>> x += 1      # so modifying x won't affect y at all, in fact after this step
                # x now points to some different object and y still points to 
                # the same object 1000

>>> x           #x now points to an new object, new id()
1001
>>> y           #still points to the same object, same id()
1000

>>> x = []
>>> y = x              
>>> x.append("foo") #modify an mutable object
>>> x,y             #changes can be seen in all references to the object
(['foo'], ['foo'])
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
0

The answer is that the references created in a closure (where a function is inside a function, and the inner function accesses variables from the outer one) are special. This is an implementation detail, but in CPython the value is a particular kind of object called a cell and it allows the variable's value to be changed without rebinding it to a new object. More info here.

The way variables work in Python is actually rather simple.

  1. All variables contain references to objects.
  2. Reassigning a variable points it to a different object.
  3. All arguments are passed by value when calling functions (though the values being passed are references).
  4. Some types of objects are mutable, which means they can be changed without changing what any of their variable names point to. Only these types can be changed when passed, since this does not require changing any references to the object.
  5. Values are never copied implicitly. Never.
kindall
  • 178,883
  • 35
  • 278
  • 309
  • 1
    -1 The `cell` object is an implementation detail. Although I agree with everything else. – Marcin Jun 03 '13 at 22:08
  • "3. All arguments are passed by value when calling functions." yet I can pass a list to a function and change it and it will change outside the function. I think the interaction with reference types that needs to be considered in this discussion. – Sarien Jun 03 '13 at 22:14
  • 1
    @Sarien, Python passes arguments by value. When you pass an object, a *reference* to the object is passed by *value*. Your code can use that reference to mutate the state of the object. This is discussed in item 4. – steveha Jun 03 '13 at 22:27
  • @Marcin: It's an implementation detail which happens to explain the behavior. I suppose I could have limited it to "those are handled specially" and not gone into specifics, but that seems... incomplete. – kindall Jun 03 '13 at 23:00
  • @kindall Other implementations are not required to use that method of implementing the behaviour, and there is no language-level access to the cell. The cell is not a python value, it's a C value. In that respect, this answer is misleading. – Marcin Jun 03 '13 at 23:02
-2

The behaviour really has very little to do with how parameters are passed (which is always the same way; there is no distinction in Python where things are sometimes passed by reference and sometimes passed by value). Rather the problem is to do with how names themselves are found.

lambda: i

creates a function that is of course equivalent to:

def anonymous():
    return i

That i is a name, within the scope of anonymous. But it's never bound within that scope (not even as a parameter). So for that to mean anything i must be a name from some outer scope. To find a suitable name i, Python will look at the scope in which anonymous was defined in the source code (and then similarly out from there), until it finds a definition for i.1

So this loop:

foo = []
for i in range(3):
    foo.append((lambda: i))

for l in foo:
    print(l())

Is almost exactly as if you had written this:

foo = []
for i in range(3):
    def anonymous():
        return i
    foo.append(anonymous)

for l in foo:
    print(l())

So that i in return i (or lambda: i) ends up being the same i from the outer scope, which is the loop variable. Not that they are all references to the same object, but that they are all the same name. So it's simply not possible for the functions stored in foo to return different values; they're all returning the object referred to by a single name.

To prove it, watch what happens when I remove the variable i after the loop:

>>> foo = []
>>> for i in range(3):
    foo.append((lambda: i)) 
>>> del i
>>> for l in foo:
    print(l())
Traceback (most recent call last):
  File "<pyshell#7>", line 2, in <module>
    print(l())
  File "<pyshell#3>", line 2, in <lambda>
    foo.append((lambda: i))
NameError: global name 'i' is not defined

You can see that the problem isn't that each function has a local i bound to the wrong thing, but rather than each function is returning the value of the same global variable, which I've now removed.

OTOH, when your loop looks like this:

foo = []
for i in range(3):
    foo.append((lambda i=i: i))

for l in foo:
    print(l())

That is quite like this:

foo = []
for i in range(3):
    def anonymous(i=i):
        return i
    foo.append(anonymous)

for l in foo:
    print(l())

Now the i in return i is not the same i as in the outer scope; it's a local variable of the function anonymous. A new function is created in each iteration of the loop (stored temporarily in the outer scope variable anonymous, and then permanently in a slot of foo), so each one has it's own local variables.

As each function is created, the default value of its parameter is set to the value of i (in the scope defining the functions). Like any other "read" of a variable, that pulls out whatever object is referenced by the variable at that time, and thereafter has no connection to the variable.2

So each function gets the default value of i as it is in the outer scope at the time it is created, and then when the function is called without an argument that default value becomes the value of the i in that function's local scope. Each function has no non-local references, so is completely unaffected by what happens outside it.


1 This is done at "compile time" (when the Python file is converted to bytecode), with no regard for what the system is like at runtime; it is almost literally looking for an outer def block with i = ... in the source code. So local variables are actually statically resolved! If that lookup chain falls all the way out to the module global scope, then Python assumes that i will be defined in the global scope at the point that the code will be run, and just treats i as a global variable whether or not there is a statically visible binding for i at module scope, hence why you can dynamically create global variables but not local ones.

2 Confusingly, this means that in lambda i=i: i, the three is refer to three completely different "variables" in two different scopes on the one line.

The leftmost i is the "name" holding the value that will be used for the default value of i, which exists independently of any particular call of the function; it's almost exactly "member data" stored in the function object.

The second i is an expression evaluated as the function is created, to get the default value. So the i=i bit acts very like an independent statement the_function.default_i = i, evaluated in the same scope containing the lambda expression.

And finally the third i is actually the local variable inside the function, which only exists within a call to the anonymous function.

Ben
  • 68,572
  • 20
  • 126
  • 174
  • @Marcin that's idiotic. To make that work you have to say it passes *references* "by value". It makes much more sense to say that you're passing *objects* by reference. In what way is "pass by reference" an inaccurate way to describe the semantics? (which is that when you pass an object into a function it receives a reference to the same object, not a copy of the object as would be the case if things were passed by value) – Ben Jun 04 '13 at 21:05
  • @Marcin To say that Python is "pass by value" is to say that *there's no such thing* as "pass by reference". **Every** reference passing system works that way! At some level, a reference has to be a value which is copied to have another reference somewhere else! But the "value" of a name in python is invisible (you can only use names as references); it's an implementation detail, completely irrelevant to terms like "pass by reference" which exist to explain the semantics. And you wonder why people are confused when you say Python is pass by value? – Ben Jun 04 '13 at 21:16
  • No it isn't. Pass by reference is completely different from how python works. Pass by reference involves the sharing of variables between function activations. Go read the wikipedia entry on the subject. – Marcin Jun 04 '13 at 21:24
  • @Marcin Hmm, seems I'm out of step with at least some standard usage, though I still think that's idiotic. I would say it's a question of what you can do with references; Python assignment acts **on** references, whereas in what you're thinking of as pass by reference assignment acts *through* references. And you could still make the same claim that there are references being passed by value (just references to something which supports assignment). Even if by reference is inaccurate, I still think "passes references by value" is incorrect, since references aren't values. I'll change my sentenc – Ben Jun 04 '13 at 21:59
  • What you think is "idiotic" is irrelevant. The term has an agreed on meaning in the community, which is still not what you think it is. References are not python values, but that does not change the fact that the calling discipline is that known for decades as pass-by-value. You seem to think that the computer scientific community should conform to your ignorance. I suggest you correct your ignorance of basic computer science. – Marcin Jun 04 '13 at 22:09
  • I also note that your post is riddled with other errors, too many to point out individually. – Marcin Jun 04 '13 at 22:10