7

I have a question about the map function in Python.

From what I understand, the function does not mutate the list it's operating on, but rather create a new one and return it. Is this correct ?

Additionally, I have the following piece of code

def reflect(p,dir):
  if(dir == 'X'):
    func = lambda (a,b) : (a * -1, b)
   else:
    func = lambda (a,b) : (a, b * -1)
  p = map(func,p)
  print 'got', p

points is an array of tuples such as: [(1, 1), (-1, 1), (-1, -1), (1, -1)]

If I call the above function in such a manner:

print points
reflect(points,'X')
print points

the list points does not change. Inside the function though, the print function properly prints what I want.

Could someone maybe point me in some direction where I could learn how all this passing by value / reference etc works in python, and how I could fix the above ? Or maybe I'm trying too hard to emulate Haskell in python...

Thanks

edit:

Say instead of p = map(func,p) I do

for i in range(len(p)):
  p[i] = func(p[i])

The value of the list is updated outside of the function, as if working by reference. Ugh, hope this is clear :S

rafalio
  • 3,928
  • 4
  • 30
  • 33
  • Could you explain a bit more explicit what your question/problem is? The reference to Haskel is not very helpful for non-Haskel-programmers. – Achim Aug 26 '11 at 10:28
  • Hey. Yeah, sorry for this, maybe just ignore the haskell comment. I'm just surprised that even though I do `p = map(func,p)`, the list p is not changed outside of the function. If I replace `p = map(func,p)` with a list iteration, it seems to work. – rafalio Aug 26 '11 at 10:35
  • Python is neither pass by value nor pass by reference, python is pass by object. – Lie Ryan Aug 26 '11 at 10:43

3 Answers3

14

You misunderstand how references work in Python. Here, all names are references, there are no "values". Names are bound to objects. But = doesn't modify the object that's pointed to by the name — it rebinds the name to a different object:

x = 42
y = x
# now:
# 'is' is a identity operator — it checks whether two names point to the
# exact same object
print x is y # => True
print x, y   # 42 42
y = 69
# now y has been rebound, but that does not change the '42' object, nor rebinds x
print x is y # => False
print x, y   # 42 69

To modify the object itself, it needs to be mutable — i.e. expose members that mutate it or have a modifiable dict. The same thing as above happens when you rebind p — it doesn't touch points at all, it simply modifies the meaning of local p name.

If you want to simulate C++-like references, you need to encapsulate the object into a mutable container, e.g. a list.

reflect([points], 'X')

# inside reflect:
p[0] = ...

But you shouldn't, at least in this case — you should just return the new object instead.

points = reflect(points, 'X')

# inside reflect, instead of p = ...
return map(func, p)

Well, now that I think about it, you can also do

p[:] = map(func, p)

But again, returning new object is usually better.

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
  • Ah ok, this makes sense. I was confused and thought the `=` operator worked differently than it does, thanks. – rafalio Aug 26 '11 at 10:38
0

I want to bump this. The answers don't really answer the question - they disregard the fact that the OP was able to achieve the desired result by iterating. The question comes down to the behavior of map. Here is a more direct example:

f=(lambda pair: pair[0].append(pair[1]))
a = ([],1)
f(a)
print(a) #prints ([1],1)
a=([],1)
map(f,[a])
print(a) #prints ([0],1)

So map isn't mutating objects in the way the OP is expecting. I have the same confusion.

Can anyone comment on exactly what is going on here? I think that'd be a good answer to the OP's question.

Note that we have different behavior if we assign the output of map as follows (as per Cat Plus Plus' answer)

f=(lambda pair: pair[0].append(pair[1]))
a = ([],1)
x = [a]
x[:] = map(f,x)
print(x) #prints None
print(a) # prints [1]

Please note that in the first example, we simply called f(a), not a=f(a). Why do we need assignment when using map and not when working outside of map?

Zach Siegel
  • 330
  • 2
  • 9
0

The data model of Python is based on a trilogy:
identifier - reference - object

.

  • The identifier is a string written in the code.
  • The reference is a variable stricto sensu, that is to say "a chunk of memory whose content can change". The value of a reference is the adress of the object.
  • The object has an implementation based on the structures of the langage C that is the foundations of Python.

Other words are also used to designate the 'identifier' :
1) name
2) variable ; because this word is used by metonymy in mathematics to designate the symbols that represent the real mathematical variables, and the variables in a computer have conceptually the same functioning as the mathematical variables (their values can change).
This use in Python is a very bad habit in my opinion : it creates ambiguities and confusion with what is called 'variable' in computer science: "chunk of memory whose content can change".

The best is to use the word : identifier

.

An identifier and an object are binded in a certain namespace. Namespaces are displayed under the form of Python's dictionaries, but they ARE NOT dictionaries.

  • The binding of the identifier and the object is indirect, via the reference.

  • The binding of the identifier and the reference is direct, and realized in the SYMBOL TABLE (or symbol-table).

In computer science, a symbol table is a data structure used by a language translator such as a compiler or interpreter, where each identifier in a program's source code is associated with information relating to its declaration or appearance in the source, such as its type, scope level and sometimes its location.
http://en.wikipedia.org/wiki/Symbol_table

They say: identifiers. Precisely.
I rarely see allusions to symbol table, though it's the crucial thing that sheds light on the functioning of the Python's data model IMO.

In my opinion, the word

binding

doesn't designate a precise and unique mechanism but globally the set of all the mechanisms concerning the trilogy identifier - reference - object

.

I dont' pretend that I perfectly understood all the matters concerning the data and execution models of Python, and that the above considerations are the more exact and precisely expressed that can be done.
However, they allow me to have an operational understanding of what happens during executions of code.
I would be very happy to be corrected on some points if I am wrong (for exemple, I am very satisfied to have learnt from Michael Foord that the nature of namespaces is not being dictionary, which is only the way they are represented)

.

That said, I don't know what is called value and reference when the subject of passing something as argument in Python is discussed, and I have the impression that a lot of the persons that have expressed on the subject in numerous esoteric discussions don't know more than me. I think that there is no best and lucid opinion on the subject that this one of Alex Martelli:

"Trying to reuse terminology that is more generally applied to languages where "variables are boxes" to a language where "variables are post-it tags" is, IMHO, more likely to confuse than to help."

Alex Martelli

http://bytes.com/topic/python/answers/37219-value-reference

eyquem
  • 26,771
  • 7
  • 38
  • 46
  • In response to your moderator flag, only questions can have tags. Answers automatically have the same tag (not displayed) as the question they answer. – Bill the Lizard Aug 28 '11 at 13:02
  • @Bill the Lizard Thank you . I realized that after some time. Your answer is interesting though, because you say that answers are also tagged the same as the question, though the tag is not displayed. It was something I was wondering about: what happens when we do a search with tags ? Now I know that tags are taken in account since the answers are tagged. – eyquem Aug 28 '11 at 13:23