views:

226

answers:

5

I know Python doesn't have pointers, but is there a way to have this yield 2 instead

>>> a = 1
>>> b = a # modify this line somehow so that b "points to" a
>>> a = 2
>>> b
1

?


Here's an example: I want form.data['field'] and form.field.value to always have the same value. It's not completely necessary, but I think it would be nice.

+4  A: 

There's no way you can do that changing only that line. You can do:

a = [1]
b = a
a[0] = 2
b[0]

That creates a list, assigns the reference to a, then b also, uses the a reference to set the first element to 2, then accesses using the b reference variable.

Matthew Flaschen
o_o frighteningly similar (not an accusation) - Deleted mine because it adds nothing
Jamie Wong
That's exactly the kind of inconsistency I hate about Python and these dynamic languages. (Yeah yeah, it's not really "inconsistent" because you're changing an attribute rather than the reference but I still don't like it)
Mark
@Mark: indeed. I know countless (well, a few) people who spent possibly hours searching for a "bug" in their code and then to find that it was caused by a list not getting hard-copied.
houbysoft
There's no inconsistency. And it has nothing to do with the great static v. dynamic debate. If they were two references to the same Java ArrayList, it would be the same, modulo syntax. If you use immutable objects (like tuples), you don't have to worry about the object being changed through another reference.
Matthew Flaschen
I've used this several times, most commonly to work around 2.x's lack of "nonlocal". It's not the prettiest thing to do, but it works fine in a pinch.
Glenn Maynard
+4  A: 

To understand why this works the way it does, check out this section on the Idiomatic Python page (and the section above it).

Brian Neal
I'm pretty sure I know *why*... I just don't like it. It forces you to move things into attributes if you want this behavior.
Mark
It's just the way the language works. I'm not sure why it matters. As others have asked, can you post a use-case where this is a problem and you think you need pointers?
Brian Neal
+6  A: 

It's not a bug, it's a feature :-)

When you look at the '=' operator in Python, don't think in terms of assignment. You don't assign things, you bind them. = is a binding operator.

So in your code, you are giving the value 1 a name: a. Then, you are giving the value in 'a' a name: b. Then you are binding the value 2 to the name 'a'. The value bound to b doesn't change in this operation.

Coming from C-like languages, this can be confusing, but once you become accustomed to it, you find that it helps you to read and reason about your code more clearly: the value which has the name 'b' will not change unless you explicitly change it. And if you do an 'import this', you'll find that the Zen of Python states that Explicit is better than implicit.

Note as well that functional languages such as Haskell also use this paradigm, with great value in terms of robustness.

d.w.
You know, I've read answers like this dozens of times, and I've never understood it. The behavior of `a = 1; b = a; a = 2;` is exactly the same in Python, C, and Java: b is 1. Why this focus on "= isn't assignment, it's binding"?
Ned Batchelder
You do assign things. That's why it's called an [assignment statement](http://docs.python.org/reference/simple_stmts.html). The distinction you're stating doesn't make sense. And this has nothing to do with compiled v. interpreted or static v. dynamic. Java is a compiled language with static type-checking, and it doesn't have pointers either.
Matthew Flaschen
What about C++? "b" might be a reference to "a". Understanding the difference between assignment and binding is critical to fully understanding why Mark can't do what he'd like to do, and how languages like Python are designed. Conceptually (not necessarily in implementation), "a = 1" doesn't overwrite the block of memory named "a" with 1; it assigns a name "a" to the already-existing object "1", which is fundamentally different than what happens in C. That's why pointers as a concept can't exist in Python--they'd become stale the next time the original variable was "assigned over".
Glenn Maynard
@d.w.: I like this way of thinking about it! "Binding" is a good word. @Ned: The output is the same, yes, but in C the value of "1" is copied to both `a` and `b` whereas in Python, they both *refer* to the *same* "1" (I think). Thus, if you could change the value of 1 (as with objects), it would be different. Which leads to some weird boxing/unboxing issues, I hear.
Mark
The difference between Python and C isn't what "assignment" means. It's what "variable" means.
dan04
@Matthew, read the link you referred me to. The first sentence refers to the (re)binding behavior of =.The distinction is in mutability.
d.w.
+2  A: 

From one point of view, everything is a pointer in Python. Your example works a lot like the C++ code.

int* a = new int(1);
int* b = a;
a = new int(2);
cout << *b << endl;   // prints 1

(A closer equivalent would use some type of shared_ptr<Object> instead of int*.)

Here's an example: I want form.data['field'] and form.field.value to always have the same value. It's not completely necessary, but I think it would be nice.

You can do this by overloading __getitem__ in form.data's class.

dan04
`form.data` isn't a class. Is it necessary to make it one, or can I override it on the fly? (It's just a python dict) Also, data would have to have a reference back to `form` to access the fields... which makes implementing this ugly.
Mark
+1  A: 

I want form.data['field'] and form.field.value to always have the same value

This is feasible, because it involves decorated names and indexing -- i.e., completely different constructs from the barenames a and b that you're asking about, and for with your request is utterly impossible. Why ask for something impossible and totally different from the (possible) thing you actually want?!

Maybe you don't realize how drastically different barenames and decorated names are. When you refer to a barename a, you're getting exactly the object a was last bound to in this scope (or an exception if it wasn't bound in this scope) -- this is such a deep and fundamental aspect of Python that it can't possibly be subverted. When you refer to a decorated name x.y, you're asking an object (the object x refers to) to please supply "the y attribute" -- and in response to that request, the object can perform totally arbitrary computations (and indexing is quite similar: it also allows arbitrary computations to be performed in response).

Now, your "actual desiderata" example is mysterious because in each case two levels of indexing or attribute-getting are involved, so the subtlety you crave could be introduced in many ways. What other attributes is form.field suppose to have, for example, besides value? Without that further .value computations, possibilities would include:

class Form(object):
   ...
   def __getattr__(self, name):
       return self.data[name]

and

class Form(object):
   ...
   @property
   def data(self):
       return self.__dict__

The presence of .value suggests picking the first form, plus a kind-of-useless wrapper:

class KouWrap(object):
   def __init__(self, value):
       self.value = value

class Form(object):
   ...
   def __getattr__(self, name):
       return KouWrap(self.data[name])

If assignments such form.field.value = 23 is also supposed to set the entry in form.data, then the wrapper must become more complex indeed, and not all that useless:

class MciWrap(object):
   def __init__(self, data, k):
       self._data = data
       self._k = k
   @property
   def value(self):
       return self._data[self._k]
   @value.setter
   def value(self, v)
       self._data[self._k] = v

class Form(object):
   ...
   def __getattr__(self, name):
       return MciWrap(self.data, name)

The latter example is roughly as close as it gets, in Python, to the sense of "a pointer" as you seem to want -- but it's crucial to understand that such subtleties can ever only work with indexing and/or decorated names, never with barenames as you originally asked!

Alex Martelli
I asked it the way I did because I was hoping to get a general solution that would work for everything right down to "barenames" and I didn't have a particular case in mind at the time -- just that I had ran into this problem with the previous iteration of this project and I didn't want to run into again. Anyway, this is a great answer! The incredulity, however, is less appreciated.
Mark
@Mark, look at the comments on your Q and you'll see that "incredulity" is a widespread reaction -- and the top-voted A is telling you "don't do it, get over it", followed by one that goes "it's just how it is". While you may not "appreciate" Python-knowledgeable people reacting with astonishment to your original specs, they _are_ quite astonishing in themselves;-).
Alex Martelli
@Alex: Yes, but you seem astonished at my lack of Python knowledge... as if we're a foreign species :P
Mark