Why index reference does not working correct for variable placing in a list

Viewed 29

I had been writing this code, so I expected that function from class, has been applied to the first and second variable from the list.

Imported files contains: ch0.txt = 111, ch1.txt = 123

Yet, only the ch1.txt is loaded, and it is set for all of variable from the list (sic!).

Could you explain me why indexing like this does not work, or how I can change code to load content of each file to another variable; in way: s[i] = content of file ch[i].txt

class Test:
    name = "empty"

    def load(self, file_name):
        wczytanie = open(file_name, "r")
        dane = wczytanie.read().split("\n")

        self.name = dane[0]

pos = Test()

s = [pos, pos, pos]
print(s)

s[0].load("ch" + str(0) + ".txt")
s[1].load("ch" + str(1) + ".txt")

print(s)
print(s[0].name)
print(s[1].name)

The output:

[<__main__.Test object at 0x00000230771A40D0>, <__main__.Test object at 0x00000230771A40D0>, <__main__.Test object at 0x00000230771A40D0>]
[<__main__.Test object at 0x00000230771A40D0>, <__main__.Test object at 0x00000230771A40D0>, <__main__.Test object at 0x00000230771A40D0>]
123
123

Process finished with exit code 0
2 Answers

It's because all 3 indexes in your list s correspond to the same instance of Test, which is pos. When you set s[0], you are actually setting pos, meaning that it changes s[1] and s[2] because they are also both pos. To fix this, try making a new instance of Test for each index.

Additionally, your name variable is a class variable (in some languages called a static variable), meaning that when you run self.name = dane[0], python actually creates a new instance variable. To fix this, initialize self.name in a __init__ function (if you want it to stay as empty before loading) and remove the unneeded name class variable.

NOTE: You also should close the file wczytanie in your load function - otherwise, other applications will not be able to use it until your application ends.

class Test:
    def __init__(self):
        self.name = "empty"

    def load(self, file_name):
        wczytanie = open(file_name, "r")
        dane = wczytanie.read().split("\n")

        self.name = dane[0]
        wczytanie.close()

s = [Test(), Test(), Test()]
print(s)

s[0].load("ch" + str(0) + ".txt")
s[1].load("ch" + str(1) + ".txt")

print(s)
print(s[0].name)
print(s[1].name)

Output:

[<__main__.Test object at 0x000001B5716FBFD0>, <__main__.Test object at 0x000001B5716FBFA0>, <__main__.Test object at 0x000001B5716FBF10>]
[<__main__.Test object at 0x000001B5716FBFD0>, <__main__.Test object at 0x000001B5716FBFA0>, <__main__.Test object at 0x000001B5716FBF10>]
111
123

As one final side note, please don't name things like dane, s, or wczytanie, which have no real meaning without context. Either add context or name these more helpfully so you in the future will understand what they do, and other programmers will too.

The indexing is working, however the name you reference is the same for every item in that list.

This is because you are defining the list with:

pos = Test()
s = [pos, pos, pos]

instead of

s = [Test(), Test(), Test()]

which means that it is actually the same object being referenced three times in that list, rather than three separate items.

Make sure you call Test() individually to get separate instances of that class.


On a side note, because you've defined the name field in the class definition, it is a Class Variable, rather than an Instance Variable. This means that the name is shared between all instances of the Test class, even if you had created multiple instances.

Read here for more info.

Update: just tested and in this case it doesn't make an actual difference due to the way it's being assigned, but worth being aware of this pitfall)


Another side note - for your file handling, always try to use a with block as this will automatically close the file for you after it has been read out of, allowing other programs to still access the file whilst your program is running:

with open(file_name, "r") as wczytanie:
    dane = wczytanie.read().split("\n")
Related