0

I had this Python 2.7 code which queries the movement limits from a device:

pan_low_limit = int(self.send_query("PN", 2))
pan_high_limit = int(self.send_query("PX", 2))
tilt_low_limit = int(self.send_query("TN", 2))
tilt_high_limit = int(self.send_query("TX", 2))

I thought I could write this nicer as:

limits = [(pan_low_limit, "PN"), (pan_high_limit, "PX"), (tilt_low_limit, "TN"), (tilt_high_limit, "TX")]
for v, c in limits:
    v = int(self.send_query(c, 2))
    print("Result from {}: {}".format(c, v))
print(limits)
print("tilt_low_limit: {}".format(tilt_low_limit))

Print statements are to find what's going on. When I run this, I get the following output (these four variables had been initialized with value 1 before this piece of code):

Result from PN: -27067
Result from PX: 27067
Result from TN: -27999
Result from TX: 9333
[(1, 'PN'), (1, 'PX'), (1, 'TN'), (1,'TX')]
tilt_low_limit: 1
PN: 1

I don't really get what's going on. It seems like the value of v in "v = int(self.send_query(c, 2))" is what I'd expect, but at the next line, these variables have the old values again?

3
  • 3
    Python's variables are not symbolic names for memory addresses (like in C or Pascal) but just names bound to an object. Rebinding a name only make this name pointing to another object, it doesn't affect the object that was previously bound to this name. You want to read this for more in-depth explanations: nedbatchelder.com/text/names.html Commented Sep 28, 2018 at 14:48
  • I don't understand exactly what you mean, so I'll read up on the link you've provided. Commented Sep 28, 2018 at 15:07
  • @brunodesthuilliers that link was very helpful and clears up a lot. Recommended reading. I also found that pythontutor.com is helpful to visualize what variables/objects are doing in small snippets of code. Commented Oct 2, 2018 at 7:46

2 Answers 2

1

This is what you want:

for i, (v, c) in enumerate(limits):
    limits[i] = int(self.send_query(c, 2))

v = does not change the value in limits, it merely changes the value of the reference/variable v.

Sign up to request clarification or add additional context in comments.

2 Comments

That doesn't seem to work. After the 'for' loop, limits = [-27067, 27067, -27999, 9333]. However the values of the variables pan_low_limit/... are all still 1.
@DieterVansteenwegenON4DD Yes, there is no way to modify the variables in the way you'd like (unless they're global, in which case you can modify the globals() dict, but don't do that). So I'm suggesting a reasonable alternative.
0

In Python a symbolic name (like pan_low_limit in your code) is a reference to a value (like "-27067").

When one name is assigned to the other (as happens for limits and v in your code), the new name becomes an additional reference to that value. In your case, in the first iteration right before the assignment, the names: pan_low_limit, limits[0][0] and v all refer to the same value:

before

However, when you assign (or bind) a name to a new value, only that name will refer to the new value. In your case, after the assignment, you will have:

after

To obtain the result that you want, you should change the variable, instead of reassigning it, so that all the references will refer to the same new value. However, integers are immutable in Python, so you cannot change it. The solution is to use something that is mutable, a list for example.

If you had a mutable variable, the original value could be changed to have the result that you are expecting. You can see this difference in the following two snippets:

Using integers:

pan_low_limit = 1
pan_high_limit = 1
tilt_low_limit = 1
tilt_high_limit = 1

limits = [(pan_low_limit, "PN"), (pan_high_limit, "PX"), (tilt_low_limit, "TN"), (tilt_high_limit, "TX")]
for v, c in limits:
    v = 2
    print("Result from {}: {}".format(c, v))
print(limits)
print("tilt_low_limit: {}".format(tilt_low_limit))

Result:

Result from PN: 2
Result from PX: 2
Result from TN: 2
Result from TX: 2
[(1, 'PN'), (1, 'PX'), (1, 'TN'), (1, 'TX')]
tilt_low_limit: 1

Using lists, we can modify the value, instead of reassigning it:

pan_low_limit = [1]
pan_high_limit = [1]
tilt_low_limit = [1]
tilt_high_limit = [1]

limits = [(pan_low_limit, "PN"), (pan_high_limit, "PX"), (tilt_low_limit, "TN"), (tilt_high_limit, "TX")]
for v, c in limits:
    v[0] = 2
    print("Result from {}: {}".format(c, v))
print(limits)
print("tilt_low_limit: {}".format(tilt_low_limit))

Result:

Result from PN: [2]
Result from PX: [2]
Result from TN: [2]
Result from TX: [2]
[([2], 'PN'), ([2], 'PX'), ([2], 'TN'), ([2], 'TX')]
tilt_low_limit: [2]

Reference: link

Thanks @bruno desthuilliers for pointing out flaws in a previous answer.

7 Comments

Sorry but stating that it's "similar to an assignment in C" is just plain wrong. If you were to rewrite the C example with a struct instead of an int, you'd find out that changing one of the field of a would not impact b[0] - since it would indeed copy the "value" - while in Python (using any mutable object instead), mutating the object via the iteration variable (or any other name pointing to this object) DOES change the one stored in the list. To make a long story short: Python's "variables" have nothing in common with C variables and you cannot explain one it terms of the other.
Thanks for pointing this out. I was indeed over-simplifying. I have now updated the answer.
that's better but still wrong ;-) The fact that ints are immuable is actually totally irrelevant, the problem is that rebinding a name only makes the name refer to another object. The behavior would be the same with mutable objects. The difference is not between mutable and immutable objects but between mutating and rebinding.
I don't fully understand what you mean. Probably a more detailed explanation is needed. It does make a difference if the variable is mutable or immutable, as you can see in my example. Look also at this answer for reference stackoverflow.com/questions/28228461/… I think you should provide a new answer with details on your theory, references and some code that proves it. I will be happy to upvote it :)
Yes, I agree that in one case I mutate it and in the other case I do not, because it is immutable :). I also agree that, if your answer is "that's how python works, period.", it is better if you do not provide a new answer. Your own reference clearly say why mutable and immutable are relevant in this scenario. The SO question that I linked explains this also.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.