Reference variables in Python

I understand that all variables in Python are references that refer to a single object, and not to a section of memory as in C / C++.

And is it possible to create a reference to a single piece of memory, so that the value changes when working with any of the links?

For example (conditionally),

a = 10
b = &a
print(a, b)  # 10 10
b = 20
print(a, b)  # 20 20
a = 15
print(a, b)  # 15 15

I need this to shorten the entry to access the internal values of nested dictionaries:

username   = "test"
userdata   = [10, 2, 30, 15]
user       = {username:userdata}

serveraddr = "127.0.0.1"
server     = {serveraddr:user}

# хочется создать имена-сокращения для этих элементов    
joincount  = server[serveraddr][username][0]
leftcount  = server[serveraddr][username][1]
gamescount = server[serveraddr][username][2]
wincount   = server[serveraddr][username][3]

# пробуем изменить их значения
joincount  = 20
leftcount  = 9
gamescount = 10
wincount   = 3

# проверяем, что значения в словаре остались прежними
print(server[serveraddr][username][0], server[serveraddr][username][1],
      server[serveraddr][username][2], server[serveraddr][username][3])
# (10, 2, 30, 15)

# хотя значения переменных изменились
print joincount, leftcount, gamescount, wincount
# (20, 9, 10, 3)

The code often uses constructs like server[serveraddr][username][n], most of them fall on the places where all the indexes are calculated. Therefore, I would like to use joincount instead of server[serveraddr][username][0] - this would improve the readability of the code.

Is it possible to implement this?

Author: Timofei Bondarev, 2015-04-30

3 answers

Python has mutable and immutable types. Modifiable ones differ in that their contents can be changed without changing the link to them. Immutable objects have to be recreated to reflect state changes. At the same time, all the old links do not see this update, because they point to the old object.

I will explain in practice. Lists, dictionaries, and sets are mutable objects:

l1 = [1, 2, 3]
l2 = l1
print(l1, l2, id(l1), id(l2))  
# [1, 2, 3] [1, 2, 3] 139917408901064 139917408901064

l1[1] = 10
print(l1, l2, id(l1), id(l2))  
# [1, 10, 3] [1, 10, 3] 139917408901064 139917408901064

Numbers, strings, and tuples are immutable objects:

v1 = 1024
v2 = v1
print(v1, v2, id(v1), id(v2))
# 1024 1024 ...7040 ...7040

v1 = 2048
print(v1, v2, id(v1), id(v2))
# 2048 1024 ...5312 ...7040

t1 = (1, 2, 3)
t2 = t1
print(t1, t2, id(t1), id(t2))
# (1, 2, 3) (1, 2, 3) ...6232 ...6232

# t1[1] = 10  # не сработает, так как кортежи неизменяемые
t1 = (1, 10, 3)
print(t1, t2, id(t1), id(t2))
# (1, 10, 3) (1, 2, 3) ...7240 ...6232

In your own in your code, you use mutable data structures, such as dictionaries and lists (user, userdata, server) and immutable strings and numbers.

Thus, without changing anything in the structure of information storage, it is possible to reduce only access to the list of numbers as much as possible

my_userdata = server[serveraddr][username]

# изменение my_userdata влечёт изменение server[serveraddr][username],
#    так как это один и тот же объект

Since numbers are immutable, storing them in a variable and further changing this variable does not affect the original number in any way (i.e., the variable joincount will never change when changing server[serveraddr][username][0], and on the contrary). changing the numbers in this list will not affect the variables that store these values


But if you try to improve the code a little, you should create a class UserData, which will store the necessary values and allow you to change them. The minimum example would be as follows:

class UserData:
    def __init__(self, joincount, leftcount, gamescount, wincount):
        self.joincount = joincount
        self.leftcount = leftcount
        self.gamescount = gamescount
        self.wincount = wincount

username   = "test"
userdata   = UserData(10, 2, 30, 15)
user       = {username:userdata}

After that, if we get our user from somewhere, we can change the desired values:

user_data = server[serveraddr][username]

user_data.joincount  = 20
user_data.leftcount  = 9
user_data.gamescount = 10
user_data.wincount   = 3

print (server[serveraddr][username].joincount, 
       server[serveraddr][username].leftcount,
       server[serveraddr][username].gamescount, 
       server[serveraddr][username].wincount)

print (user_data.joincount, user_data.leftcount, 
       user_data.gamescount, user_data.wincount)

Using such a structure will increase the readability of your code, compared to using list indexes.


If you have access to change the contents of the dictionary, I would recommend that instead of using a self-written class, you use namedtuple from the standard module collections, which allows you to create an object that behaves similar to a tuple, but provides access to fields like the class above (but only for reading):

UserData = collections.namedtuple('UserData', 
    ['joincount', 'leftcount', 'gamescount', 'wincount'])

In this case, will have to replace the old object UserData with the new one:

old_userdata = server[serveraddr][username]
new_userdata = UserData(joincount=20, leftcount=9, gamescount=10, wincount=3)

server[serveraddr][username] = new_userdata
print(new_userdata, old_userdata, server[serveraddr][username])

Getting a field to read it will be executed in the same way as in the example above:

print(old_userdata.joincount, old_userdata.gamescount)
 20
Author: Timofei Bondarev, 2015-04-30 22:28:32

You need to read and understand mutable and immutable objects in python. Yes, you can make a link to a nested list, and fill it in through it. For example:

lst = [[]]
sublst = lst[0]
sublst.append(1)
print(lst)
sublst.append(2)
print(lst)
print(id(lst[0]))
print(id(sublst))
sublst[0] = -1
print(lst)

Conclusion:

[[1]]
[[1, 2]]
139794716732424
139794716732424
[[-1, 2]]

In this case, sublst is a reference to a nested list in lst, calling sublst.append is equivalent to lst[0].append. Similarly, you can work with a dictionary, for example.

 7
Author: Avernial, 2015-04-30 07:58:48

Faced with a similar question: I would like to have a list of references to variables.

I'm making a network scanner. In my case, there are now two files - the logic and the GUI that uses it. In the logic, the counters/flags/data of the last calculations are constantly changing... - all this is in the form of class attributes in different threads.

In the GUI, I now want to display all this data with periodic updates in a separate frame. The most "stupid" option is to write everything in the GUI by direct names. I'm already used to transmitting references to methods and don't understand this double standard: "references to methods are allowed, but not to attributes!"

BUT while I was writing , I realized a solution that I did not see due to my inexperience:

  1. creating a function in the logic class that outputs a dictionary
  2. I fill in the dictionary with up-to-date data
  3. from the GUI, I call this method and go through the values, displaying everything in the frame

So I don't think there is anything inside, just configure the formation in the logic

 0
Author: centroid, 2021-01-23 16:02:00