Dieter Vansteenwegen
Dieter Vansteenwegen

Reputation: 268

Store variables in a list in Python

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?

Upvotes: 0

Views: 3200

Answers (2)

Amedeo
Amedeo

Reputation: 847

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.

Upvotes: 0

Quelklef
Quelklef

Reputation: 2147

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.

Upvotes: 1

Related Questions