Search code examples
pythonfunctionclassdictionaryobject-oriented-database

Variable from __init__ changed in one function, but not recognized as changed to other function


Sup, i'm doing a little account registration just for learning propose. I created a class called Accounts and did many different functions to work with. With previous acknowledgment i knew that i need to start them from a function called def __init__(self)

class Account:
    def __init__(self):
        self.contas = {}
        self.bancodecontas = open("x.txt", 'r')

    def getaccounts(self):
        for linha in self.bancodecontas:
            linha = linha.strip()
            conta = linha.split(",")
            login = conta[0]
            senha = conta[1]
            self.contas[login] = senha

    def accountsprinting(self):
        for login, senha in self.contas.items():
            print("Login= ", login, "Senha= ", senha)

getaccounts() is working fine, i tested a print(self.contas) in the end of it and it printed all accounts from inside my x.txt. The problem start when i need to call accountsprinting(), i tried to start it with print(self.contas) but shows me a empty dictionary, which means it is not accessing the "new" self.contas. I did the exact samething in a different type of project and it worked fine, i know i'm missing something really obvious here, so i'm asking sorry beforehand for my lack of attention. Thanks for your time, good codding.

EDIT 1

People asked for the entire program, this is my entire program. I'm using PyCharm, i created this as a accounts.py, a root file or resources file, and i'm going to be importing this class to another main.py to use the respective functions. I know i must call Accounts().getaccounts() first, then i must call the other functions, so i can first fill my "accounts database". Even doing this:

Adding print(self.contas) to the end of getaccounts() and the start of accountsprinting()

And doing on the same .py:

Account().getaccounts()
Account().accountsprinting()

Or doing on different .py:

from AccountManager import Account
Account().getaccounts()
Account().accountsprinting()

The output is the same: {'Bruno': '666', 'Bruno2': '444', 'Pedro': '2222a', 'Breno': '092b'} {}

EDIT 2

Adding self.getaccounts()to def __init__(self) as @Darkonaut said, really worked, on the same .py and even doing an import from another .py, but i would like to understand why without it, it doesn't work, makes no sense to me. Thanks a lot =)


Solution

  • You forgot self.getaccounts() in the last line of your __init__ method, hence contas remains empty because it never get's filled.


    TL;DR Make sure you keep a reference to a newly created instance to keep it alive and perform following method calls on this instance.

    __init__ is a method which is being called after an instance get's created and initializes the instance. You create an instance of a class every time you call a class like you did with Account().

    But if you don't keep a (external) reference to this newly created instance, you can't address the instance to invoke further methods on it. What happens in your code:

    Account().getaccounts() # new instance of Account created,
    # i.a. __init__ called, finally getaccounts called
    
    Account().accountsprinting() # new instance of Account created,
    # i.a. __init__ called, finally accountsprinting called
    

    Both instances are quickly garbage collected (CPython implementation of Python assumed), because you don't hold an external reference to them, like you would do if you assign a new instance to a name like: acc = Account().

    You can check that you get a new object every time you call Account() by comparing identity with Account(1) is Account(1) # False or by looking at the id numbers:

    id(Account(1))
    # 88311904
    id(Account(1))
    # 88312408
    

    As a side note:

    It doesn't have to be a named reference like acc above, you could also hold an implicit, unnamed reference by placing the new instances in a list for example and the list would keep the reference to the instances and thus, keeping them alive:

    class Account:
        def __init__(self, x):
            self.x = x
    
    lst = [Account(1), Account(2)]
    lst[0].x
    # 1
    lst[1].x
    # 2
    

    self in your code is an (internal) reference connecting (binding) class and instance. If you assign to self like you do with self.contas[login] = senha within your instance-method getaccounts, you do this only for the actual instance where you are calling getaccounts upon. So when you call Account().getaccounts() and later Account().accountsprinting() you are doing this for two different instances and not on the same. Hence the second instance has an empty contas dict because for this instance you didn't call getaccounts() before.